sysml-v2-parser 0.22.0

SysML v2 textual notation parser for Rust
Documentation
use sysml_v2_parser::ast::{
    CalcDefBody, CalcDefBodyElement, PackageBody, PackageBodyElement, RootElement,
};
use sysml_v2_parser::parse_with_diagnostics;

#[test]
fn calc_body_parses_return_expression_without_swallowing_siblings() {
    let input = r#"package P {
  calc def SubsystemPowerSum {
    in parts;
    return sum(parts.powerDrawW);
  }
  calc def SubsystemCostSum {
    in parts;
    return sum(parts.bomCost);
  }
}"#;
    let result = parse_with_diagnostics(input);
    assert!(
        result.errors.is_empty(),
        "calc return-expression bodies should parse cleanly: {:?}",
        result.errors
    );
    let pkg = match &result.root.elements[0].value {
        RootElement::Package(p) => &p.value,
        _ => panic!("expected package"),
    };
    let PackageBody::Brace { elements } = &pkg.body else {
        panic!("expected brace body");
    };
    let calc_defs: Vec<_> = elements
        .iter()
        .filter_map(|e| match &e.value {
            PackageBodyElement::CalcDef(c) => Some(&c.value),
            _ => None,
        })
        .collect();
    assert_eq!(calc_defs.len(), 2, "expected two calc defs");
    for calc in calc_defs {
        let CalcDefBody::Brace { elements } = &calc.body else {
            panic!("expected brace calc body");
        };
        let expressions: Vec<_> = elements
            .iter()
            .filter_map(|e| match &e.value {
                CalcDefBodyElement::Expression(expr) => Some(expr),
                _ => None,
            })
            .collect();
        assert_eq!(
            expressions.len(),
            1,
            "each calc should have exactly one return expression"
        );
        assert!(
            !elements
                .iter()
                .any(|e| matches!(e.value, CalcDefBodyElement::Other(_))),
            "return expression must not be recovered as Other(...)"
        );
    }
}