src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/vals/index.go (about)

     1  package vals
     2  
     3  import (
     4  	"errors"
     5  	"os"
     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 any) (v any, 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 any) (any, error)
    19  }
    20  
    21  var errNotIndexable = errors.New("not indexable")
    22  
    23  type noSuchKeyError struct {
    24  	key any
    25  }
    26  
    27  // NoSuchKey returns an error indicating that a key is not found in a map-like
    28  // value.
    29  func NoSuchKey(k any) error {
    30  	return noSuchKeyError{k}
    31  }
    32  
    33  func (err noSuchKeyError) Error() string {
    34  	return "no such key: " + ReprPlain(err.key)
    35  }
    36  
    37  // Index indexes a value with the given key. It is implemented for the builtin
    38  // type string, *os.File, List, StructMap and PseudoStructMap types, and types
    39  // satisfying the ErrIndexer or Indexer interface (the Map type satisfies
    40  // Indexer). For other types, it returns a nil value and a non-nil error.
    41  func Index(a, k any) (any, error) {
    42  	convertResult := func(v any, ok bool) (any, error) {
    43  		if !ok {
    44  			return nil, NoSuchKey(k)
    45  		}
    46  		return v, nil
    47  	}
    48  	switch a := a.(type) {
    49  	case string:
    50  		return indexString(a, k)
    51  	case *os.File:
    52  		return indexFile(a, k)
    53  	case ErrIndexer:
    54  		return a.Index(k)
    55  	case Indexer:
    56  		return convertResult(a.Index(k))
    57  	case List:
    58  		return indexList(a, k)
    59  	case StructMap:
    60  		return convertResult(indexStructMap(a, k))
    61  	case PseudoMap:
    62  		return convertResult(indexStructMap(a.Fields(), k))
    63  	default:
    64  		return nil, errNotIndexable
    65  	}
    66  }
    67  
    68  func indexFile(f *os.File, k any) (any, error) {
    69  	switch k {
    70  	case "fd":
    71  		return int(f.Fd()), nil
    72  	case "name":
    73  		return f.Name(), nil
    74  	}
    75  	return nil, NoSuchKey(k)
    76  }
    77  
    78  func indexStructMap(a StructMap, k any) (any, bool) {
    79  	fieldName, ok := k.(string)
    80  	if !ok || fieldName == "" {
    81  		return nil, false
    82  	}
    83  	for it := iterateStructMap(a); it.HasElem(); it.Next() {
    84  		k, v := it.elem()
    85  		if k == fieldName {
    86  			return FromGo(v), true
    87  		}
    88  	}
    89  	return nil, false
    90  }