diff --git a/Cargo.toml b/Cargo.toml index 67d37cf2..92ee172b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ bitflags = "2.5.0" bumpalo = { version = "3.16.0", features = ["collections", "boxed"] } dtoa = "1.0.9" base64 = "0.22.1" +serde_json = "1.0.117" [dev-dependencies] test-case = "3.3.1" diff --git a/src/bin/jsonata.rs b/src/bin/jsonata.rs index 397921d3..50076637 100644 --- a/src/bin/jsonata.rs +++ b/src/bin/jsonata.rs @@ -55,7 +55,7 @@ fn main() { None => opt.input.unwrap_or_else(|| "{}".to_string()), }; - match jsonata.evaluate(Some(&input)) { + match jsonata.evaluate(Some(&input), None) { Ok(result) => println!("{}", result.serialize(true)), Err(error) => println!("{}", error), } diff --git a/src/evaluator/value/impls.rs b/src/evaluator/value/impls.rs index 157986c1..001a1fe2 100644 --- a/src/evaluator/value/impls.rs +++ b/src/evaluator/value/impls.rs @@ -94,8 +94,8 @@ impl std::fmt::Debug for Value<'_> { } } -impl std::string::ToString for Value<'_> { - fn to_string(&self) -> String { - format!("{:#?}", self) +impl std::fmt::Display for Value<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:#?}", self) } } diff --git a/src/lib.rs b/src/lib.rs index f8bd7f54..665bd2c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ #![cfg_attr(not(doctest), doc = include_str!("../README.md"))] +use std::collections::HashMap; + use bumpalo::Bump; mod errors; @@ -6,6 +8,7 @@ mod evaluator; mod parser; pub use errors::Error; +pub use evaluator::functions::FunctionContext; pub use evaluator::value::{ArrayFlags, Value}; use evaluator::{frame::Frame, functions::*, Evaluator}; @@ -48,7 +51,43 @@ impl<'a> JsonAta<'a> { ); } - pub fn evaluate(&self, input: Option<&str>) -> Result<&'a Value<'a>> { + fn json_value_to_value(&self, json_value: &serde_json::Value) -> &'a mut Value<'a> { + return match json_value { + serde_json::Value::Null => Value::null(self.arena), + serde_json::Value::Bool(b) => Value::bool(self.arena, *b), + serde_json::Value::Number(n) => Value::number(self.arena, n.as_f64().unwrap()), + serde_json::Value::String(s) => Value::string(self.arena, s), + + serde_json::Value::Array(a) => { + let array = Value::array_with_capacity(self.arena, a.len(), ArrayFlags::empty()); + for v in a.iter() { + array.push(self.json_value_to_value(v)) + } + + return array; + } + serde_json::Value::Object(o) => { + let object = Value::object_with_capacity(self.arena, o.len()); + for (k, v) in o.iter() { + object.insert(k, self.json_value_to_value(v)); + } + return object; + } + }; + } + + pub fn evaluate( + &self, + input: Option<&str>, + bindings: Option<&HashMap<&str, &serde_json::Value>>, + ) -> Result<&'a Value<'a>> { + if let Some(bindings) = bindings { + for (key, json_value) in bindings.iter() { + let value = self.json_value_to_value(json_value); + self.assign_var(key, value); + } + }; + self.evaluate_timeboxed(input, None, None) } @@ -131,7 +170,7 @@ mod tests { let jsonata = JsonAta::new("$test()", &arena).unwrap(); jsonata.register_function("test", 0, |ctx, _| Ok(Value::number(ctx.arena, 1))); - let result = jsonata.evaluate(Some(r#"anything"#)); + let result = jsonata.evaluate(Some(r#"anything"#), None); assert_eq!(result.unwrap(), Value::number(&arena, 1)); } @@ -144,7 +183,7 @@ mod tests { Ok(Value::string(ctx.arena, "time for tea")) }); - let result = jsonata.evaluate(Some(r#"anything"#)); + let result = jsonata.evaluate(Some(r#"anything"#), None); assert_eq!(result.unwrap(), Value::string(&arena, "time for tea")); } @@ -158,7 +197,7 @@ mod tests { return Ok(Value::number(ctx.arena, (num.as_f64()).sqrt())); }); - let result = jsonata.evaluate(Some(r#"anything"#)); + let result = jsonata.evaluate(Some(r#"anything"#), None); assert_eq!( result @@ -179,7 +218,7 @@ mod tests { return Ok(Value::bool(ctx.arena, (num.as_f64()) % 2.0 == 0.0)); }); - let result = jsonata.evaluate(Some(r#"anything"#)); + let result = jsonata.evaluate(Some(r#"anything"#), None); assert_eq!( result @@ -190,4 +229,47 @@ mod tests { vec![4.0, 16.0] ); } + + #[test] + fn evaluate_with_bindings_simple() { + let arena = Bump::new(); + let jsonata = JsonAta::new("$a + $b", &arena).unwrap(); + + let a = &serde_json::Value::Number(serde_json::Number::from(1)); + let b = &serde_json::Value::Number(serde_json::Number::from(2)); + + let mut bindings = HashMap::new(); + bindings.insert("a", a); + bindings.insert("b", b); + + let result = jsonata.evaluate(None, Some(&bindings)); + + assert_eq!(result.unwrap().as_f64(), 3.0); + } + + #[test] + fn evaluate_with_bindings_nested() { + let arena = Bump::new(); + let jsonata = JsonAta::new("$foo[0].a + $foo[1].b", &arena).unwrap(); + + let foo_string = r#" + [ + { + "a": 1 + }, + { + "b": 2 + } + ] + "#; + + let foo: serde_json::Value = serde_json::from_str(foo_string).unwrap(); + + let mut bindings = HashMap::new(); + bindings.insert("foo", &foo); + + let result = jsonata.evaluate(None, Some(&bindings)); + + assert_eq!(result.unwrap().as_f64(), 3.0); + } } diff --git a/tests/testsuite.rs b/tests/testsuite.rs index f1d3b263..73ee1254 100644 --- a/tests/testsuite.rs +++ b/tests/testsuite.rs @@ -33,7 +33,7 @@ fn test_case(resource: &str) { &arena, ) .unwrap(); - let test = test_jsonata.evaluate(None).unwrap(); + let test = test_jsonata.evaluate(None, None).unwrap(); let test = Value::wrap_in_array_if_needed(&arena, test, ArrayFlags::empty()); for case in test.members() {