elvish/pkg/eval/vals/has_key.go

64 lines
1.5 KiB
Go

package vals
import (
"reflect"
"src.elv.sh/pkg/persistent/hashmap"
)
// HasKeyer wraps the HasKey method.
type HasKeyer interface {
// HasKey returns whether the receiver has the given argument as a valid
// key.
HasKey(any) bool
}
// HasKey returns whether a container has a key. It is implemented for the Map
// type, StructMap types, and types satisfying the HasKeyer interface. It falls
// back to iterating keys using IterateKeys, and if that fails, it falls back to
// calling Len and checking if key is a valid numeric or slice index. Otherwise
// it returns false.
func HasKey(container, key any) bool {
switch container := container.(type) {
case HasKeyer:
return container.HasKey(key)
case Map:
return hashmap.HasKey(container, key)
case StructMap:
return hasKeyStructMap(container, key)
case PseudoStructMap:
return hasKeyStructMap(container.Fields(), key)
default:
var found bool
err := IterateKeys(container, func(k any) bool {
if key == k {
found = true
}
return !found
})
if err == nil {
return found
}
if len := Len(container); len >= 0 {
// TODO(xiaq): Not all types that implement Lener have numerical
// indices
_, err := ConvertListIndex(key, len)
return err == nil
}
return false
}
}
func hasKeyStructMap(m StructMap, k any) bool {
kstring, ok := k.(string)
if !ok || kstring == "" {
return false
}
for _, fieldName := range getStructMapInfo(reflect.TypeOf(m)).fieldNames {
if fieldName == kstring {
return true
}
}
return false
}