github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/options.go (about) 1 package eval 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "src.elv.sh/pkg/eval/vals" 8 "src.elv.sh/pkg/parse" 9 "src.elv.sh/pkg/strutil" 10 ) 11 12 // RawOptions is the type of an argument a Go-native function can take to 13 // declare that it wants to parse options itself. See the doc of NewGoFn for 14 // details. 15 type RawOptions map[string]interface{} 16 17 // Takes a raw option map and a pointer to a struct, and populate the struct 18 // with options. A field named FieldName corresponds to the option named 19 // field-name, unless the field has a explicit "name" tag. Fields typed 20 // ParsedOptions are ignored. 21 func scanOptions(rawOpts RawOptions, ptr interface{}) error { 22 ptrValue := reflect.ValueOf(ptr) 23 if ptrValue.Kind() != reflect.Ptr || ptrValue.Elem().Kind() != reflect.Struct { 24 return fmt.Errorf( 25 "internal bug: need struct ptr to scan options, got %T", ptr) 26 } 27 28 // fieldIdxForOpt maps option name to the index of field in `struc`. 29 fieldIdxForOpt := make(map[string]int) 30 struc := ptrValue.Elem() 31 for i := 0; i < struc.Type().NumField(); i++ { 32 if !struc.Field(i).CanSet() { 33 continue // ignore unexported fields 34 } 35 36 f := struc.Type().Field(i) 37 optName := f.Tag.Get("name") 38 if optName == "" { 39 optName = strutil.CamelToDashed(f.Name) 40 } 41 fieldIdxForOpt[optName] = i 42 } 43 44 for k, v := range rawOpts { 45 fieldIdx, ok := fieldIdxForOpt[k] 46 if !ok { 47 return fmt.Errorf("unknown option %s", parse.Quote(k)) 48 } 49 err := vals.ScanToGo(v, struc.Field(fieldIdx).Addr().Interface()) 50 if err != nil { 51 return err 52 } 53 } 54 return nil 55 }