Repository: diegoholiveira/jsonlogic (go)

Issue:
get the jsonlogic evaluated back from the method that apply
Hey @diegoholiveira,

I'm using this library to apply a jsonlogic built from my UI (using https://react-querybuilder.js.org), but for my use case, I need to help users to "debug" if the response is false

To achieve this, my idea is to print to the user, the logic applied, but I want the vars replaced directly. To be clear, instead of

```json
{
  "and":[
    { "==":[{ "var":"VariableA" }, true] },
    { "==":[{ "var":"VariableB" }, true] },
    { ">=":[{ "var":"VariableC" }, 17179869184] },
    { "==":[{ "var":"VariableD" }, "0"] },
    { "<":[{ "var":"VariableE" }, 20] }
  ]
}
```

I need

```json
{
  "and":[
    { "==":[false, true] },
    { "==":[true, true] },
    { ">=":[34359738368, 17179869184] },
    { "==":[12, "0"] },
    { "<":[14, 20] }
  ]
}
```

I haven't seen any function that can achieve this (only `solveVars` and `getVar`)
There is any way to get this result ?

Kindly

Pull request description:
feat: add function to get jsonlogic with solved vars (replaced by values)
Close #65 

I added one function in `vars.go` (not exported)

```go
func solveVarsBackToJsonLogic(rule, data interface{}) (json.RawMessage, error) {
	ruleMap := rule.(map[string]interface{})
	result := make(map[string]interface{})

	for operator, values := range ruleMap {
		result[operator] = solveVars(values, data)
	}

	resultJson, err := json.Marshal(result)

	if err != nil {
		return nil, err
	}

	// 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
	}

	return []byte(resultEscaped), nil
}
```

And one exported function which uses the one above

```go
func GetJsonLogicWithSolvedVars(rule, data json.RawMessage) ([]byte, error) {
	if data == nil {
		data = json.RawMessage("{}")
	}

	// parse rule and data from json.RawMessage to interface
	var _rule interface{}
	var _data interface{}

	err := json.Unmarshal(rule, &_rule)
	if err != nil {
		return nil, err
	}

	err = json.Unmarshal(data, &_data)
	if err != nil {
		return nil, err
	}

	return solveVarsBackToJsonLogic(_rule, _data)
}
```

The `README.md` has also been updated to add an example of this new function and a test has been added in `jsonlogic_test.go`

Patch:
diff --git a/.gitignore b/.gitignore
index c61e685..bc16316 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,4 +11,5 @@
 # Output of the go coverage tool, specifically when used with LiteIDE
 *.out
 
-.idea/
\ No newline at end of file
+.idea/
+.vscode/
\ No newline at end of file
diff --git a/jsonlogic.go b/jsonlogic.go
index 734ce79..bd5c676 100644
--- a/jsonlogic.go
+++ b/jsonlogic.go
@@ -515,6 +515,28 @@ func Apply(rule, data io.Reader, result io.Writer) error {
 	return json.NewEncoder(result).Encode(output)
 }
 
+func GetJsonLogicWithSolvedVars(rule, data json.RawMessage) ([]byte, error) {
+	if data == nil {
+		data = json.RawMessage("{}")
+	}
+
+	// parse rule and data from json.RawMessage to interface
+	var _rule interface{}
+	var _data interface{}
+
+	err := json.Unmarshal(rule, &_rule)
+	if err != nil {
+		return nil, err
+	}
+
+	err = json.Unmarshal(data, &_data)
+	if err != nil {
+		return nil, err
+	}
+
+	return solveVarsBackToJsonLogic(_rule, _data)
+}
+
 func ApplyRaw(rule, data json.RawMessage) (json.RawMessage, error) {
 	if data == nil {
 		data = json.RawMessage("{}")
diff --git a/readme.md b/readme.md
index c752bcc..8e4547b 100644
--- a/readme.md
+++ b/readme.md
@@ -138,6 +138,66 @@ func main() {
 }
 ```
 
+If you want to get the json logic used, with the variables replaced by their values : 
+
+```go
+package main
+
+import (
+	"fmt"
+	"encoding/json"
+
+	"github.com/diegoholiveira/jsonlogic/v3"
+)
+
+func main() {
+	logic := json.RawMessage(`{ "==":[{ "var":"foo" }, true] }`)
+	data := json.RawMessage(`{"foo": "false"}`)
+
+	result, err := jsonlogic.GetJsonLogicWithSolvedVars(logic, data)
+
+  if err != nil {
+    fmt.Println(err)
+  }
+  
+	fmt.Println(string(result)) // will output { "==":[false, true] }
+}
+
+```
+
 # License
 
 This project is licensed under the MIT License - see the LICENSE file for details
+
+
+
+For example, if you specify the folowing rules model :
+
+
+```json
+{
+  "and":[
+    { "==":[{ "var":"VariableA" }, true] },
+    { "==":[{ "var":"VariableB" }, true] },
+    { ">=":[{ "var":"VariableC" }, 17179869184] },
+    { "==":[{ "var":"VariableD" }, "0"] },
+    { "<":[{ "var":"VariableE" }, 20] }
+  ]
+}
+
+```
+
+You will get as output, the folowing response (using a specific data, all variables will be replaced with matching values) :
+
+```json
+{
+  "and":[
+    { "==":[false, true] },
+    { "==":[true, true] },
+    { ">=":[34359738368, 17179869184] },
+    { "==":[12, "0"] },
+    { "<":[14, 20] }
+  ]
+}
+
+```
\ No newline at end of file
diff --git a/vars.go b/vars.go
index 31fc2ac..46294d5 100644
--- a/vars.go
+++ b/vars.go
@@ -1,6 +1,8 @@
 package jsonlogic
 
 import (
+	"encoding/json"
+	"strconv"
 	"strings"
 )
 
@@ -105,3 +107,28 @@ 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{})
+
+	for operator, values := range ruleMap {
+		result[operator] = solveVars(values, data)
+	}
+
+	resultJson, err := json.Marshal(result)
+
+	if err != nil {
+		return nil, err
+	}
+
+	// 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
+	}
+
+	return []byte(resultEscaped), nil
+}


Test patch:
diff --git a/jsonlogic_test.go b/jsonlogic_test.go
index 709620d..1747f23 100644
--- a/jsonlogic_test.go
+++ b/jsonlogic_test.go
@@ -740,3 +740,55 @@ func TestIssue58_example(t *testing.T) {
 	expected := `{"foo":"is_bar","path":"foo_is_bar"}`
 	assert.JSONEq(t, expected, result.String())
 }
+
+func TestJsonLogicWithSolvedVars(t *testing.T) {
+	rule := json.RawMessage(`{
+		"or":[
+		{
+			"and":[
+				{"==": [{ "var":"is_foo" }, true ]},
+				{"==": [{ "var":"is_bar" }, true ]},
+				{">=": [{ "var":"foo" }, 17179869184 ]},
+				{"==": [{ "var":"bar" }, 0 ]}
+			]
+      	},
+      	{
+			"and":[
+				{"==": [{ "var":"is_bar" }, true ]},
+				{"==": [{ "var":"is_foo" }, false ]},
+				{"==": [{ "var":"foo" }, 34359738368 ]},
+				{"==": [{ "var":"bar" }, 0 ]}
+			]
+      	}]
+    }`)
+
+	data := json.RawMessage(`{"foo": 34359738368, "bar": 10, "is_foo": false, "is_bar": true}`)
+
+	output, err := GetJsonLogicWithSolvedVars(rule, data)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expected := `{
+		"or":[
+		{
+			"and":[
+				{ "==":[ false, true ] },
+				{ "==":[ true, true ] },
+				{ ">=":[ 34359738368, 17179869184 ] },
+				{ "==":[ 10, 0 ] }
+			]
+		},
+		{
+			"and":[
+				{ "==":[ true, true ] },
+				{ "==":[ false, false ] },
+				{ "==":[ 34359738368, 34359738368 ] },
+				{ "==":[ 10, 0 ] }
+			]
+		}]
+	}`
+
+	assert.JSONEq(t, expected, string(output))
+}