Skip to content

Commit

Permalink
Added and tested StringMatches condition
Browse files Browse the repository at this point in the history
  • Loading branch information
redjack96 committed Sep 3, 2024
1 parent 61502a2 commit 2b5ad4b
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 11 deletions.
2 changes: 1 addition & 1 deletion internal/asl/choice_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ const (
StringLessThanEqualsPath ComparisonOperatorKind = "StringLessThanEqualsPath"
StringGreaterThanEquals ComparisonOperatorKind = "StringGreaterThanEquals"
StringGreaterThanEqualsPath ComparisonOperatorKind = "StringGreaterThanEqualsPath"
StringMatches ComparisonOperatorKind = "StringMatches" // StringMatches The value MUST be a StringComparator which MAY contain one or more "*" characters. The expression yields true if the data value selected by the Variable Path matches the value, where "*" in the value matches zero or more characters. Thus, foo*.log matches foo23.log, *.log matches zebra.log, and foo*.* matches foobar.zebra. No characters other than "*" have any special meaning during matching. NumericEquals ComparisonOperatorKind = "NumericEquals"
StringMatches ComparisonOperatorKind = "StringMatches" // uses the '*' operator to matches a string against another
NumericEquals ComparisonOperatorKind = "NumericEquals"
NumericEqualsPath ComparisonOperatorKind = "NumericEqualsPath"
NumericLessThan ComparisonOperatorKind = "NumericLessThan"
Expand Down
75 changes: 65 additions & 10 deletions internal/fc/conditions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"fmt"
"github.com/grussorusso/serverledge/internal/function"
"github.com/grussorusso/serverledge/utils"
"regexp"
"strconv"
"strings"
)

type Predicate struct {
Expand All @@ -21,16 +23,17 @@ type Condition struct {
type CondEnum string

const (
And CondEnum = "And"
Or CondEnum = "Or"
Not CondEnum = "Not"
Const CondEnum = "Const"
Eq CondEnum = "Eq" // also works for strings
Diff CondEnum = "Diff" // also works for strings
Greater CondEnum = "Greater" // also works for strings
Smaller CondEnum = "Smaller" // also works for strings
Empty CondEnum = "Empty" // for collections
IsNumeric CondEnum = "IsNumeric" // for collections
And CondEnum = "And"
Or CondEnum = "Or"
Not CondEnum = "Not"
Const CondEnum = "Const"
Eq CondEnum = "Eq" // also works for strings
Diff CondEnum = "Diff" // also works for strings
Greater CondEnum = "Greater" // also works for strings
Smaller CondEnum = "Smaller" // also works for strings
Empty CondEnum = "Empty" // for collections
IsNumeric CondEnum = "IsNumeric" // for collections
StringMatches CondEnum = "StringMatches"
)

func (p Predicate) Test(input map[string]interface{}) bool {
Expand Down Expand Up @@ -106,6 +109,8 @@ func (c Condition) String() string {
return "empty input"
case IsNumeric:
return fmt.Sprintf("IsNumeric(%v)", c.Op[0])
case StringMatches:
return fmt.Sprintf("StringMatches(%s,%s)", c.Op[0], c.Op[1])
default:
return ""
}
Expand Down Expand Up @@ -301,6 +306,48 @@ func (c Condition) Test(input map[string]interface{}) (bool, error) {
return false, err
}
return isNumeric(ops[0]), nil
case StringMatches:
ops, err := c.findInputs(input)
inputString, okString := ops[0].(string)
if !okString {
return false, fmt.Errorf("condition StringMatches: first operand (string to match) is not a string")
}
pattern, okPattern := ops[1].(string)
if !okPattern {
return false, fmt.Errorf("condition StringMatches: second operand (pattern) is not a string")
}

// Replace \\* with a placeholder to treat it as a literal *
pattern = strings.ReplaceAll(pattern, "\\\\*", "\x00")

// Replace \\\\ with a placeholder to treat it as a literal \
pattern = strings.ReplaceAll(pattern, "\\\\\\\\", "\x01")

// Check for unescaped single backslashes
if strings.Contains(pattern, "\\") {
return false, fmt.Errorf("runtime error: open escape sequence found")
}

// Escape special regex characters in the pattern except for "*"
escapedPattern := regexp.QuoteMeta(pattern)

// Replace placeholder \x00 back to literal * in the escaped pattern
escapedPattern = strings.ReplaceAll(escapedPattern, "\x00", "\\*")

// Replace placeholder \x01 back to literal \ in the escaped pattern
escapedPattern = strings.ReplaceAll(escapedPattern, "\x01", "\\\\")

// Replace "*" with ".*" to match zero or more characters
regexPattern := strings.ReplaceAll(escapedPattern, "\\*", ".*")

// Compile the regex pattern
re, err := regexp.Compile("^" + regexPattern + "$")
if err != nil {
return false, err
}

// Check if the input matches the regex pattern
return re.MatchString(inputString), nil
default:
return false, fmt.Errorf("invalid operation condition %s", c.Type)
}
Expand Down Expand Up @@ -545,6 +592,14 @@ func NewIsNumericParamCondition(param1 *ParamOrValue) Condition {
}
}

func NewStringMatchesParamCondition(param1 *ParamOrValue, param2 *ParamOrValue) Condition {
return Condition{
Type: StringMatches,
Find: []bool{param1.IsParam, param2.IsParam},
Op: []interface{}{param1.GetOperand(), param2.GetOperand()},
}
}

//func NewEmptyParamCondition(input map[string]interface{}, param1 string) Condition {
// val1, found := input[param1]
// if !found {
Expand Down
23 changes: 23 additions & 0 deletions internal/test/condition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,26 @@ func TestStringGreaterAndSmaller(t *testing.T) {
utils.AssertFalse(t, ok)

}

func TestStringMatches(t *testing.T) {
tests := []struct {
input string
pattern string
match bool
}{
{"foo23.log", "foo*.log", true},
{"zebra.log", "*.log", true},
{"foobar.zebra", "foo*.*", true},
{"test.log", "foo*.log", false},
{"foo.log", "*.txt", false},
{"foo.log", "*.txt", false},
{"fo*o.log", "fo\\\\*o.log", true},
}

for _, test := range tests {
cond := fc.NewStringMatchesParamCondition(fc.NewValue(test.input), fc.NewValue(test.pattern))
ok, err := cond.Test(map[string]interface{}{})
utils.AssertNil(t, err)
utils.AssertEqualsMsg(t, ok, test.match, fmt.Sprintf("expected %s to match %s", test.input, test.pattern))
}
}

0 comments on commit 2b5ad4b

Please sign in to comment.