elvish/pkg/eval/vals/assoc.go
Qi Xiao bd1a6e98dc pkg/eval/vals: Rework the structmap mechanism.
* Fields may now be exposed via getter methods.

* Field names are automatically converted to dashed-case.

* Assoc behavior is no longer implemented for structmaps.

* The IsStructMap marker method no longer takes a special marker argument. There
  is a little danger of a value accidentally implementing this method.
2020-06-28 01:26:58 +01:00

61 lines
1.6 KiB
Go

package vals
import (
"errors"
)
// Assocer wraps the Assoc method.
type Assocer interface {
// Assoc returns a slightly modified version of the receiver with key k
// associated with value v.
Assoc(k, v interface{}) (interface{}, error)
}
var (
errAssocUnsupported = errors.New("assoc is not supported")
errReplacementMustBeString = errors.New("replacement must be string")
errAssocWithSlice = errors.New("assoc with slice not yet supported")
errStructMapKey = errors.New("struct-maps must be assoced with existing key")
)
// Assoc takes a container, a key and value, and returns a modified version of
// the container, in which the key associated with the value. It is implemented
// for the builtin type string, List and Map types, StructMap types, and types
// satisfying the Assocer interface. For other types, it returns an error.
func Assoc(a, k, v interface{}) (interface{}, error) {
switch a := a.(type) {
case string:
return assocString(a, k, v)
case List:
return assocList(a, k, v)
case Map:
return a.Assoc(k, v), nil
case Assocer:
return a.Assoc(k, v)
}
return nil, errAssocUnsupported
}
func assocString(s string, k, v interface{}) (interface{}, error) {
i, j, err := convertStringIndex(k, s)
if err != nil {
return nil, err
}
repl, ok := v.(string)
if !ok {
return nil, errReplacementMustBeString
}
return s[:i] + repl + s[j:], nil
}
func assocList(l List, k, v interface{}) (interface{}, error) {
index, err := ConvertListIndex(k, l.Len())
if err != nil {
return nil, err
}
if index.Slice {
return nil, errAssocWithSlice
}
return l.Assoc(index.Lower, v), nil
}