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  }