elvish/pkg/eval/options.go
Kurtis Rader 87656c99fa Replace AnyError in tests with a specific error
The `AnyError` placeholder error can cause tests to succeed for errors
other than what was expected. That is, the use of `AnyError` can mask
bugs in a unit test. So replace it with the specific error, or error type,
the test expects to be raised.

This does not remove the anyError structure because it is used in
the TestCase.DoesNotCompile() method. To keep the size of this change
as small as possible I want to defer updating that use to a separate
change. However, remove the public AnyError var so future test writers
don't attempt to use it.
2021-12-13 01:08:24 +00:00

64 lines
1.6 KiB
Go

package eval
import (
"fmt"
"reflect"
"src.elv.sh/pkg/eval/vals"
"src.elv.sh/pkg/parse"
"src.elv.sh/pkg/strutil"
)
type BadOption struct {
OptName string
}
func (e BadOption) Error() string {
return "unknown option: " + parse.Quote(e.OptName)
}
// RawOptions is the type of an argument a Go-native function can take to
// declare that it wants to parse options itself. See the doc of NewGoFn for
// details.
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)
}
// fieldIdxForOpt maps option name to the index of field in `struc`.
fieldIdxForOpt := make(map[string]int)
struc := ptrValue.Elem()
for i := 0; i < struc.Type().NumField(); i++ {
if !struc.Field(i).CanSet() {
continue // ignore unexported fields
}
f := struc.Type().Field(i)
optName := f.Tag.Get("name")
if optName == "" {
optName = strutil.CamelToDashed(f.Name)
}
fieldIdxForOpt[optName] = i
}
for k, v := range rawOpts {
fieldIdx, ok := fieldIdxForOpt[k]
if !ok {
return BadOption{k}
}
err := vals.ScanToGo(v, struc.Field(fieldIdx).Addr().Interface())
if err != nil {
return err
}
}
return nil
}