github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/vals/index.go (about) 1 package vals 2 3 import ( 4 "errors" 5 "os" 6 "reflect" 7 ) 8 9 // Indexer wraps the Index method. 10 type Indexer interface { 11 // Index retrieves the value corresponding to the specified key in the 12 // container. It returns the value (if any), and whether it actually exists. 13 Index(k interface{}) (v interface{}, ok bool) 14 } 15 16 // ErrIndexer wraps the Index method. 17 type ErrIndexer interface { 18 // Index retrieves one value from the receiver at the specified index. 19 Index(k interface{}) (interface{}, error) 20 } 21 22 var errNotIndexable = errors.New("not indexable") 23 24 type noSuchKeyError struct { 25 key interface{} 26 } 27 28 // NoSuchKey returns an error indicating that a key is not found in a map-like 29 // value. 30 func NoSuchKey(k interface{}) error { 31 return noSuchKeyError{k} 32 } 33 34 func (err noSuchKeyError) Error() string { 35 return "no such key: " + ReprPlain(err.key) 36 } 37 38 // Index indexes a value with the given key. It is implemented for the builtin 39 // type string, *os.File, List, StructMap and PseudoStructMap types, and types 40 // satisfying the ErrIndexer or Indexer interface (the Map type satisfies 41 // Indexer). For other types, it returns a nil value and a non-nil error. 42 func Index(a, k interface{}) (interface{}, error) { 43 switch a := a.(type) { 44 case string: 45 return indexString(a, k) 46 case *os.File: 47 return indexFile(a, k) 48 case ErrIndexer: 49 return a.Index(k) 50 case Indexer: 51 v, ok := a.Index(k) 52 if !ok { 53 return nil, NoSuchKey(k) 54 } 55 return v, nil 56 case List: 57 return indexList(a, k) 58 case StructMap: 59 return indexStructMap(a, k) 60 case PseudoStructMap: 61 return indexStructMap(a.Fields(), k) 62 default: 63 return nil, errNotIndexable 64 } 65 } 66 67 func indexFile(f *os.File, k interface{}) (interface{}, error) { 68 switch k { 69 case "fd": 70 return int(f.Fd()), nil 71 case "name": 72 return f.Name(), nil 73 } 74 return nil, NoSuchKey(k) 75 } 76 77 func indexStructMap(a StructMap, k interface{}) (interface{}, error) { 78 fieldName, ok := k.(string) 79 if !ok || fieldName == "" { 80 return nil, NoSuchKey(k) 81 } 82 aValue := reflect.ValueOf(a) 83 it := iterateStructMap(reflect.TypeOf(a)) 84 for it.Next() { 85 k, v := it.Get(aValue) 86 if k == fieldName { 87 return FromGo(v), nil 88 } 89 } 90 return nil, NoSuchKey(fieldName) 91 }