jsonata 0.0.0

An (incomplete) implementation of JSONata in Rust
Documentation
#![cfg(test)]
extern crate test_generator;

use bumpalo::Bump;
use std::fs;
use std::path;
use test_generator::test_resources;

use jsonata::value::ArrayFlags;
use jsonata::{JsonAta, Value};

const SKIP: &[&str] = &[
    // The order of object properties in the output is not deterministic,
    // so string comparison fails. If we were using something like a BTreeMap
    // or an IndexedMap then running these would be possible.
    "tests/testsuite/groups/function-string/case018.json",
    "tests/testsuite/groups/function-string/case027.json",
    "tests/testsuite/groups/function-string/case028.json",
];

#[test_resources("tests/testsuite/groups/*/*.json")]
fn t(resource: &str) {
    if SKIP.iter().any(|&s| s == resource) {
        return;
    }

    test_case(resource);
}

fn test_case(resource: &str) {
    let arena = Bump::new();
    let test_jsonata = JsonAta::new(
        &fs::read_to_string(path::Path::new(resource)).unwrap(),
        &arena,
    )
    .unwrap();
    let test = test_jsonata.evaluate(None).unwrap();
    let test = Value::wrap_in_array_if_needed(&arena, test, ArrayFlags::empty());

    for case in test.members() {
        let timelimit = &case["timelimit"];
        let timelimit = if timelimit.is_integer() {
            Some(timelimit.as_usize())
        } else {
            None
        };

        let depth = &case["depth"];
        let depth = if depth.is_integer() {
            Some(depth.as_usize())
        } else {
            None
        };

        let expr = &case["expr"];
        let expr_file = &case["expr-file"];

        let expr = if expr.is_string() {
            expr.as_str().to_string()
        } else if expr_file.is_string() {
            fs::read_to_string(
                path::Path::new(resource)
                    .parent()
                    .unwrap()
                    .join(expr_file.as_str().to_string()),
            )
            .unwrap()
        } else {
            panic!("No expression")
        };

        let data = &case["data"];
        let dataset = &case["dataset"];

        let data = if dataset.is_string() {
            let dataset = format!("tests/testsuite/datasets/{}.json", dataset.as_str());
            fs::read_to_string(&dataset).unwrap()
        } else if data.is_undefined() {
            "".to_string()
        } else {
            data.serialize(false)
        };

        let jsonata = JsonAta::new(&expr, &arena);

        match jsonata {
            Ok(jsonata) => {
                if case["bindings"].is_object() {
                    for (key, value) in case["bindings"].entries() {
                        jsonata.assign_var(key, value);
                    }
                }

                let data = if data.is_empty() {
                    None
                } else {
                    Some(data.as_str())
                };

                let result = jsonata.evaluate_timeboxed(data, depth, timelimit);

                match result {
                    Ok(result) => {
                        let expected_result = &case["result"];

                        if case["undefinedResult"] == true {
                            assert!(result.is_undefined());
                        } else if case["unordered"] == true {
                            // Some test cases specify that the expected array result can be unordered
                            // because the order is implementation dependent. To implement that here
                            // we do a pretty bad O(n^2) just to see if the test passes.
                            assert!(expected_result.is_array());
                            assert!(result.is_array());
                            for expected_member in expected_result.members() {
                                let mut found = false;
                                for member in result.members() {
                                    if member == expected_member {
                                        found = true;
                                        break;
                                    }
                                }
                                assert!(found);
                            }
                        } else {
                            assert_eq!(result, expected_result);
                        }
                    }
                    Err(error) => {
                        eprintln!("{}", error);
                        let code = if !case["error"].is_undefined() {
                            &case["error"]["code"]
                        } else {
                            &case["code"]
                        };
                        assert_eq!(*code, error.code());
                    }
                }
            }
            Err(error) => {
                eprintln!("{}", error);
                let code = if !case["error"].is_undefined() {
                    &case["error"]["code"]
                } else {
                    &case["code"]
                };
                assert_eq!(*code, error.code());
            }
        }
    }
}