2018-09-28 01:15:19 +08:00
|
|
|
package eval
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
|
2021-01-27 09:28:38 +08:00
|
|
|
"src.elv.sh/pkg/eval/vals"
|
|
|
|
"src.elv.sh/pkg/parse"
|
2018-09-28 01:15:19 +08:00
|
|
|
)
|
|
|
|
|
2021-12-13 09:23:14 +08:00
|
|
|
// UnknownOption is thrown by a native function when called with an unknown option.
|
|
|
|
type UnknownOption struct {
|
2021-07-15 10:31:01 +08:00
|
|
|
OptName string
|
|
|
|
}
|
|
|
|
|
2021-12-13 09:23:14 +08:00
|
|
|
// Error implements the error interface.
|
|
|
|
func (e UnknownOption) Error() string {
|
2021-07-15 10:31:01 +08:00
|
|
|
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.
|
2022-03-20 23:50:25 +08:00
|
|
|
type RawOptions map[string]any
|
2018-09-28 01:15:19 +08:00
|
|
|
|
|
|
|
// 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
|
2021-12-31 23:54:51 +08:00
|
|
|
// field-name. Options that don't have corresponding fields in the struct causes
|
|
|
|
// an error.
|
2022-01-01 00:50:44 +08:00
|
|
|
//
|
|
|
|
// Similar to vals.ScanMapToGo, but requires rawOpts to contain a subset of keys
|
|
|
|
// supported by the struct.
|
2022-03-20 23:50:25 +08:00
|
|
|
func scanOptions(rawOpts RawOptions, ptr any) error {
|
2022-01-01 00:50:44 +08:00
|
|
|
_, keyIdx := vals.StructFieldsInfo(reflect.TypeOf(ptr).Elem())
|
|
|
|
structValue := reflect.ValueOf(ptr).Elem()
|
2018-09-28 01:15:19 +08:00
|
|
|
for k, v := range rawOpts {
|
2022-01-01 00:50:44 +08:00
|
|
|
fieldIdx, ok := keyIdx[k]
|
2018-09-28 01:15:19 +08:00
|
|
|
if !ok {
|
2021-12-13 09:23:14 +08:00
|
|
|
return UnknownOption{k}
|
2018-09-28 01:15:19 +08:00
|
|
|
}
|
2022-07-27 06:16:54 +08:00
|
|
|
|
|
|
|
// An option with no value (e.g., `&a-opt`) has `$true` as its default value. However, if
|
|
|
|
// the option struct member is a string we want an empty string as the default value.
|
|
|
|
switch b := v.(type) {
|
|
|
|
case bool:
|
|
|
|
if b && structValue.Field(fieldIdx).Type().Name() == "string" {
|
|
|
|
v = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-01 00:50:44 +08:00
|
|
|
err := vals.ScanToGo(v, structValue.Field(fieldIdx).Addr().Interface())
|
2018-09-28 01:15:19 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|