1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
package filter
import (
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
)
// Match returns true if the given object matches the given filter.
func Match(obj any, set ClauseSet) (bool, error) {
if set.ParseInt == nil {
set.ParseInt = DefaultParseInt
}
if set.ParseUint == nil {
set.ParseUint = DefaultParseUint
}
if set.ParseString == nil {
set.ParseString = DefaultParseString
}
if set.ParseBool == nil {
set.ParseBool = DefaultParseBool
}
if set.ParseRegexp == nil {
set.ParseRegexp = DefaultParseRegexp
}
match := true
for _, clause := range set.Clauses {
value := ValueOf(obj, clause.Field)
clauseMatch, err := set.match(clause, value)
if err != nil {
return false, err
}
// Finish out logic
if clause.Not {
clauseMatch = !clauseMatch
}
switch clause.PrevLogical {
case set.Ops.And:
match = match && clauseMatch
case set.Ops.Or:
match = match || clauseMatch
default:
return false, fmt.Errorf("unexpected clause operator")
}
}
return match, nil
}
// DefaultParseInt converts the value of the clause to int64.
func DefaultParseInt(c Clause) (int64, error) {
return strconv.ParseInt(c.Value, 10, 0)
}
// DefaultParseUint converts the value of the clause to Uint64.
func DefaultParseUint(c Clause) (uint64, error) {
return strconv.ParseUint(c.Value, 10, 0)
}
// DefaultParseString converts the value of the clause to string.
func DefaultParseString(c Clause) (string, error) {
return c.Value, nil
}
// DefaultParseBool converts the value of the clause to boolean.
func DefaultParseBool(c Clause) (bool, error) {
return strconv.ParseBool(c.Value)
}
// DefaultParseRegexp converts the value of the clause to regexp.
func DefaultParseRegexp(c Clause) (*regexp.Regexp, error) {
regexpValue := c.Value
if !(strings.Contains(regexpValue, "^") || strings.Contains(regexpValue, "$")) {
regexpValue = "^" + regexpValue + "$"
}
return regexp.Compile("(?i)" + regexpValue)
}
func (s ClauseSet) match(c Clause, objValue any) (bool, error) {
var valueStr string
var valueRegexp *regexp.Regexp
var valueInt int64
var valueUint uint64
var valueBool bool
var err error
// If 'value' is type of string try to test value as a regexp.
valInfo := reflect.ValueOf(objValue)
kind := valInfo.Kind()
switch kind {
case reflect.String:
valueRegexp, _ = s.ParseRegexp(c)
if valueRegexp == nil {
valueStr, err = s.ParseString(c)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
valueInt, err = s.ParseInt(c)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
valueUint, err = s.ParseUint(c)
case reflect.Bool:
valueBool, err = s.ParseBool(c)
default:
return false, fmt.Errorf("Invalid type %q for field %q", kind.String(), c.Field)
}
if err != nil {
return false, fmt.Errorf("Failed to parse value: %w", err)
}
switch c.Operator {
case s.Ops.Equals:
if valueRegexp != nil {
return valueRegexp.MatchString(objValue.(string)), nil
}
switch val := objValue.(type) {
case string:
// Comparison is case insensitive.
return strings.EqualFold(val, valueStr), nil
case int, int8, int16, int32, int64:
return objValue == valueInt, nil
case uint, uint8, uint16, uint32, uint64:
return objValue == valueUint, nil
case bool:
return objValue == valueBool, nil
}
case s.Ops.NotEquals:
if valueRegexp != nil {
return !valueRegexp.MatchString(objValue.(string)), nil
}
switch val := objValue.(type) {
case string:
// Comparison is case insensitive.
return !strings.EqualFold(val, valueStr), nil
case int, int8, int16, int32, int64:
return objValue != valueInt, nil
case uint, uint8, uint16, uint32, uint64:
return objValue != valueUint, nil
case bool:
return objValue != valueBool, nil
}
case s.Ops.GreaterThan:
switch objValue.(type) {
case string, bool:
return false, fmt.Errorf("Invalid operator %q for field %q", c.Operator, c.Field)
case int, int8, int16, int32, int64:
return valInfo.Int() > valueInt, nil
case uint, uint8, uint16, uint32, uint64:
return valInfo.Uint() > valueUint, nil
}
case s.Ops.LessThan:
switch objValue.(type) {
case string, bool:
return false, fmt.Errorf("Invalid operator %q for field %q", c.Operator, c.Field)
case int, int8, int16, int32, int64:
return valInfo.Int() < valueInt, nil
case uint, uint8, uint16, uint32, uint64:
return valInfo.Uint() < valueUint, nil
}
case s.Ops.GreaterEqual:
switch objValue.(type) {
case string, bool:
return false, fmt.Errorf("Invalid operator %q for field %q", c.Operator, c.Field)
case int, int8, int16, int32, int64:
return valInfo.Int() >= valueInt, nil
case uint, uint8, uint16, uint32, uint64:
return valInfo.Uint() >= valueUint, nil
}
case s.Ops.LessEqual:
switch objValue.(type) {
case string, bool:
return false, fmt.Errorf("Invalid operator %q for field %q", c.Operator, c.Field)
case int, int8, int16, int32, int64:
return valInfo.Int() <= valueInt, nil
case uint, uint8, uint16, uint32, uint64:
return valInfo.Uint() <= valueUint, nil
}
default:
return false, fmt.Errorf("Unsupported operation")
}
return false, fmt.Errorf("Unsupported filter type %q for field %q", kind.String(), c.Field)
}
|