github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/filter/facts/matchers.go (about) 1 // Copyright (c) 2019-2021, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package facts 6 7 import ( 8 "fmt" 9 "regexp" 10 "strconv" 11 "strings" 12 13 "github.com/tidwall/gjson" 14 ) 15 16 func eqMatch(fact gjson.Result, value string) (bool, error) { 17 switch fact.Type { 18 case gjson.String: 19 return strings.EqualFold(fact.String(), value), nil 20 21 case gjson.Number: 22 if strings.Contains(value, ".") { 23 v, err := strconv.ParseFloat(value, 64) 24 if err != nil { 25 return false, err 26 } 27 28 return fact.Float() == v, nil 29 } 30 31 return strconv.Itoa(int(fact.Int())) == value, nil 32 33 case gjson.True: 34 return truthy(value), nil 35 36 case gjson.False: 37 return falsey(value), nil 38 39 case gjson.Null: 40 return false, nil 41 42 case gjson.JSON: 43 return false, nil 44 45 default: 46 return false, fmt.Errorf("do not know how to evaluate data of type %s", fact.Type) 47 } 48 } 49 50 func reMatch(fact gjson.Result, value string) (bool, error) { 51 switch fact.Type { 52 case gjson.String: 53 return regexMatch(fact.String(), value) 54 55 case gjson.Number: 56 if strings.Contains(value, ".") { 57 return regexMatch(fmt.Sprintf("%.4f", fact.Float()), value) 58 } 59 60 return regexMatch(strconv.Itoa(int(fact.Int())), value) 61 62 case gjson.True: 63 return truthy(strings.ToLower(value)), nil 64 65 case gjson.False: 66 return falsey(strings.ToLower(value)), nil 67 68 case gjson.Null: 69 return false, nil 70 71 case gjson.JSON: 72 return false, nil 73 74 default: 75 return false, fmt.Errorf("do not know how to evaluate data of type %s", fact.Type) 76 } 77 } 78 79 func leMatch(fact gjson.Result, value string) (bool, error) { 80 switch fact.Type { 81 case gjson.String: 82 return strings.ToLower(fact.String()) <= strings.ToLower(value), nil 83 84 case gjson.Number: 85 if strings.Contains(value, ".") { 86 v, err := strconv.ParseFloat(value, 64) 87 if err != nil { 88 return false, err 89 } 90 91 return fact.Float() <= v, nil 92 } 93 94 v, err := strconv.Atoi(value) 95 if err != nil { 96 return false, err 97 } 98 99 return int(fact.Int()) <= v, nil 100 101 default: 102 return false, fmt.Errorf("do not know how to evaluate data of type %s", fact.Type) 103 } 104 } 105 106 func geMatch(fact gjson.Result, value string) (bool, error) { 107 switch fact.Type { 108 case gjson.String: 109 return strings.ToLower(fact.String()) >= strings.ToLower(value), nil 110 111 case gjson.Number: 112 if strings.Contains(value, ".") { 113 v, err := strconv.ParseFloat(value, 64) 114 if err != nil { 115 return false, err 116 } 117 118 return fact.Float() >= v, nil 119 } 120 121 v, err := strconv.Atoi(value) 122 if err != nil { 123 return false, err 124 } 125 126 return int(fact.Int()) >= v, nil 127 128 default: 129 return false, fmt.Errorf("do not know how to evaluate data of type %s", fact.Type) 130 } 131 } 132 133 func ltMatch(fact gjson.Result, value string) (bool, error) { 134 switch fact.Type { 135 case gjson.String: 136 return strings.ToLower(fact.String()) < strings.ToLower(value), nil 137 138 case gjson.Number: 139 if strings.Contains(value, ".") { 140 v, err := strconv.ParseFloat(value, 64) 141 if err != nil { 142 return false, err 143 } 144 145 return fact.Float() < v, nil 146 } 147 148 v, err := strconv.Atoi(value) 149 if err != nil { 150 return false, err 151 } 152 153 return int(fact.Int()) < v, nil 154 155 default: 156 return false, fmt.Errorf("do not know how to evaluate data of type %s", fact.Type) 157 } 158 } 159 160 func gtMatch(fact gjson.Result, value string) (bool, error) { 161 switch fact.Type { 162 case gjson.String: 163 return strings.ToLower(fact.String()) > strings.ToLower(value), nil 164 165 case gjson.Number: 166 if strings.Contains(value, ".") { 167 f, err := strconv.ParseFloat(value, 64) 168 if err != nil { 169 return false, err 170 } 171 172 return fact.Float() >= f, nil 173 } 174 175 v, err := strconv.Atoi(value) 176 if err != nil { 177 return false, err 178 } 179 180 return int(fact.Int()) > v, nil 181 182 default: 183 return false, fmt.Errorf("do not know how to evaluate data of type %s", fact.Type) 184 } 185 } 186 187 func neMatch(fact gjson.Result, value string) (bool, error) { 188 switch fact.Type { 189 case gjson.String: 190 return !strings.EqualFold(fact.String(), value), nil 191 192 case gjson.Number: 193 if strings.Contains(value, ".") { 194 f, err := strconv.ParseFloat(value, 64) 195 if err != nil { 196 return false, err 197 } 198 199 return f != fact.Float(), nil 200 } 201 202 return strconv.Itoa(int(fact.Int())) != value, nil 203 204 case gjson.True: 205 return !truthy(strings.ToLower(value)), nil 206 207 case gjson.False: 208 return falsey(strings.ToLower(value)), nil 209 210 case gjson.Null: 211 return false, nil 212 213 case gjson.JSON: 214 return false, nil 215 216 default: 217 return false, fmt.Errorf("do not know how to evaluate data of type %s", fact.Type) 218 } 219 } 220 221 func regexMatch(value string, pattern string) (bool, error) { 222 pattern = strings.TrimLeft(pattern, "/") 223 pattern = strings.TrimRight(pattern, "/") 224 225 pattern = fmt.Sprintf("(?i)%s", pattern) 226 227 re, err := regexp.Compile(pattern) 228 if err != nil { 229 return false, err 230 } 231 232 return re.MatchString(value), nil 233 } 234 235 func truthy(value string) bool { 236 b, err := strconv.ParseBool(value) 237 238 if err == nil && b { 239 return true 240 } 241 242 return false 243 } 244 245 func falsey(value string) bool { 246 return !truthy(value) 247 }