func exprError(format string, args ...interface{}) error {
	return fmt.Errorf("expression: "+format, args...)
}
exprError returns a formatted error specific to expressions.
package resmgr
import (
	"fmt"
	"path"
	"path/filepath"
	"strings"
	logger "github.com/intel/cri-resource-manager/pkg/log"
)
type Evaluable interface {
	Eval(string) interface{}
}
type Expression struct {
	Key    string   `json:"key"`               
	Op     Operator `json:"operator"`          
	Values []string `json:"values,omitempty"`  
}
const (
	KeyPod       = "pod"
	KeyID        = "id"
	KeyUID       = "uid"
	KeyName      = "name"
	KeyNamespace = "namespace"
	KeyQOSClass  = "qosclass"
	KeyLabels    = "labels"
	KeyTags      = "tags"
)
type Operator string
const (
	Equals Operator = "Equals"
	NotEqual Operator = "NotEqual"
	In Operator = "In"
	NotIn Operator = "NotIn"
	Exists Operator = "Exists"
	NotExist Operator = "NotExist"
	AlwaysTrue Operator = "AlwaysTrue"
	Matches Operator = "Matches"
	MatchesNot Operator = "MatchesNot"
	MatchesAny Operator = "MatchesAny"
	MatchesNone Operator = "MatchesNone"
)
var log = logger.NewLogger("expression")
func (e *Expression) Validate() error {
	if e == nil {
		return exprError("nil expression")
	}
	switch e.Op {
	case Equals, NotEqual:
		if len(e.Values) != 1 {
			return exprError("invalid expression, '%s' requires a single value", e.Op)
		}
	case Matches, MatchesNot:
		if len(e.Values) != 1 {
			return exprError("invalid expression, '%s' requires a single value", e.Op)
		}
	case Exists, NotExist:
		if e.Values != nil && len(e.Values) != 0 {
			return exprError("invalid expression, '%s' does not take any values", e.Op)
		}
	case In, NotIn:
	case MatchesAny, MatchesNone:
	case AlwaysTrue:
	default:
		return exprError("invalid expression, unknown operator: %q", e.Op)
	}
	return nil
}
func (e *Expression) Evaluate(subject Evaluable) bool {
	log.Debug("evaluating %q @ %s...", *e, subject)
	value, ok := e.KeyValue(subject)
	result := false
	switch e.Op {
	case Equals:
		result = ok && (value == e.Values[0] || e.Values[0] == "*")
	case NotEqual:
		result = !ok || value != e.Values[0]
	case Matches, MatchesNot:
		match := false
		if ok {
			match, _ = filepath.Match(e.Values[0], value)
		}
		result = ok && match
		if e.Op == MatchesNot {
			result = !result
		}
	case In, NotIn:
		if ok {
			for _, v := range e.Values {
				if value == v || v == "*" {
					result = true
				}
			}
		}
		if e.Op == NotIn {
			result = !result
		}
	case MatchesAny, MatchesNone:
		if ok {
			for _, pattern := range e.Values {
				if match, _ := filepath.Match(pattern, value); match {
					result = true
					break
				}
			}
		}
		if e.Op == MatchesNone {
			result = !result
		}
	case Exists:
		result = ok
	case NotExist:
		result = !ok
	case AlwaysTrue:
		result = true
	}
	log.Debug("%q @ %s => %v", *e, subject, result)
	return result
}
func (e *Expression) KeyValue(subject Evaluable) (string, bool) {
	log.Debug("looking up %q @ %s...", e.Key, subject)
	value := ""
	ok := false
	keys, vsep := splitKeys(e.Key)
	if len(keys) == 1 {
		value, ok, _ = ResolveRef(subject, keys[0])
	} else {
		vals := make([]string, 0, len(keys))
		for _, key := range keys {
			v, found, _ := ResolveRef(subject, key)
			vals = append(vals, v)
			ok = ok || found
		}
		value = strings.Join(vals, vsep)
	}
	log.Debug("%q @ %s => %q, %v", e.Key, subject, value, ok)
	return value, ok
}
func splitKeys(keys string) ([]string, string) {
	if len(keys) < 4 || keys[0] != ':' {
		return []string{keys}, ""
	}
	keys = keys[1:]
	ksep := keys[0:1]
	vsep := keys[1:2]
	if validSeparator(ksep[0]) && validSeparator(vsep[0]) {
		keys = keys[2:]
	} else {
		ksep = ":"
		vsep = ":"
	}
	return strings.Split(keys, ksep), vsep
}
func validSeparator(b byte) bool {
	switch {
	case '0' <= b && b <= '9':
		return false
	case 'a' <= b && b <= 'z':
		return false
	case 'A' <= b && b <= 'Z':
		return false
	case b == '/', b == '.':
		return false
	}
	return true
}
func ResolveRef(subject Evaluable, spec string) (string, bool, error) {
	var obj interface{}
	log.Debug("resolving %q @ %s...", spec, subject)
	spec = path.Clean(spec)
	ref := strings.Split(spec, "/")
	if len(ref) == 1 {
		if strings.Index(spec, ".") != -1 {
			ref = []string{"labels", spec}
		}
	}
	obj = subject
	for len(ref) > 0 {
		key := ref[0]
		log.Debug("resolve walking %q @ %s...", key, obj)
		switch v := obj.(type) {
		case string:
			obj = v
		case map[string]string:
			value, ok := v[key]
			if !ok {
				return "", false, nil
			}
			obj = value
		case error:
			return "", false, exprError("%s: failed to resolve %q: %v", subject, spec, v)
		default:
			e, ok := obj.(Evaluable)
			if !ok {
				return "", false, exprError("%s: failed to resolve %q, unexpected type %T",
					subject, spec, obj)
			}
			obj = e.Eval(key)
		}
		ref = ref[1:]
	}
	str, ok := obj.(string)
	if !ok {
		return "", false, exprError("%s: reference %q resolved to non-string: %T",
			subject, spec, obj)
	}
	log.Debug("resolved %q @ %s => %s", spec, subject, str)
	return str, true, nil
}
func (e *Expression) String() string {
	return fmt.Sprintf("<%s %s %s>", e.Key, e.Op, strings.Join(e.Values, ","))
}
func (e *Expression) DeepCopy() *Expression {
	out := &Expression{}
	e.DeepCopyInto(out)
	return out
}
func (e *Expression) DeepCopyInto(out *Expression) {
	out.Key = e.Key
	out.Op = e.Op
	out.Values = make([]string, len(e.Values))
	copy(out.Values, e.Values)
}