github.com/viant/toolbox@v0.34.5/predicates.go (about)

     1  package toolbox
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"time"
     8  )
     9  
    10  //TrueProvider represents a true provider
    11  var TrueProvider = func(input interface{}) bool {
    12  	return true
    13  }
    14  
    15  type withinSecPredicate struct {
    16  	baseTime        time.Time
    17  	deltaInSeconds  int
    18  	dateLayout      string
    19  	actual          string
    20  	elapsed         time.Duration
    21  	maxAllowedDelay time.Duration
    22  }
    23  
    24  func (p *withinSecPredicate) String() string {
    25  	return fmt.Sprintf("(elapsed: %d, max allowed delay: %d)\n", int(p.elapsed), int(p.maxAllowedDelay))
    26  }
    27  
    28  //Apply returns true if passed in time is within deltaInSeconds from baseTime
    29  func (p *withinSecPredicate) Apply(value interface{}) bool {
    30  	timeValue, err := ToTime(value, p.dateLayout)
    31  	if err != nil {
    32  		return false
    33  	}
    34  	elapsed := timeValue.Sub(p.baseTime)
    35  	if elapsed < 0 {
    36  		elapsed *= -1
    37  	}
    38  	var maxAllowedDelay = time.Duration(p.deltaInSeconds) * time.Second
    39  	var passed = maxAllowedDelay >= elapsed
    40  	if !passed {
    41  		p.elapsed = elapsed
    42  		p.maxAllowedDelay = maxAllowedDelay
    43  	}
    44  	return passed
    45  }
    46  
    47  func (p *withinSecPredicate) ToString() string {
    48  	return fmt.Sprintf(" %v within %v s", p.baseTime, p.deltaInSeconds)
    49  }
    50  
    51  //NewWithinPredicate returns new NewWithinPredicate predicate, it takes base time, delta in second, and dateLayout
    52  func NewWithinPredicate(baseTime time.Time, deltaInSeconds int, dateLayout string) Predicate {
    53  	return &withinSecPredicate{
    54  		baseTime:       baseTime,
    55  		deltaInSeconds: deltaInSeconds,
    56  		dateLayout:     dateLayout,
    57  	}
    58  }
    59  
    60  type betweenPredicate struct {
    61  	from float64
    62  	to   float64
    63  }
    64  
    65  func (p *betweenPredicate) Apply(value interface{}) bool {
    66  	floatValue := AsFloat(value)
    67  	return floatValue >= p.from && floatValue <= p.to
    68  }
    69  
    70  func (p *betweenPredicate) String() string {
    71  	return fmt.Sprintf("x BETWEEN %v AND %v", p.from, p.to)
    72  }
    73  
    74  //NewBetweenPredicate creates a new BETWEEN predicate, it takes from, and to.
    75  func NewBetweenPredicate(from, to interface{}) Predicate {
    76  	return &betweenPredicate{
    77  		from: AsFloat(from),
    78  		to:   AsFloat(to),
    79  	}
    80  }
    81  
    82  type inPredicate struct {
    83  	predicate Predicate
    84  }
    85  
    86  func (p *inPredicate) Apply(value interface{}) bool {
    87  	return p.predicate.Apply(value)
    88  }
    89  
    90  //NewInPredicate creates a new IN predicate
    91  func NewInPredicate(values ...interface{}) Predicate {
    92  	converted, kind := DiscoverCollectionValuesAndKind(values)
    93  	switch kind {
    94  	case reflect.Int:
    95  		predicate := inIntPredicate{values: make(map[int]bool)}
    96  		SliceToMap(converted, predicate.values, func(item interface{}) int {
    97  			return AsInt(item)
    98  		}, TrueProvider)
    99  		return &predicate
   100  	case reflect.Float64:
   101  		predicate := inFloatPredicate{values: make(map[float64]bool)}
   102  		SliceToMap(converted, predicate.values, func(item interface{}) float64 {
   103  			return AsFloat(item)
   104  		}, TrueProvider)
   105  		return &predicate
   106  	default:
   107  		predicate := inStringPredicate{values: make(map[string]bool)}
   108  		SliceToMap(converted, predicate.values, func(item interface{}) string {
   109  			return AsString(item)
   110  		}, TrueProvider)
   111  		return &predicate
   112  	}
   113  }
   114  
   115  type inFloatPredicate struct {
   116  	values map[float64]bool
   117  }
   118  
   119  func (p *inFloatPredicate) Apply(value interface{}) bool {
   120  	candidate := AsFloat(value)
   121  	return p.values[candidate]
   122  }
   123  
   124  type inIntPredicate struct {
   125  	values map[int]bool
   126  }
   127  
   128  func (p *inIntPredicate) Apply(value interface{}) bool {
   129  	candidate := AsInt(value)
   130  	return p.values[int(candidate)]
   131  }
   132  
   133  type inStringPredicate struct {
   134  	values map[string]bool
   135  }
   136  
   137  func (p *inStringPredicate) Apply(value interface{}) bool {
   138  	candidate := AsString(value)
   139  	return p.values[candidate]
   140  }
   141  
   142  type numericComparablePredicate struct {
   143  	rightOperand float64
   144  	operator     string
   145  }
   146  
   147  func (p *numericComparablePredicate) Apply(value interface{}) bool {
   148  	leftOperand := AsFloat(value)
   149  	switch p.operator {
   150  	case ">":
   151  		return leftOperand > p.rightOperand
   152  	case ">=":
   153  		return leftOperand >= p.rightOperand
   154  	case "<":
   155  		return leftOperand < p.rightOperand
   156  	case "<=":
   157  		return leftOperand <= p.rightOperand
   158  	case "=":
   159  		return leftOperand == p.rightOperand
   160  	case "!=":
   161  		return leftOperand != p.rightOperand
   162  	}
   163  	return false
   164  }
   165  
   166  type stringComparablePredicate struct {
   167  	rightOperand string
   168  	operator     string
   169  }
   170  
   171  func (p *stringComparablePredicate) Apply(value interface{}) bool {
   172  	leftOperand := AsString(value)
   173  
   174  	switch p.operator {
   175  	case "=":
   176  		return leftOperand == p.rightOperand
   177  	case "!=":
   178  		return leftOperand != p.rightOperand
   179  	}
   180  	return false
   181  }
   182  
   183  //NewComparablePredicate create a new comparable predicate for =, !=, >=, <=
   184  func NewComparablePredicate(operator string, leftOperand interface{}) Predicate {
   185  	if CanConvertToFloat(leftOperand) {
   186  		return &numericComparablePredicate{AsFloat(leftOperand), operator}
   187  	}
   188  	return &stringComparablePredicate{AsString(leftOperand), operator}
   189  }
   190  
   191  type nilPredicate struct{}
   192  
   193  func (p *nilPredicate) Apply(value interface{}) bool {
   194  	return value == nil || reflect.ValueOf(value).IsNil()
   195  }
   196  
   197  //NewNilPredicate returns a new nil predicate
   198  func NewNilPredicate() Predicate {
   199  	return &nilPredicate{}
   200  }
   201  
   202  type likePredicate struct {
   203  	matchingFragments []string
   204  }
   205  
   206  func (p *likePredicate) Apply(value interface{}) bool {
   207  	textValue := strings.ToLower(AsString(value))
   208  	for _, matchingFragment := range p.matchingFragments {
   209  		matchingIndex := strings.Index(textValue, matchingFragment)
   210  		if matchingIndex == -1 {
   211  			return false
   212  		}
   213  		if matchingIndex < len(textValue) {
   214  			textValue = textValue[matchingIndex:]
   215  		}
   216  	}
   217  	return true
   218  }
   219  
   220  //NewLikePredicate create a new like predicate
   221  func NewLikePredicate(matching string) Predicate {
   222  	return &likePredicate{matchingFragments: strings.Split(strings.ToLower(matching), "%")}
   223  }