xidl-parser 0.105.0

A IDL codegen.
Documentation
use crate::{hir, parser::parser_text};

fn lower(source: &str) -> hir::Specification {
    hir::Specification::from_typed_ast_with_properties_and_path(
        parser_text(source).expect("parse source"),
        [("expand_interface".into(), false.into())].into(),
        "printer.idl",
    )
    .expect("lower source")
}

#[test]
fn versioned_includes_keep_the_callers_resolver_during_lowering() {
    struct Dependency;
    impl crate::parser::IncludeResolver for Dependency {
        fn resolve(
            &mut self,
            _parent: Option<&str>,
            path: &str,
        ) -> crate::error::ParserResult<(String, String)> {
            assert_eq!(path, "dependency/types.idl");
            Ok((
                "dependency/types.idl".into(),
                "@version enum LibraryVersions { old, current }; struct Stamp { long seconds; };"
                    .into(),
            ))
        }
    }
    let source = "#include \"dependency/types.idl@old\"\n\
                  @version enum V { v1, v2 }; struct Item { Stamp stamp; };";
    let typed =
        crate::parser::parser_text_with_resolver(source, Some("api.idl"), &mut Dependency).unwrap();
    let spec = hir::Specification::from_typed_ast_with_resolver(
        typed,
        [("expand_interface".into(), false.into())].into(),
        "api.idl",
        &mut Dependency,
    )
    .unwrap();
    let record = crate::versioning::VersioningRecord::analyze(&spec)
        .unwrap()
        .unwrap();
    for (id, _) in record.timeline().iter() {
        let projected = record.project(&spec, id).unwrap();
        let printed = hir::printer::IdlPrinter::render(&projected);
        assert!(printed.contains("struct Stamp"), "{printed}");
        assert!(!printed.contains("LibraryVersions"), "{printed}");
        assert_eq!(
            serde_json::to_value(lower(&printed)).unwrap(),
            serde_json::to_value(&projected).unwrap()
        );
    }
}

#[test]
fn versioned_rest_projection_prints_and_reparses_to_the_same_hir() {
    let source = include_str!("../../../bdd/features/data/versioned_rest.idl");
    let source = lower(source);
    let record = crate::versioning::VersioningRecord::analyze(&source)
        .expect("analyze")
        .expect("versioned");
    for (version, definition) in record.timeline().iter() {
        let projected = record.project(&source, version).expect("project");
        let printed = hir::printer::IdlPrinter::render(&projected);
        if definition.ident == "v1" {
            insta::assert_snapshot!("version_diff_projection_v1", &printed);
        }
        let reparsed = lower(&printed);
        assert_eq!(
            serde_json::to_value(&reparsed).expect("serialize reparsed"),
            serde_json::to_value(&projected).expect("serialize projected"),
            "{}: {printed}",
            definition.ident
        );
    }
}

#[test]
fn printer_ignores_documentation_annotations() {
    let source = lower("/// first\nstruct Item { /// member\n string name; };");
    let printed = hir::printer::IdlPrinter::render(&source);
    assert!(!printed.contains("first"));
    assert!(!printed.contains("member"));
    assert!(printed.contains("struct Item"));
}

#[test]
fn inline_constructs_round_trip_without_emitting_duplicate_declarations() {
    let source = lower(
        "typedef struct Inline { string value; } InlineAlias; \
         union Choice switch (long) { case 1: struct Item { long value; } item; };",
    );
    let printed = hir::printer::IdlPrinter::render(&source);
    let reparsed = lower(&printed);
    assert_eq!(
        serde_json::to_value(&reparsed).expect("serialize reparsed"),
        serde_json::to_value(&source).expect("serialize source")
    );
    assert_eq!(printed.matches("struct Inline").count(), 1);
    assert_eq!(printed.matches("struct Item").count(), 1);
}

#[test]
fn printer_preserves_defaults_and_octet_types() {
    let source = lower("struct Item { long count default 3; octet payload; };");
    let printed = hir::printer::IdlPrinter::render(&source);
    let reparsed = lower(&printed);
    assert_eq!(
        serde_json::to_value(&reparsed).expect("serialize reparsed"),
        serde_json::to_value(&source).expect("serialize source")
    );
    assert!(printed.contains("default 3"));
    assert!(printed.contains("octet payload"));
}