Skip to content

Commit

Permalink
Merge pull request #5 from codetiger/main
Browse files Browse the repository at this point in the history
Added PreserveOperator and fix for Dot paths
  • Loading branch information
codetiger authored Dec 1, 2024
2 parents d01c209 + eac171b commit f3ee812
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 72 deletions.
69 changes: 23 additions & 46 deletions examples/specific.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,27 @@ use serde_json::json;
fn main() {
let logic = JsonLogic::new();

let rule = json!({
"filter": [
{
"var": "locales"
},
{
"!==": [
{
"var": "code"
},
{
"var": "../../locale"
}
]
}
]
});
let data = json!({
"locale": "pl",
"locales": [
{
"name": "Israel",
"code": "he",
"flag": "🇮🇱",
"iso": "he-IL",
"dir": "rtl"
},
{
"name": "українська",
"code": "ue",
"flag": "🇺🇦",
"iso": "uk-UA",
"dir": "ltr"
},
{
"name": "Polski",
"code": "pl",
"flag": "🇵🇱",
"iso": "pl-PL",
"dir": "ltr"
}
]
});

let result = logic.apply(&rule, &data).unwrap();
println!("Result: {}", result);
// Test both escaped dot and regular dot navigation
let test_cases = vec![
// Test 1: Escaped dot should look up exact key
(
json!({"var": "hello\\.world"}),
json!({"hello": {"world": "i'm here!"}, "hello.world": "ups!"}),
"ups!"
),
// Test 2: Regular dot should navigate nested object
(
json!({"var": "hello.world"}),
json!({"hello": {"world": "i'm here!"}, "hello.world": "ups!"}),
"i'm here!"
)
];

for (rule, data, expected) in test_cases {
let result = logic.apply(&rule, &data).unwrap();
println!("Rule: {}", rule);
println!("Result: {}", result);
println!("Expected: {}", expected);
println!("---");
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod error;
mod operators;

use error::Error;
use operators::preserve::PreserveOperator;
use operators::{
operator::Operator,
var::VarOperator,
Expand Down Expand Up @@ -83,6 +84,7 @@ impl JsonLogic {
self.operators.insert("none".into(), Arc::new(NoneOperator));
self.operators.insert("some".into(), Arc::new(SomeOperator));

self.operators.insert("preserve".into(), Arc::new(PreserveOperator));

}

Expand Down
3 changes: 2 additions & 1 deletion src/operators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ pub mod arithmetic;
pub mod string;
pub mod array;
pub mod missing;
pub mod array_ops;
pub mod array_ops;
pub mod preserve;
20 changes: 20 additions & 0 deletions src/operators/preserve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::operators::operator::Operator;
use crate::{Error, JsonLogic, JsonLogicResult};
use serde_json::Value;

#[derive(Default)]
pub struct PreserveOperator;

impl Operator for PreserveOperator {
fn apply(&self, _logic: &JsonLogic, args: &Value, _data: &Value) -> JsonLogicResult {
match args {
Value::Object(obj) => Ok(Value::Object(obj.clone())),
_ => Err(Error::InvalidArguments("preserve requires object argument".into()))
}
}

// Optionally disable auto traversal since we want to preserve the raw value
fn auto_traverse(&self) -> bool {
false
}
}
47 changes: 25 additions & 22 deletions src/operators/var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,35 @@ impl VarOperator {
return Some(data.clone());
}

// Handle escaped path first
if path.contains("\\.") {
let unescaped_key = path.replace("\\.", ".");
if let Value::Object(map) = data {
return map.get(&unescaped_key).cloned();
}
return None;
}

// Handle dot navigation for unescaped paths
let mut current = data;
for part in path.split('.') {
current = match (current, part.parse::<usize>()) {
(Value::Object(map), _) => map.get(part).unwrap_or(&Value::Null),
(Value::Array(arr), Ok(index)) => arr.get(index).unwrap_or(&Value::Null),
current = match current {
Value::Object(map) => map.get(part).unwrap_or(&Value::Null),
Value::Array(arr) => {
if let Ok(index) = part.parse::<usize>() {
arr.get(index).unwrap_or(&Value::Null)
} else {
return None;
}
},
_ => return None
};

if current == &Value::Null {
return None;
}
}

Some(current.clone())
}

Expand All @@ -42,26 +58,13 @@ impl VarOperator {
}
}

fn handle_string_path(path_str: &str, data: &Value, default: Option<&Value>) -> JsonLogicResult {
if path_str.is_empty() {
return Ok(data.clone());
fn handle_string_path(path: &str, data: &Value, default: Option<&Value>) -> JsonLogicResult {
match Self::get_value_at_path(data, path) {
Some(value) => Ok(value),
None => Self::get_default(default)
}

let mut current = data;
for part in path_str.split('.') {
current = match (current, part.parse::<usize>()) {
(Value::Object(map), _) => map.get(part).unwrap_or(&Value::Null),
(Value::Array(arr), Ok(index)) => arr.get(index).unwrap_or(&Value::Null),
_ => return Self::get_default(default)
};

if current == &Value::Null {
return Self::get_default(default);
}
}

Ok(current.clone())
}

}

impl Operator for VarOperator {
Expand Down
4 changes: 1 addition & 3 deletions tests/alltests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,8 @@ fn test_jsonlogic_all_test_suites() {
let test_sources = vec![
// Remote URLs
"https://jsonlogic.com/tests.json",
// "https://raw.githubusercontent.com/TotalTechGeek/json-logic-engine/refs/heads/master/bench/tests.json",
// "https://raw.githubusercontent.com/TotalTechGeek/json-logic-engine/refs/heads/master/bench/compatible.json",
// Local file
// "tests/custom_tests.json", // Add your local test file path here
"tests/custom_tests.json", // Add your local test file path here
];

let mut overall_passed = 0;
Expand Down
29 changes: 29 additions & 0 deletions tests/custom_tests.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
[
"Custom Tests",
[
{"var": "hello\\.world"},
{"hello": {"world": "i'm here!"}, "hello.world": "ups!"},
"ups!"
],
[
{"var": "hello.world"},
{"hello": {"world": "i'm here!"}, "hello.world": "ups!"},
"i'm here!"
],
[
{
"var": "hello\\.world"
Expand Down Expand Up @@ -57,5 +67,24 @@
]
},
[{"name":"Israel","code":"he","flag":"🇮🇱","iso":"he-IL","dir":"rtl"},{"name":"українська","code":"ue","flag":"🇺🇦","iso":"uk-UA","dir":"ltr"}]
],
[
{
"if": [
{
"and": [
{ "===": [ { "var": "first_name" }, true ] },
{ "===": [ { "var": "last_name" }, true ] }
]
},
{ "preserve": { "first_name": "scott", "last_name": "wyatt" } },
{ "preserve": { "first_name": "no", "last_name": "idea" } }
]
},
{
"first_name": true,
"last_name": true
},
{"first_name":"scott","last_name":"wyatt"}
]
]

0 comments on commit f3ee812

Please sign in to comment.