json-eval-rs 0.0.125

High-performance JSON Logic evaluator with schema validation and dependency tracking. Built on blazing-fast Rust engine.
Documentation
use json_eval_rs::jsoneval::JSONEval;
use serde_json::json;

fn condition_hidden_layout_schema(keep_hidden_value: bool) -> serde_json::Value {
    json!({
        "type": "object",
        "properties": {
            "toggle": { "type": "boolean" },
            "section": {
                "type": "object",
                "condition": {
                    "hidden": {
                        "$evaluation": { "$ref": "#/properties/toggle" }
                    }
                },
                "$layout": {
                    "elements": [
                        {
                            "type": "VerticalLayout",
                            "elements": [
                                { "$ref": "#/properties/target" }
                            ]
                        }
                    ]
                }
            },
            "target": {
                "type": "string",
                "config": {
                    "all": { "keepHiddenValue": keep_hidden_value }
                }
            }
        },
        "$layout": {
            "elements": [
                { "$ref": "#/properties/section" }
            ]
        }
    })
}

#[test]
fn condition_hidden_layout_ref_clears_non_empty_dependent_data() {
    let schema = condition_hidden_layout_schema(false).to_string();
    let initial_data = r#"{"toggle":false,"target":"keep me"}"#;
    let changed_data = r#"{"toggle":true,"target":"keep me"}"#;
    let mut eval = JSONEval::new(&schema, None, Some(initial_data)).unwrap();

    eval.evaluate(initial_data, None, None, None).unwrap();
    let changes = eval
        .evaluate_dependents(
            &["toggle".to_string()],
            Some(changed_data),
            None,
            true,
            None,
            None,
            true,
        )
        .unwrap();

    assert!(
        changes.as_array().unwrap().iter().any(|change| {
            change.get("$ref").and_then(|value| value.as_str()) == Some("target")
                && change.get("$hidden") == Some(&json!(true))
                && change.get("clear") == Some(&json!(true))
        }),
        "condition-hidden layout ref must emit hidden clear event"
    );
    assert_eq!(eval.eval_data.data().pointer("/target"), Some(&json!(null)));
}

#[test]
fn inherited_condition_hidden_respects_keep_hidden_value() {
    let schema = condition_hidden_layout_schema(true).to_string();
    let initial_data = r#"{"toggle":false,"target":"keep me"}"#;
    let changed_data = r#"{"toggle":true,"target":"keep me"}"#;
    let mut eval = JSONEval::new(&schema, None, Some(initial_data)).unwrap();

    eval.evaluate(initial_data, None, None, None).unwrap();
    let changes = eval
        .evaluate_dependents(
            &["toggle".to_string()],
            Some(changed_data),
            None,
            true,
            None,
            None,
            true,
        )
        .unwrap();

    assert!(
        !changes.as_array().unwrap().iter().any(|change| {
            change.get("$ref").and_then(|value| value.as_str()) == Some("target")
                && change.get("clear") == Some(&json!(true))
        }),
        "keepHiddenValue must suppress inherited hidden clear"
    );
    assert_eq!(
        eval.eval_data.data().pointer("/target"),
        Some(&json!("keep me"))
    );
}

#[test]
fn layout_only_hidden_ref_does_not_clear_dependent_data() {
    let schema = json!({
        "type": "object",
        "properties": {
            "trigger": { "type": "string" },
            "target": { "type": "string" },
            "section": {
                "$layout": {
                    "hideLayout": { "all": true },
                    "elements": [
                        { "$ref": "#/properties/target" }
                    ]
                }
            }
        },
        "$layout": {
            "elements": [
                { "$ref": "#/properties/section" }
            ]
        }
    })
    .to_string();
    let data = r#"{"trigger":"changed","target":"keep me"}"#;
    let mut eval = JSONEval::new(&schema, None, Some(data)).unwrap();

    eval.evaluate(data, None, None, None).unwrap();
    let changes = eval
        .evaluate_dependents(
            &["trigger".to_string()],
            Some(data),
            None,
            true,
            None,
            None,
            true,
        )
        .unwrap();

    assert!(!changes.as_array().unwrap().iter().any(|change| {
        change.get("$ref").and_then(|value| value.as_str()) == Some("target")
            && change.get("clear") == Some(&json!(true))
    }));
    assert_eq!(
        eval.eval_data.data().pointer("/target"),
        Some(&json!("keep me"))
    );
}