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 }