Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go 1.18 as minimum requirement #95

Merged
merged 1 commit into from
Jan 5, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: ['1.16', '1.18', '1.19', '1.20', '1.21']
go: ['1.18', '1.19', '1.20', '1.21', '1.22', '1.23']
name: Running with Go ${{ matrix.go }}
steps:
- name: Install Go
32 changes: 16 additions & 16 deletions arrays.go
Original file line number Diff line number Diff line change
@@ -12,10 +12,10 @@ func (e ErrReduceDataType) Error() string {
return fmt.Sprintf("The type \"%s\" is not supported", e.dataType)
}

func filter(values, data interface{}) interface{} {
parsed := values.([]interface{})
func filter(values, data any) any {
parsed := values.([]any)

var subject interface{}
var subject any

if isSlice(parsed[0]) {
subject = parsed[0]
@@ -25,15 +25,15 @@ func filter(values, data interface{}) interface{} {
subject = apply(parsed[0], data)
}

result := make([]interface{}, 0)
result := make([]any, 0)

if subject == nil {
return result
}

logic := solveVars(parsed[1], data)

for _, value := range subject.([]interface{}) {
for _, value := range subject.([]any) {
v := parseValues(logic, value)

if isTrue(v) {
@@ -44,10 +44,10 @@ func filter(values, data interface{}) interface{} {
return result
}

func _map(values, data interface{}) interface{} {
parsed := values.([]interface{})
func _map(values, data any) any {
parsed := values.([]any)

var subject interface{}
var subject any

if isSlice(parsed[0]) {
subject = parsed[0]
@@ -57,15 +57,15 @@ func _map(values, data interface{}) interface{} {
subject = apply(parsed[0], data)
}

result := make([]interface{}, 0)
result := make([]any, 0)

if subject == nil {
return result
}

logic := solveVars(parsed[1], data)

for _, value := range subject.([]interface{}) {
for _, value := range subject.([]any) {
v := parseValues(logic, value)

if isTrue(v) || isNumber(v) || isBool(v) {
@@ -76,10 +76,10 @@ func _map(values, data interface{}) interface{} {
return result
}

func reduce(values, data interface{}) interface{} {
parsed := values.([]interface{})
func reduce(values, data any) any {
parsed := values.([]any)

var subject interface{}
var subject any

if isSlice(parsed[0]) {
subject = parsed[0]
@@ -94,7 +94,7 @@ func reduce(values, data interface{}) interface{} {
}

var (
accumulator interface{}
accumulator any
valueType string
)

@@ -120,13 +120,13 @@ func reduce(values, data interface{}) interface{} {
}
}

context := map[string]interface{}{
context := map[string]any{
"current": float64(0),
"accumulator": accumulator,
"valueType": valueType,
}

for _, value := range subject.([]interface{}) {
for _, value := range subject.([]any) {
if value == nil {
continue
}
10 changes: 5 additions & 5 deletions comp.go
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import (
)

// at simulate undefined in javascript
func at(values []interface{}, index int) interface{} {
func at(values []any, index int) any {
if index >= 0 && index < len(values) {
return values[index]
}
@@ -16,7 +16,7 @@ func at(values []interface{}, index int) interface{} {

type undefinedType struct{}

func toNumberForLess(v interface{}) float64 {
func toNumberForLess(v any) float64 {
switch value := v.(type) {
case nil:
return 0
@@ -45,7 +45,7 @@ func toNumberForLess(v interface{}) float64 {

// less reference javascript implementation
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Less_than#description
func less(a, b interface{}) bool {
func less(a, b any) bool {
// If both values are strings, they are compared as strings,
// based on the values of the Unicode code points they contain.
if isString(a) && isString(b) {
@@ -56,7 +56,7 @@ func less(a, b interface{}) bool {
return toNumberForLess(b) > toNumberForLess(a)
}

func hardEquals(a, b interface{}) bool {
func hardEquals(a, b any) bool {
ra := reflect.ValueOf(a).Kind()
rb := reflect.ValueOf(b).Kind()

@@ -67,7 +67,7 @@ func hardEquals(a, b interface{}) bool {
return equals(a, b)
}

func equals(a, b interface{}) bool {
func equals(a, b any) bool {
// comparison to a nil value is falsy
if a == nil || b == nil {
// if a and b is nil, return true, else return falsy
10 changes: 8 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
module github.com/diegoholiveira/jsonlogic/v3

go 1.14
go 1.18

require (
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df
github.com/stretchr/testify v1.6.1
github.com/stretchr/testify v1.10.0
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
13 changes: 6 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0=
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
32 changes: 16 additions & 16 deletions helpers.go
Original file line number Diff line number Diff line change
@@ -5,19 +5,19 @@ import (
"strconv"
)

func is(obj interface{}, kind reflect.Kind) bool {
func is(obj any, kind reflect.Kind) bool {
return obj != nil && reflect.TypeOf(obj).Kind() == kind
}

func isBool(obj interface{}) bool {
func isBool(obj any) bool {
return is(obj, reflect.Bool)
}

func isString(obj interface{}) bool {
func isString(obj any) bool {
return is(obj, reflect.String)
}

func isNumber(obj interface{}) bool {
func isNumber(obj any) bool {
switch obj.(type) {
case int, float64:
return true
@@ -26,19 +26,19 @@ func isNumber(obj interface{}) bool {
}
}

func isPrimitive(obj interface{}) bool {
func isPrimitive(obj any) bool {
return isBool(obj) || isString(obj) || isNumber(obj)
}

func isMap(obj interface{}) bool {
func isMap(obj any) bool {
return is(obj, reflect.Map)
}

func isSlice(obj interface{}) bool {
func isSlice(obj any) bool {
return is(obj, reflect.Slice)
}

func isTrue(obj interface{}) bool {
func isTrue(obj any) bool {
if isBool(obj) {
return obj.(bool)
}
@@ -56,8 +56,8 @@ func isTrue(obj interface{}) bool {
return false
}

func toSliceOfNumbers(values interface{}) []float64 {
_values := values.([]interface{})
func toSliceOfNumbers(values any) []float64 {
_values := values.([]any)

numbers := make([]float64, len(_values))
for i, n := range _values {
@@ -66,26 +66,26 @@ func toSliceOfNumbers(values interface{}) []float64 {
return numbers
}

func toNumber(value interface{}) float64 {
func toNumber(value any) float64 {
if isString(value) {
w, _ := strconv.ParseFloat(value.(string), 64)

return w
}

switch value.(type) {
switch value := value.(type) {
case int:
return float64(value.(int))
return float64(value)
default:
return value.(float64)
}
}

func toString(value interface{}) string {
func toString(value any) string {
if isNumber(value) {
switch value.(type) {
switch value := value.(type) {
case int:
return strconv.FormatInt(int64(value.(int)), 10)
return strconv.FormatInt(int64(value), 10)
default:
return strconv.FormatFloat(value.(float64), 'f', -1, 64)
}
18 changes: 9 additions & 9 deletions internal/testing.go
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ type (
Tests []Test
)

func convertInterfaceToReader(i interface{}) io.Reader {
func convertInterfaceToReader(i any) io.Reader {
var result bytes.Buffer

encoder := json.NewEncoder(&result)
@@ -45,7 +45,7 @@ func GetScenariosFromOfficialTestSuite() Tests {

response.Body.Close()

var scenarios []interface{}
var scenarios []any

err = json.Unmarshal(buffer, &scenarios)
if err != nil {
@@ -55,23 +55,23 @@ func GetScenariosFromOfficialTestSuite() Tests {
}

// add missing but relevant scenarios
var rule []interface{}
var rule []any

scenarios = append(scenarios,
append(rule,
make(map[string]interface{}),
make(map[string]interface{}),
make(map[string]interface{})))
make(map[string]any),
make(map[string]any),
make(map[string]any)))

for _, scenario := range scenarios {
if reflect.ValueOf(scenario).Kind() == reflect.String {
continue
}

tests = append(tests, Test{
Rule: convertInterfaceToReader(scenario.([]interface{})[0]),
Data: convertInterfaceToReader(scenario.([]interface{})[1]),
Expected: convertInterfaceToReader(scenario.([]interface{})[2]),
Rule: convertInterfaceToReader(scenario.([]any)[0]),
Data: convertInterfaceToReader(scenario.([]any)[1]),
Expected: convertInterfaceToReader(scenario.([]any)[2]),
})
}

130 changes: 65 additions & 65 deletions jsonlogic.go
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ func (e ErrInvalidOperator) Error() string {
return fmt.Sprintf("The operator \"%s\" is not supported", e.operator)
}

func between(operator string, values []interface{}, data interface{}) interface{} {
func between(operator string, values []any, data any) any {
a := parseValues(values[0], data)
b := parseValues(values[1], data)
c := parseValues(values[2], data)
@@ -37,7 +37,7 @@ func between(operator string, values []interface{}, data interface{}) interface{
return less(c, b) && less(b, a)
}

func unary(operator string, value interface{}) interface{} {
func unary(operator string, value any) any {
if operator == "+" || operator == "*" || operator == "/" {
return toNumber(value)
}
@@ -63,13 +63,13 @@ func unary(operator string, value interface{}) interface{} {
return b
}

func _and(values, data interface{}) interface{} {
func _and(values, data any) any {
values = getValuesWithoutParsing(values, data)
var v float64

isBoolExpression := true

for _, value := range values.([]interface{}) {
for _, value := range values.([]any) {
value = parseValues(value, data)
if isSlice(value) {
return value
@@ -103,10 +103,10 @@ func _and(values, data interface{}) interface{} {
return v
}

func _or(values, data interface{}) interface{} {
func _or(values, data any) any {
values = getValuesWithoutParsing(values, data)

for _, value := range values.([]interface{}) {
for _, value := range values.([]any) {
if isTrue(parseValues(value, data)) {
return value
}
@@ -115,8 +115,8 @@ func _or(values, data interface{}) interface{} {
return false
}

func _inRange(value interface{}, values interface{}) bool {
v := values.([]interface{})
func _inRange(value any, values any) bool {
v := values.([]any)

i := v[0]
j := v[1]
@@ -128,7 +128,7 @@ func _inRange(value interface{}, values interface{}) bool {
return toString(value) >= toString(i) && toString(j) >= toString(value)
}

func _in(value interface{}, values interface{}) bool {
func _in(value any, values any) bool {
if value == nil || values == nil {
return false
}
@@ -141,7 +141,7 @@ func _in(value interface{}, values interface{}) bool {
return false
}

for _, element := range values.([]interface{}) {
for _, element := range values.([]any) {
if isSlice(element) {
if _inRange(value, element) {
return true
@@ -166,8 +166,8 @@ func _in(value interface{}, values interface{}) bool {
return false
}

func max(values interface{}) interface{} {
converted := values.([]interface{})
func max(values any) any {
converted := values.([]any)
size := len(converted)
if size == 0 {
return nil
@@ -185,8 +185,8 @@ func max(values interface{}) interface{} {
return bigger
}

func min(values interface{}) interface{} {
converted := values.([]interface{})
func min(values any) any {
converted := values.([]any)
size := len(converted)
if size == 0 {
return nil
@@ -204,16 +204,16 @@ func min(values interface{}) interface{} {
return smallest
}

func merge(values interface{}, level int8) interface{} {
result := make([]interface{}, 0)
func merge(values any, level int8) any {
result := make([]any, 0)

if isPrimitive(values) || level > 1 {
return append(result, values)
}

if isSlice(values) {
for _, value := range values.([]interface{}) {
_values := merge(value, level+1).([]interface{})
for _, value := range values.([]any) {
_values := merge(value, level+1).([]any)

result = append(result, _values...)
}
@@ -222,12 +222,12 @@ func merge(values interface{}, level int8) interface{} {
return result
}

func conditional(values, data interface{}) interface{} {
func conditional(values, data any) any {
if isPrimitive(values) {
return values
}

parsed := values.([]interface{})
parsed := values.([]any)

length := len(parsed)

@@ -253,8 +253,8 @@ func conditional(values, data interface{}) interface{} {
return nil
}

func setProperty(value, data interface{}) interface{} {
_value := value.([]interface{})
func setProperty(value, data any) any {
_value := value.([]any)

object := _value[0]

@@ -268,20 +268,20 @@ func setProperty(value, data interface{}) interface{} {
panic(err)
}

_modified := modified.(map[string]interface{})
_modified := modified.(map[string]any)
_modified[property] = parseValues(_value[2], data)

return interface{}(_modified)
return any(_modified)
}

func missing(values, data interface{}) interface{} {
func missing(values, data any) any {
if isString(values) {
values = []interface{}{values}
values = []any{values}
}

missing := make([]interface{}, 0)
missing := make([]any, 0)

for _, _var := range values.([]interface{}) {
for _, _var := range values.([]any) {
_value := getVar(_var, data)

if _value == nil {
@@ -292,15 +292,15 @@ func missing(values, data interface{}) interface{} {
return missing
}

func missingSome(values, data interface{}) interface{} {
parsed := values.([]interface{})
func missingSome(values, data any) any {
parsed := values.([]any)
number := int(toNumber(parsed[0]))
vars := parsed[1]

missing := make([]interface{}, 0)
found := make([]interface{}, 0)
missing := make([]any, 0)
found := make([]any, 0)

for _, _var := range vars.([]interface{}) {
for _, _var := range vars.([]any) {
_value := getVar(_var, data)

if _value == nil {
@@ -314,13 +314,13 @@ func missingSome(values, data interface{}) interface{} {
return missing
}

return make([]interface{}, 0)
return make([]any, 0)
}

func all(values, data interface{}) interface{} {
parsed := values.([]interface{})
func all(values, data any) any {
parsed := values.([]any)

var subject interface{}
var subject any

if isMap(parsed[0]) {
subject = apply(parsed[0], data)
@@ -334,7 +334,7 @@ func all(values, data interface{}) interface{} {
return false
}

for _, value := range subject.([]interface{}) {
for _, value := range subject.([]any) {
conditions := solveVars(parsed[1], value)
v := apply(conditions, value)

@@ -346,10 +346,10 @@ func all(values, data interface{}) interface{} {
return true
}

func none(values, data interface{}) interface{} {
parsed := values.([]interface{})
func none(values, data any) any {
parsed := values.([]any)

var subject interface{}
var subject any

if isMap(parsed[0]) {
subject = apply(parsed[0], data)
@@ -365,7 +365,7 @@ func none(values, data interface{}) interface{} {

conditions := solveVars(parsed[1], data)

for _, value := range subject.([]interface{}) {
for _, value := range subject.([]any) {
v := apply(conditions, value)

if isTrue(v) {
@@ -376,10 +376,10 @@ func none(values, data interface{}) interface{} {
return true
}

func some(values, data interface{}) interface{} {
parsed := values.([]interface{})
func some(values, data any) any {
parsed := values.([]any)

var subject interface{}
var subject any

if isMap(parsed[0]) {
subject = apply(parsed[0], data)
@@ -393,7 +393,7 @@ func some(values, data interface{}) interface{} {
return false
}

for _, value := range subject.([]interface{}) {
for _, value := range subject.([]any) {
v := apply(
solveVars(
solveVars(parsed[1], data),
@@ -410,7 +410,7 @@ func some(values, data interface{}) interface{} {
return false
}

func parseValues(values, data interface{}) interface{} {
func parseValues(values, data any) any {
if values == nil || isPrimitive(values) {
return values
}
@@ -419,9 +419,9 @@ func parseValues(values, data interface{}) interface{} {
return apply(values, data)
}

parsed := make([]interface{}, 0)
parsed := make([]any, 0)

for _, value := range values.([]interface{}) {
for _, value := range values.([]any) {
if isMap(value) {
parsed = append(parsed, apply(value, data))
} else {
@@ -436,7 +436,7 @@ func parseValues(values, data interface{}) interface{} {
// values without parsing. This means that each of the returned values might be a subtree
// of JSONLogic.
// Used in lazy evaluation of "AND" and "OR" operators
func getValuesWithoutParsing(values, data interface{}) interface{} {
func getValuesWithoutParsing(values, data any) any {
if values == nil || isPrimitive(values) {
return values
}
@@ -445,11 +445,11 @@ func getValuesWithoutParsing(values, data interface{}) interface{} {
return apply(values, data)
}

return values.([]interface{})
return values.([]any)
}

func apply(rules, data interface{}) interface{} {
ruleMap := rules.(map[string]interface{})
func apply(rules, data any) any {
ruleMap := rules.(map[string]any)

// A map with more than 1 key counts as a primitive
// end recursion
@@ -486,7 +486,7 @@ func apply(rules, data interface{}) interface{} {
}

// an empty-map rule should return an empty-map
return make(map[string]interface{})
return make(map[string]any)
}

// Apply read the rule and it's data from io.Reader, executes it
@@ -496,8 +496,8 @@ func Apply(rule, data io.Reader, result io.Writer) error {
data = strings.NewReader("{}")
}

var _rule interface{}
var _data interface{}
var _rule any
var _data any

decoder := json.NewDecoder(rule)
err := decoder.Decode(&_rule)
@@ -525,8 +525,8 @@ func GetJsonLogicWithSolvedVars(rule, data json.RawMessage) ([]byte, error) {
}

// parse rule and data from json.RawMessage to interface
var _rule interface{}
var _data interface{}
var _rule any
var _data any

err := json.Unmarshal(rule, &_rule)
if err != nil {
@@ -548,8 +548,8 @@ func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) {
data = json.RawMessage("{}")
}

var _rule interface{}
var _data interface{}
var _rule any
var _data any

err := json.Unmarshal(rule, &_rule)
if err != nil {
@@ -569,9 +569,9 @@ func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) {
return json.Marshal(&result)
}

// ApplyInterface receives a rule and data as interface{} and returns the result
// ApplyInterface receives a rule and data as any and returns the result
// of the rule applied to the data.
func ApplyInterface(rule, data interface{}) (output interface{}, err error) {
func ApplyInterface(rule, data any) (output any, err error) {
defer func() {
if e := recover(); e != nil {
// fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
@@ -584,13 +584,13 @@ func ApplyInterface(rule, data interface{}) (output interface{}, err error) {
}

if isSlice(rule) {
var parsed []interface{}
var parsed []any

for _, value := range rule.([]interface{}) {
for _, value := range rule.([]any) {
parsed = append(parsed, parseValues(value, data))
}

return interface{}(parsed), nil
return any(parsed), nil
}

return rule, err
16 changes: 8 additions & 8 deletions jsonlogic_test.go
Original file line number Diff line number Diff line change
@@ -381,8 +381,8 @@ func TestDataWithDefaultValueWithApplyRaw(t *testing.T) {
}

func TestDataWithDefaultValueWithApplyInterface(t *testing.T) {
rule := map[string]interface{}{
"+": []interface{}{
rule := map[string]any{
"+": []any{
float64(1),
float64(2),
},
@@ -398,8 +398,8 @@ func TestDataWithDefaultValueWithApplyInterface(t *testing.T) {
}

func TestMissingOperators(t *testing.T) {
rule := map[string]interface{}{
"sum": []interface{}{
rule := map[string]any{
"sum": []any{
float64(1),
float64(2),
},
@@ -571,18 +571,18 @@ func TestReduceFilterAndNotContains(t *testing.T) {
func TestReduceWithUnsupportedValue(t *testing.T) {
b := []byte(`{"reduce":[{"filter":[{"var":"data"},{"==":[{"var":""},""]}]},{"cat":[{"var":"current"},{"var":"accumulator"}]},null]}`)

rule := map[string]interface{}{}
rule := map[string]any{}
_ = json.Unmarshal(b, &rule)
data := map[string]interface{}{
"data": []interface{}{"str"},
data := map[string]any{
"data": []any{"str"},
}

_, err := jsonlogic.ApplyInterface(rule, data)
assert.EqualError(t, err, "The type \"<nil>\" is not supported")
}

func TestAddOperator(t *testing.T) {
jsonlogic.AddOperator("strlen", func(values, data interface{}) interface{} {
jsonlogic.AddOperator("strlen", func(values, data any) any {
v, ok := values.(string)

if ok {
16 changes: 8 additions & 8 deletions math.go
Original file line number Diff line number Diff line change
@@ -2,30 +2,30 @@ package jsonlogic

import "math"

func mod(a interface{}, b interface{}) interface{} {
func mod(a any, b any) any {
_a := toNumber(a)
_b := toNumber(b)

return math.Mod(_a, _b)
}

func abs(a interface{}) interface{} {
func abs(a any) any {
_a := toNumber(a)

return math.Abs(_a)
}

func sum(values interface{}) interface{} {
func sum(values any) any {
sum := float64(0)

for _, n := range values.([]interface{}) {
for _, n := range values.([]any) {
sum += toNumber(n)
}

return sum
}

func minus(values interface{}) interface{} {
func minus(values any) any {
_values := toSliceOfNumbers(values)

sum := _values[0]
@@ -36,17 +36,17 @@ func minus(values interface{}) interface{} {
return sum
}

func mult(values interface{}) interface{} {
func mult(values any) any {
sum := float64(1)

for _, n := range values.([]interface{}) {
for _, n := range values.([]any) {
sum *= toNumber(n)
}

return sum
}

func div(values interface{}) interface{} {
func div(values any) any {
_values := toSliceOfNumbers(values)

sum := _values[0]
8 changes: 4 additions & 4 deletions operation.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package jsonlogic

// customOperators holds custom operators
var customOperators = make(map[string]func(values, data interface{}) (result interface{}))
var customOperators = make(map[string]func(values, data any) (result any))

// AddOperator allows for custom operators to be used
func AddOperator(key string, cb func(values, data interface{}) (result interface{})) {
func AddOperator(key string, cb func(values, data any) (result any)) {
customOperators[key] = cb
}

func operation(operator string, values, data interface{}) interface{} {
func operation(operator string, values, data any) any {
// "AND" evaluates values lazily, so parseValues() is delayed until needed
if operator == "and" {
return _and(values, data)
@@ -77,7 +77,7 @@ func operation(operator string, values, data interface{}) interface{} {
return nil
}

parsed := values.([]interface{})
parsed := values.([]any)

if operator != "in" && len(parsed) == 1 {
return unary(operator, parsed[0])
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
@@ -119,7 +119,7 @@ import (

func main() {
// add a new operator "strlen" for get string length
jsonlogic.AddOperator("strlen", func(values, data interface{}) interface{} {
jsonlogic.AddOperator("strlen", func(values, data any) any {
v, ok := values.(string)
if ok {
return len(v)
8 changes: 4 additions & 4 deletions strings.go
Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@ import (
"strings"
)

func substr(values interface{}) interface{} {
parsed := values.([]interface{})
func substr(values any) any {
parsed := values.([]any)

runes := []rune(toString(parsed[0]))

@@ -41,13 +41,13 @@ func substr(values interface{}) interface{} {
return string(runes[from:to])
}

func concat(values interface{}) interface{} {
func concat(values any) any {
if isString(values) {
return values
}

var s bytes.Buffer
for _, text := range values.([]interface{}) {
for _, text := range values.([]any) {
s.WriteString(toString(text))
}

12 changes: 6 additions & 6 deletions validator.go
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import (

// IsValid reads a JSON Logic rule from io.Reader and validates it
func IsValid(rule io.Reader) bool {
var _rule interface{}
var _rule any

decoderRule := json.NewDecoder(rule)
err := decoderRule.Decode(&_rule)
@@ -18,13 +18,13 @@ func IsValid(rule io.Reader) bool {
return ValidateJsonLogic(_rule)
}

func ValidateJsonLogic(rules interface{}) bool {
func ValidateJsonLogic(rules any) bool {
if isVar(rules) {
return true
}

if isMap(rules) {
for operator, value := range rules.(map[string]interface{}) {
for operator, value := range rules.(map[string]any) {
if !isOperator(operator) {
return false
}
@@ -34,7 +34,7 @@ func ValidateJsonLogic(rules interface{}) bool {
}

if isSlice(rules) {
for _, value := range rules.([]interface{}) {
for _, value := range rules.([]any) {
if isSlice(value) || isMap(value) {
if ValidateJsonLogic(value) {
continue
@@ -107,12 +107,12 @@ func isOperator(op string) bool {
return false
}

func isVar(value interface{}) bool {
func isVar(value any) bool {
if !isMap(value) {
return false
}

_var, ok := value.(map[string]interface{})["var"]
_var, ok := value.(map[string]any)["var"]
if !ok {
return false
}
30 changes: 15 additions & 15 deletions vars.go
Original file line number Diff line number Diff line change
@@ -6,11 +6,11 @@ import (
"strings"
)

func solveVars(values, data interface{}) interface{} {
func solveVars(values, data any) any {
if isMap(values) {
logic := map[string]interface{}{}
logic := map[string]any{}

for key, value := range values.(map[string]interface{}) {
for key, value := range values.(map[string]any) {
if key == "var" {
if isString(value) && (value == "" || strings.HasPrefix(value.(string), ".")) {
logic["var"] = value
@@ -28,13 +28,13 @@ func solveVars(values, data interface{}) interface{} {
}
}

return interface{}(logic)
return any(logic)
}

if isSlice(values) {
logic := []interface{}{}
logic := []any{}

for _, value := range values.([]interface{}) {
for _, value := range values.([]any) {
logic = append(logic, solveVars(value, data))
}

@@ -44,7 +44,7 @@ func solveVars(values, data interface{}) interface{} {
return values
}

func getVar(value, data interface{}) interface{} {
func getVar(value, data any) any {
if value == nil {
if !isPrimitive(data) {
return nil
@@ -60,10 +60,10 @@ func getVar(value, data interface{}) interface{} {
value = toString(value)
}

var _default interface{}
var _default any

if isSlice(value) { // syntax sugar
v := value.([]interface{})
v := value.([]any)

if len(v) == 0 {
return data
@@ -82,20 +82,20 @@ func getVar(value, data interface{}) interface{} {

parts := strings.Split(value.(string), ".")

var _value interface{}
var _value any

for _, part := range parts {
if part == "" {
continue
}

if isMap(data) {
_value = data.(map[string]interface{})[part]
_value = data.(map[string]any)[part]
}

if isSlice(data) {
pos := int(toNumber(part))
container := data.([]interface{})
container := data.([]any)
if pos >= len(container) {
return _default
}
@@ -116,9 +116,9 @@ func getVar(value, data interface{}) interface{} {
return _value
}

func solveVarsBackToJsonLogic(rule, data interface{}) (json.RawMessage, error) {
ruleMap := rule.(map[string]interface{})
result := make(map[string]interface{})
func solveVarsBackToJsonLogic(rule, data any) (json.RawMessage, error) {
ruleMap := rule.(map[string]any)
result := make(map[string]any)

for operator, values := range ruleMap {
result[operator] = solveVars(values, data)