lemma-engine 0.8.19

A language that means business.
Documentation
use crate::evaluation::response::{EvaluatedRule, Response, RuleResult};
use crate::planning::semantics::{LiteralValue, RulePath, Source, Span};
use crate::OperationResult;
use indexmap::IndexMap;
use rust_decimal::Decimal;

fn dummy_source() -> Source {
    Source::new(
        crate::parsing::source::SourceType::Volatile,
        Span {
            start: 0,
            end: 0,
            line: 1,
            col: 1,
        },
    )
}

fn dummy_rule(name: &str) -> EvaluatedRule {
    EvaluatedRule {
        name: name.to_string(),
        path: RulePath::new(vec![], name.to_string()),
        source_location: dummy_source(),
        rule_type: crate::planning::semantics::primitive_boolean().clone(),
    }
}

fn number_rule_result(name: &str, value: Decimal) -> RuleResult {
    let expression_units = std::collections::HashMap::new();
    RuleResult::from_operation_result(
        dummy_rule(name),
        OperationResult::Value(LiteralValue::number_from_decimal(value)),
        crate::planning::semantics::primitive_number(),
        &expression_units,
        None,
    )
}

#[test]
fn test_response_serialization() {
    let mut results = IndexMap::new();
    results.insert(
        "test_rule".to_string(),
        number_rule_result("test_rule", 42.into()),
    );
    let response = Response {
        spec_name: "test_spec".to_string(),
        effective: "2026-01-01".to_string(),
        spec_hash: None,
        spec_effective_from: None,
        spec_effective_to: None,
        data: vec![],
        results,
    };

    let json = serde_json::to_string(&response).unwrap();
    let deserialized: serde_json::Value = serde_json::from_str(&json).unwrap();
    assert_eq!(deserialized["spec"], "test_spec");
    assert!(deserialized["results"]
        .as_object()
        .unwrap()
        .contains_key("test_rule"));
    assert_eq!(deserialized["results"]["test_rule"]["display"], "42");
    assert_eq!(deserialized["results"]["test_rule"]["number"], "42");
    assert_eq!(deserialized["results"]["test_rule"]["vetoed"], false);
}

#[test]
fn response_number_json_is_scalar() {
    let mut results = IndexMap::new();
    results.insert(
        "double".to_string(),
        number_rule_result("double", 20.into()),
    );
    let response = Response {
        spec_name: "test_spec".to_string(),
        effective: "2026-01-01".to_string(),
        spec_hash: None,
        spec_effective_from: None,
        spec_effective_to: None,
        data: vec![],
        results,
    };
    let json: serde_json::Value =
        serde_json::from_str(&serde_json::to_string(&response).unwrap()).unwrap();
    let number = &json["results"]["double"]["number"];
    assert!(!number.is_array());
    assert_eq!(number.as_str(), Some("20"));
}