// Package q contains a list of Matchers used to compare struct fields with values package q import ( "go/token" "reflect" ) // A Matcher is used to test against a record to see if it matches. type Matcher interface { // Match is used to test the criteria against a structure. Match(interface{}) (bool, error) } // A ValueMatcher is used to test against a reflect.Value. type ValueMatcher interface { // MatchValue tests if the given reflect.Value matches. // It is useful when the reflect.Value of an object already exists. MatchValue(*reflect.Value) (bool, error) } type cmp struct { value interface{} token token.Token } func (c *cmp) MatchField(v interface{}) (bool, error) { return compare(v, c.value, c.token), nil } type trueMatcher struct{} func (*trueMatcher) Match(i interface{}) (bool, error) { return true, nil } func (*trueMatcher) MatchValue(v *reflect.Value) (bool, error) { return true, nil } type or struct { children []Matcher } func (c *or) Match(i interface{}) (bool, error) { v := reflect.Indirect(reflect.ValueOf(i)) return c.MatchValue(&v) } func (c *or) MatchValue(v *reflect.Value) (bool, error) { for _, matcher := range c.children { if vm, ok := matcher.(ValueMatcher); ok { ok, err := vm.MatchValue(v) if err != nil { return false, err } if ok { return true, nil } continue } ok, err := matcher.Match(v.Interface()) if err != nil { return false, err } if ok { return true, nil } } return false, nil } type and struct { children []Matcher } func (c *and) Match(i interface{}) (bool, error) { v := reflect.Indirect(reflect.ValueOf(i)) return c.MatchValue(&v) } func (c *and) MatchValue(v *reflect.Value) (bool, error) { for _, matcher := range c.children { if vm, ok := matcher.(ValueMatcher); ok { ok, err := vm.MatchValue(v) if err != nil { return false, err } if !ok { return false, nil } continue } ok, err := matcher.Match(v.Interface()) if err != nil { return false, err } if !ok { return false, nil } } return true, nil } type strictEq struct { field string value interface{} } func (s *strictEq) MatchField(v interface{}) (bool, error) { return reflect.DeepEqual(v, s.value), nil } type in struct { list interface{} } func (i *in) MatchField(v interface{}) (bool, error) { ref := reflect.ValueOf(i.list) if ref.Kind() != reflect.Slice { return false, nil } c := cmp{ token: token.EQL, } for i := 0; i < ref.Len(); i++ { c.value = ref.Index(i).Interface() ok, err := c.MatchField(v) if err != nil { return false, err } if ok { return true, nil } } return false, nil } type not struct { children []Matcher } func (n *not) Match(i interface{}) (bool, error) { v := reflect.Indirect(reflect.ValueOf(i)) return n.MatchValue(&v) } func (n *not) MatchValue(v *reflect.Value) (bool, error) { var err error for _, matcher := range n.children { vm, ok := matcher.(ValueMatcher) if ok { ok, err = vm.MatchValue(v) } else { ok, err = matcher.Match(v.Interface()) } if err != nil { return false, err } if ok { return false, nil } } return true, nil } // Eq matcher, checks if the given field is equal to the given value func Eq(field string, v interface{}) Matcher { return NewFieldMatcher(field, &cmp{value: v, token: token.EQL}) } // StrictEq matcher, checks if the given field is deeply equal to the given value func StrictEq(field string, v interface{}) Matcher { return NewFieldMatcher(field, &strictEq{value: v}) } // Gt matcher, checks if the given field is greater than the given value func Gt(field string, v interface{}) Matcher { return NewFieldMatcher(field, &cmp{value: v, token: token.GTR}) } // Gte matcher, checks if the given field is greater than or equal to the given value func Gte(field string, v interface{}) Matcher { return NewFieldMatcher(field, &cmp{value: v, token: token.GEQ}) } // Lt matcher, checks if the given field is lesser than the given value func Lt(field string, v interface{}) Matcher { return NewFieldMatcher(field, &cmp{value: v, token: token.LSS}) } // Lte matcher, checks if the given field is lesser than or equal to the given value func Lte(field string, v interface{}) Matcher { return NewFieldMatcher(field, &cmp{value: v, token: token.LEQ}) } // In matcher, checks if the given field matches one of the value of the given slice. // v must be a slice. func In(field string, v interface{}) Matcher { return NewFieldMatcher(field, &in{list: v}) } // True matcher, always returns true func True() Matcher { return &trueMatcher{} } // Or matcher, checks if at least one of the given matchers matches the record func Or(matchers ...Matcher) Matcher { return &or{children: matchers} } // And matcher, checks if all of the given matchers matches the record func And(matchers ...Matcher) Matcher { return &and{children: matchers} } // Not matcher, checks if all of the given matchers return false func Not(matchers ...Matcher) Matcher { return ¬{children: matchers} }