2018-09-28 01:15:19 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
|
2021-01-27 09:28:38 +08:00
|
|
|
"src.elv.sh/pkg/eval/vals"
|
|
|
|
"src.elv.sh/pkg/parse"
|
|
|
|
"src.elv.sh/pkg/strutil"
|
2018-09-28 01:15:19 +08:00
|
|
|
)
|
|
|
|
|
2021-07-15 10:31:01 +08:00
|
|
|
type BadOption struct {
|
|
|
|
OptName string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e BadOption) Error() string {
|
|
|
|
return "unknown option: " + parse.Quote(e.OptName)
|
|
|
|
}
|
|
|
|
|
2019-04-19 05:57:14 +08:00
|
|
|
// RawOptions is the type of an argument a Go-native function can take to
|
2021-01-05 13:22:23 +08:00
|
|
|
// declare that it wants to parse options itself. See the doc of NewGoFn for
|
2019-04-19 05:57:14 +08:00
|
|
|
// details.
|
2018-09-28 01:15:19 +08:00
|
|
|
type RawOptions map[string]interface{}
|
|
|
|
|
|
|
|
// Takes a raw option map and a pointer to a struct, and populate the struct
|
|
|
|
// with options. A field named FieldName corresponds to the option named
|
|
|
|
// field-name, unless the field has a explicit "name" tag. Fields typed
|
|
|
|
// ParsedOptions are ignored.
|
|
|
|
func scanOptions(rawOpts RawOptions, ptr interface{}) error {
|
|
|
|
ptrValue := reflect.ValueOf(ptr)
|
|
|
|
if ptrValue.Kind() != reflect.Ptr || ptrValue.Elem().Kind() != reflect.Struct {
|
|
|
|
return fmt.Errorf(
|
|
|
|
"internal bug: need struct ptr to scan options, got %T", ptr)
|
|
|
|
}
|
|
|
|
|
2020-08-15 11:08:45 +08:00
|
|
|
// fieldIdxForOpt maps option name to the index of field in `struc`.
|
2018-09-28 01:15:19 +08:00
|
|
|
fieldIdxForOpt := make(map[string]int)
|
2020-08-15 11:08:45 +08:00
|
|
|
struc := ptrValue.Elem()
|
2018-09-28 01:15:19 +08:00
|
|
|
for i := 0; i < struc.Type().NumField(); i++ {
|
|
|
|
if !struc.Field(i).CanSet() {
|
2020-08-15 11:08:45 +08:00
|
|
|
continue // ignore unexported fields
|
2018-09-28 01:15:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
f := struc.Type().Field(i)
|
|
|
|
optName := f.Tag.Get("name")
|
|
|
|
if optName == "" {
|
2020-09-03 12:10:59 +08:00
|
|
|
optName = strutil.CamelToDashed(f.Name)
|
2018-09-28 01:15:19 +08:00
|
|
|
}
|
|
|
|
fieldIdxForOpt[optName] = i
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range rawOpts {
|
|
|
|
fieldIdx, ok := fieldIdxForOpt[k]
|
|
|
|
if !ok {
|
2021-07-15 10:31:01 +08:00
|
|
|
return BadOption{k}
|
2018-09-28 01:15:19 +08:00
|
|
|
}
|
|
|
|
err := vals.ScanToGo(v, struc.Field(fieldIdx).Addr().Interface())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|