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  }