Skip to content

Commit

Permalink
Merge pull request #10 from Stedi/asennikov/allow-evaluating-with-bin…
Browse files Browse the repository at this point in the history
…dings

feat: allow evaluating with bindings
  • Loading branch information
asennikov authored May 9, 2024
2 parents 8bac923 + db77a9e commit e40b9f1
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 10 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/bin/jsonata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down
6 changes: 3 additions & 3 deletions src/evaluator/value/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
92 changes: 87 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#![cfg_attr(not(doctest), doc = include_str!("../README.md"))]
use std::collections::HashMap;

use bumpalo::Bump;

mod errors;
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};
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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));
}
Expand All @@ -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"));
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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);
}
}
2 changes: 1 addition & 1 deletion tests/testsuite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down

0 comments on commit e40b9f1

Please sign in to comment.