diff --git a/issues_test.go b/issues_test.go index 85b6602..c2a289b 100644 --- a/issues_test.go +++ b/issues_test.go @@ -361,3 +361,50 @@ func TestIssue110(t *testing.T) { expected := `["111","333"]` assert.JSONEq(t, expected, result.String()) } + +func TestIssue125_InOperatorWithVarsInSlice(t *testing.T) { + // This test demonstrates the issue: vars within slices are not resolved + rule := strings.NewReader(`{"in": [{"var": "needle"}, [{"var": "item1"}, {"var": "item2"}]]}`) + data := strings.NewReader(`{"needle":"foo", "item1":"bar", "item2":"foo"}`) + + var result bytes.Buffer + err := jsonlogic.Apply(rule, data, &result) + if err != nil { + t.Fatal(err) + } + + // Should be true because "foo" should be found in the resolved array ["bar", "foo"] + // Currently fails because it compares "foo" against unresolved [{"var": "item1"}, {"var": "item2"}] + expected := `true` + assert.JSONEq(t, expected, result.String()) +} + +func TestIssue125_CustomOperatorWithVarsInSlice(t *testing.T) { + // Add a custom operator that processes slice elements + jsonlogic.AddOperator("contains_any", func(values, data any) any { + parsed := values.([]any) + needle := parsed[0] + haystack := parsed[1].([]any) + + for _, item := range haystack { + if item == needle { + return true + } + } + return false + }) + + rule := strings.NewReader(`{"contains_any": [{"var": "needle"}, [{"var": "item1"}, {"var": "item2"}]]}`) + data := strings.NewReader(`{"needle":"foo", "item1":"bar", "item2":"foo"}`) + + var result bytes.Buffer + err := jsonlogic.Apply(rule, data, &result) + if err != nil { + t.Fatal(err) + } + + // Should be true because "foo" should be found in the resolved array ["bar", "foo"] + // Currently fails because the custom operator receives unresolved [{"var": "item1"}, {"var": "item2"}] + expected := `true` + assert.JSONEq(t, expected, result.String()) +} diff --git a/jsonlogic.go b/jsonlogic.go index 3546e9a..541b272 100644 --- a/jsonlogic.go +++ b/jsonlogic.go @@ -162,21 +162,13 @@ func parseValues(values, data any) any { if typing.IsMap(value) { parsed = append(parsed, apply(value, data)) } else { - parsed = append(parsed, value) + parsed = append(parsed, parseValues(value, data)) } } return parsed } -// If values represents a map (an operation), returns the result. Otherwise returns the -// values without parsing. This means that each of the returned values might be a subtree -// of JSONLogic. -// Used in lazy evaluation of "AND", "OR", and "IF" operators -func getValuesWithoutParsing(values, data any) any { - return values.([]any) -} - func apply(rules, data any) any { ruleMap := rules.(map[string]any) diff --git a/logic.go b/logic.go index 617386a..a2405a8 100644 --- a/logic.go +++ b/logic.go @@ -5,7 +5,7 @@ import ( ) func _and(values, data any) any { - values = getValuesWithoutParsing(values, data) + values = values.([]any) var v float64 @@ -46,7 +46,7 @@ func _and(values, data any) any { } func _or(values, data any) any { - values = getValuesWithoutParsing(values, data) + values = values.([]any) for _, value := range values.([]any) { parsed := parseValues(value, data) @@ -69,7 +69,7 @@ func evaluateClause(clause any, data any) any { } func conditional(values, data any) any { - values = getValuesWithoutParsing(values, data) + values = values.([]any) clauses := values.([]any)