github.com/elves/elvish@v0.15.0/pkg/eval/vals/has_key.go (about)

     1  package vals
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/xiaq/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(interface{}) 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 interface{}) 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 PseudoStructMap:
    30  		return hasKeyStructMap(container.Fields(), key)
    31  	default:
    32  		var found bool
    33  		err := IterateKeys(container, func(k interface{}) 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 interface{}) 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  }