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

     1  package vals
     2  
     3  import (
     4  	"errors"
     5  )
     6  
     7  // Assocer wraps the Assoc method.
     8  type Assocer interface {
     9  	// Assoc returns a slightly modified version of the receiver with key k
    10  	// associated with value v.
    11  	Assoc(k, v any) (any, error)
    12  }
    13  
    14  var (
    15  	errAssocUnsupported        = errors.New("assoc is not supported")
    16  	errReplacementMustBeString = errors.New("replacement must be string")
    17  	errAssocWithSlice          = errors.New("assoc with slice not yet supported")
    18  )
    19  
    20  // Assoc takes a container, a key and value, and returns a modified version of
    21  // the container, in which the key associated with the value. It is implemented
    22  // for the builtin type string, List and Map types, StructMap types, and types
    23  // satisfying the Assocer interface. For other types, it returns an error.
    24  func Assoc(a, k, v any) (any, error) {
    25  	switch a := a.(type) {
    26  	case string:
    27  		return assocString(a, k, v)
    28  	case List:
    29  		return assocList(a, k, v)
    30  	case Map:
    31  		return a.Assoc(k, v), nil
    32  	case StructMap:
    33  		return promoteToMap(a).Assoc(k, v), nil
    34  	case Assocer:
    35  		return a.Assoc(k, v)
    36  	}
    37  	return nil, errAssocUnsupported
    38  }
    39  
    40  func assocString(s string, k, v any) (any, error) {
    41  	i, j, err := convertStringIndex(k, s)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	repl, ok := v.(string)
    46  	if !ok {
    47  		return nil, errReplacementMustBeString
    48  	}
    49  	return s[:i] + repl + s[j:], nil
    50  }
    51  
    52  func assocList(l List, k, v any) (any, error) {
    53  	index, err := ConvertListIndex(k, l.Len())
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	if index.Slice {
    58  		return nil, errAssocWithSlice
    59  	}
    60  	return l.Assoc(index.Lower, v), nil
    61  }