src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/vals/has_key.go (about) 1 package vals 2 3 import ( 4 "reflect" 5 6 "src.elv.sh/pkg/persistent/hashmap" 7 ) 8 9 // HasKeyer wraps the HasKey method. 10 type HasKeyer interface { 11 // HasKey returns whether the receiver has the given argument as a valid 12 // key. 13 HasKey(any) bool 14 } 15 16 // HasKey returns whether a container has a key. It is implemented for the Map 17 // type, StructMap types, and types satisfying the HasKeyer interface. It falls 18 // back to iterating keys using IterateKeys, and if that fails, it falls back to 19 // calling Len and checking if key is a valid numeric or slice index. Otherwise 20 // it returns false. 21 func HasKey(container, key any) bool { 22 switch container := container.(type) { 23 case HasKeyer: 24 return container.HasKey(key) 25 case Map: 26 return hashmap.HasKey(container, key) 27 case StructMap: 28 return hasKeyStructMap(container, key) 29 case PseudoMap: 30 return hasKeyStructMap(container.Fields(), key) 31 default: 32 var found bool 33 err := IterateKeys(container, func(k any) bool { 34 if key == k { 35 found = true 36 } 37 return !found 38 }) 39 if err == nil { 40 return found 41 } 42 if len := Len(container); len >= 0 { 43 // TODO(xiaq): Not all types that implement Lener have numerical 44 // indices 45 _, err := ConvertListIndex(key, len) 46 return err == nil 47 } 48 return false 49 } 50 } 51 52 func hasKeyStructMap(m StructMap, k any) bool { 53 kstring, ok := k.(string) 54 if !ok || kstring == "" { 55 return false 56 } 57 for _, fieldName := range getStructMapInfo(reflect.TypeOf(m)).fieldNames { 58 if fieldName == kstring { 59 return true 60 } 61 } 62 return false 63 }