Skip to content
Merged
Show file tree
Hide file tree
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 comp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
)

func TestHardEqualsWithNonSliceValues(t *testing.T) {
Expand Down
14 changes: 10 additions & 4 deletions internal/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ type (
Tests []Test
)

// This gets the tests.json file that we've proposed become the new official one in
// GetScenariosFromProposedOfficialTestSuite reads the tests.json file that we've proposed become the new official one in
// https://github.com/jwadhams/json-logic/pull/48 but that hasn't merged yet.
func GetScenariosFromProposedOfficialTestSuite() Tests {
var err error
buffer, err := os.ReadFile("internal/json_logic_pr_48_tests.json")
if err != nil {
log.Fatal(err)
Expand All @@ -33,18 +32,25 @@ func GetScenariosFromProposedOfficialTestSuite() Tests {
return getScenariosFromFile(buffer)
}

// GetScenariosFromOfficialTestSuite fetches test scenarios from the official JSON Logic test suite.
// It makes an HTTP request to jsonlogic.com to retrieve the latest test cases.
func GetScenariosFromOfficialTestSuite() Tests {
response, err := http.Get("http://jsonlogic.com/tests.json")
req, err := http.NewRequest("GET", "http://jsonlogic.com/tests.json", nil)
if err != nil {
log.Fatal(err)
}

response, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()

buffer, err := io.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}

response.Body.Close()
return getScenariosFromFile(buffer)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/typing/typing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,4 @@ func TestToString(t *testing.T) {
assert.Equal(t, tt.expected, result)
})
}
}
}
6 changes: 2 additions & 4 deletions issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
)

func TestIssue50(t *testing.T) {
Expand Down Expand Up @@ -198,7 +198,6 @@ func TestJsonLogicWithSolvedVars(t *testing.T) {
data := json.RawMessage(`{"foo": 34359738368, "bar": 10, "is_foo": false, "is_bar": true}`)

output, err := jsonlogic.GetJsonLogicWithSolvedVars(rule, data)

if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -255,7 +254,6 @@ func TestIssue79(t *testing.T) {
var result bytes.Buffer

err := jsonlogic.Apply(rule, data, &result)

if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -385,7 +383,7 @@ func TestIssue125_CustomOperatorWithVarsInSlice(t *testing.T) {
parsed := values.([]any)
needle := parsed[0]
haystack := parsed[1].([]any)

for _, item := range haystack {
if item == needle {
return true
Expand Down
34 changes: 34 additions & 0 deletions jsonlogic.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
// Package jsonlogic provides a Go implementation of JSONLogic rules engine.
// JSONLogic is a way to write rules that involve logic (boolean and mathematical operations),
// consistently in JSON. It's designed to be a lightweight, portable way to share logic
// between front-end and back-end systems.
//
// The package supports all standard JSONLogic operators and allows for custom operator registration.
// Rules can be applied to data using various input/output formats including io.Reader/Writer,
// json.RawMessage, and native Go interfaces.
//
// Basic usage:
//
// rule := strings.NewReader(`{"==":[{"var":"name"}, "John"]}`)
// data := strings.NewReader(`{"name":"John"}`)
// var result strings.Builder
//
// err := jsonlogic.Apply(rule, data, &result)
// if err != nil {
// log.Fatal(err)
// }
// // result.String() will be "true"
//
// For more examples and documentation, see: https://jsonlogic.com
package jsonlogic

import (
Expand Down Expand Up @@ -119,6 +141,18 @@ func ApplyInterface(rule, data any) (output any, err error) {
return rule, err
}

// GetJsonLogicWithSolvedVars processes a JSON Logic rule by resolving variables with actual data values.
// It returns the rule with variables substituted but maintains the JSON Logic structure.
//
// Parameters:
// - rule: json.RawMessage containing the JSON Logic rule
// - data: json.RawMessage containing the data context for variable resolution
//
// Returns:
// - []byte: the processed rule with resolved variables as JSON bytes
// - error: error if unmarshaling or processing fails
//
// This is useful for debugging or when you need to see the rule with variables resolved.
func GetJsonLogicWithSolvedVars(rule, data json.RawMessage) ([]byte, error) {
if data == nil {
data = json.RawMessage("{}")
Expand Down
2 changes: 1 addition & 1 deletion jsonlogic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
"github.com/diegoholiveira/jsonlogic/v3/internal"
)

Expand Down
2 changes: 2 additions & 0 deletions lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/diegoholiveira/jsonlogic/v3/internal/typing"
)

// ErrReduceDataType represents an error when an unsupported data type is used in reduce operations.
// It contains the data type name that caused the error.
type ErrReduceDataType struct {
dataType string
}
Expand Down
2 changes: 1 addition & 1 deletion lists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
)

func TestFilterParseTheSubjectFromFirstPosition(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
)

func TestSubOperation(t *testing.T) {
Expand Down
14 changes: 13 additions & 1 deletion operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import (
"github.com/diegoholiveira/jsonlogic/v3/internal/typing"
)

// OperatorFn defines the signature for custom operator functions.
// It takes values and data as input and returns a result.
type OperatorFn func(values, data any) (result any)

// ErrInvalidOperator represents an error when an unsupported operator is used.
// It contains the operator name that caused the error.
type ErrInvalidOperator struct {
operator string
}
Expand All @@ -19,9 +23,17 @@ func (e ErrInvalidOperator) Error() string {

// operators holds custom operators
var operators = make(map[string]OperatorFn)

var operatorsLock = &sync.RWMutex{}

// AddOperator allows for custom operators to be used
// AddOperator registers a custom operator with the given key and function.
// The operator function will be called with parsed values and the original data context.
//
// Parameters:
// - key: the operator name to register (e.g., "custom_op")
// - cb: the function to execute when the operator is encountered
//
// Concurrency: This function is safe for concurrent use as it properly locks the operators map.
func AddOperator(key string, cb OperatorFn) {
operatorsLock.Lock()
defer operatorsLock.Unlock()
Expand Down
2 changes: 1 addition & 1 deletion strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
)

func TestCat(t *testing.T) {
Expand Down
21 changes: 20 additions & 1 deletion validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ import (
"github.com/diegoholiveira/jsonlogic/v3/internal/typing"
)

// IsValid reads a JSON Logic rule from io.Reader and validates it
// IsValid reads a JSON Logic rule from io.Reader and validates its syntax.
// It checks if the rule conforms to valid JSON Logic format and uses supported operators.
//
// Parameters:
// - rule: io.Reader containing the JSON Logic rule to validate
//
// Returns:
// - bool: true if the rule is valid, false otherwise
//
// The function returns false if the JSON cannot be parsed or if the rule contains invalid operators.
func IsValid(rule io.Reader) bool {
var _rule any

Expand All @@ -20,6 +29,16 @@ func IsValid(rule io.Reader) bool {
return ValidateJsonLogic(_rule)
}

// ValidateJsonLogic validates if the given rules conform to JSON Logic format.
// It recursively checks the structure and ensures all operators are supported.
//
// Parameters:
// - rules: any value representing the JSON Logic rule to validate
//
// Returns:
// - bool: true if the rules are valid JSON Logic, false otherwise
//
// The function handles primitives, maps (operators), slices (arrays), and variable references.
func ValidateJsonLogic(rules any) bool {
if isVar(rules) {
return true
Expand Down
2 changes: 1 addition & 1 deletion validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
)

func TestJSONLogicValidator(t *testing.T) {
Expand Down
3 changes: 1 addition & 2 deletions vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"strconv"
"strings"

"github.com/barkimedes/go-deepcopy"
deepcopy "github.com/barkimedes/go-deepcopy"

"github.com/diegoholiveira/jsonlogic/v3/internal/typing"
)
Expand Down Expand Up @@ -129,7 +129,6 @@ func solveVarsBackToJsonLogic(rule, data any) (json.RawMessage, error) {
// we need to use Unquote due to unicode characters (example \u003e= need to be >=)
// used for prettier json.RawMessage
resultEscaped, err := strconv.Unquote(strings.Replace(strconv.Quote(string(resultJson)), `\\u`, `\u`, -1))

if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion vars_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/diegoholiveira/jsonlogic/v3"
jsonlogic "github.com/diegoholiveira/jsonlogic/v3"
)

func TestSetProperty(t *testing.T) {
Expand Down