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