Files
2026-07-13 12:32:14 +08:00

151 lines
3.9 KiB
Go

package evaluate
import (
"fmt"
"regexp"
"strings"
"time"
"github.com/kashav/fsql/tokenizer"
"github.com/kashav/fsql/transform"
)
// cmpAlpha performs alphabetic comparison on a and b.
func cmpAlpha(o *Opts, a, b interface{}) (result bool, err error) {
switch o.Operator {
case tokenizer.Equals:
result = a.(string) == b.(string)
case tokenizer.NotEquals:
result = a.(string) != b.(string)
case tokenizer.Like:
aStr, bStr := a.(string), b.(string)
if strings.HasPrefix(bStr, "%") && strings.HasSuffix(bStr, "%") {
result = strings.Contains(aStr, bStr[1:len(bStr)-1])
} else if strings.HasPrefix(bStr, "%") {
result = strings.HasSuffix(aStr, bStr[1:])
} else if strings.HasSuffix(bStr, "%") {
result = strings.HasPrefix(aStr, bStr[:len(bStr)-1])
} else {
result = strings.Contains(aStr, bStr)
}
case tokenizer.RLike:
result = regexp.MustCompile(b.(string)).MatchString(a.(string))
case tokenizer.In:
switch t := b.(type) {
case map[interface{}]bool:
if _, ok := t[a.(string)]; ok {
result = true
}
case []string:
for _, el := range t {
if a.(string) == el {
result = true
}
}
case string:
for _, el := range strings.Split(t, ",") {
if a.(string) == el {
result = true
}
}
}
default:
err = &ErrUnsupportedOperator{o.Attribute, o.Operator}
}
return result, err
}
// cmpNumeric performs numeric comparison on a and b.
func cmpNumeric(o *Opts, a, b interface{}) (result bool, err error) {
switch o.Operator {
case tokenizer.Equals:
result = a.(int64) == b.(int64)
case tokenizer.NotEquals:
result = a.(int64) != b.(int64)
case tokenizer.GreaterThanEquals:
result = a.(int64) >= b.(int64)
case tokenizer.GreaterThan:
result = a.(int64) > b.(int64)
case tokenizer.LessThanEquals:
result = a.(int64) <= b.(int64)
case tokenizer.LessThan:
result = a.(int64) < b.(int64)
case tokenizer.In:
if _, ok := b.(map[interface{}]bool)[a.(int64)]; ok {
result = true
}
default:
err = &ErrUnsupportedOperator{o.Attribute, o.Operator}
}
return result, err
}
// cmpTime performs time comparison on a and b.
func cmpTime(o *Opts, a, b interface{}) (result bool, err error) {
switch o.Operator {
case tokenizer.Equals:
result = a.(time.Time).Equal(b.(time.Time))
case tokenizer.NotEquals:
result = !a.(time.Time).Equal(b.(time.Time))
case tokenizer.GreaterThanEquals:
result = a.(time.Time).After(b.(time.Time)) || a.(time.Time).Equal(b.(time.Time))
case tokenizer.GreaterThan:
result = a.(time.Time).After(b.(time.Time))
case tokenizer.LessThanEquals:
result = a.(time.Time).Before(b.(time.Time)) || a.(time.Time).Equal(b.(time.Time))
case tokenizer.LessThan:
result = a.(time.Time).Before(b.(time.Time))
case tokenizer.In:
if _, ok := b.(map[interface{}]bool)[a.(time.Time)]; ok {
result = true
}
default:
err = &ErrUnsupportedOperator{o.Attribute, o.Operator}
}
return result, err
}
// cmpMode performs mode comparison with info and typ.
func cmpMode(o *Opts) (result bool, err error) {
if o.Operator != tokenizer.Is {
return false, &ErrUnsupportedOperator{o.Attribute, o.Operator}
}
switch strings.ToUpper(o.Value.(string)) {
case "DIR":
result = o.File.Mode().IsDir()
case "REG":
result = o.File.Mode().IsRegular()
default:
result = false
}
return result, err
}
// cmpHash computes the hash of the current file and compares it with the
// provided value.
func cmpHash(o *Opts) (result bool, err error) {
hashType := "SHA1"
if len(o.Modifiers) > 0 {
hashType = o.Modifiers[0].Name
}
hashFunc := transform.FindHash(hashType)
if hashFunc == nil {
return false, fmt.Errorf("unexpected hash algorithm %s", hashType)
}
h, err := transform.ComputeHash(o.File, o.Path, hashFunc())
if err != nil {
return false, err
}
switch o.Operator {
case tokenizer.Equals:
result = h == o.Value
case tokenizer.NotEquals:
result = h != o.Value
default:
err = &ErrUnsupportedOperator{o.Attribute, o.Operator}
}
return result, err
}