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  }