xidl-parser 0.105.0

A IDL codegen.
Documentation
use super::*;
use crate::error::ParseError;
use crate::versioning::{VersioningError, VersioningRecord};

fn analyze_error(text: &str) -> VersioningError {
    match lower_error(text) {
        ParseError::Versioning(error) => error,
        other => panic!("expected a versioning error, got {other:?}"),
    }
}

#[test]
fn version_enum_is_discovered() {
    let (_, record) = analyze(
        r#"
@version
enum Versions {
    @rename("1.0") v1,
    v2,
};

struct Pet {
    string id;
};
"#,
    );

    assert_eq!(record.timeline().enum_ident(), "Versions");
    assert_eq!(record.timeline().len(), 2);
    assert_eq!(record.timeline().declared(), "v1 (1.0), v2 (v2)");
}

#[test]
fn unversioned_specifications_are_accepted() {
    let spec = parse("struct Pet { string id; };");
    assert!(
        VersioningRecord::analyze(&spec)
            .expect("analysis")
            .is_none()
    );
}

#[test]
fn version_marker_outside_an_enum_is_rejected() {
    let error = analyze_error(
        r#"
@version
struct Pet {
    string id;
};
"#,
    );

    assert!(matches!(error, VersioningError::VersionOutsideEnum { .. }));
}

#[test]
fn version_marker_on_a_member_is_rejected() {
    let error = analyze_error(
        r#"
@version
enum Versions { v1, v2 };

struct Pet {
    @version
    string id;
};
"#,
    );

    assert!(matches!(error, VersioningError::VersionOutsideEnum { .. }));
}

#[test]
fn version_marker_with_unknown_arguments_is_rejected() {
    let error = analyze_error(
        r#"
@version("1.0")
enum Versions { v1, v2 };
"#,
    );

    assert!(matches!(
        error,
        VersioningError::MalformedVersionParameter { .. }
    ));
}

#[test]
fn duplicate_version_enums_are_rejected() {
    let error = analyze_error(
        r#"
@version
enum Versions { v1 };

@version
enum Other { v1 };
"#,
    );

    assert!(matches!(
        error,
        VersioningError::DuplicateVersionEnum { .. }
    ));
}

#[test]
fn empty_version_enum_is_rejected() {
    let error = analyze_error("@version\nenum Versions { };");
    assert!(matches!(error, VersioningError::EmptyVersionEnum { .. }));
}

#[test]
fn lifecycle_annotation_without_a_version_enum_is_rejected() {
    let error = analyze_error(
        r#"
struct Pet {
    @added(Versions::v2)
    int32 age;
};
"#,
    );

    assert!(matches!(error, VersioningError::MissingVersionEnum { .. }));
}

#[test]
fn lifecycle_annotations_on_the_version_enum_are_rejected() {
    let error = analyze_error(
        r#"
@version
enum Versions {
    @added(Versions::v2)
    v1,
    v2,
};
"#,
    );

    assert!(matches!(
        error,
        VersioningError::VersionedVersionEnum { .. }
    ));
}

#[test]
fn unsupported_version_annotations_are_rejected() {
    let error = analyze_error(
        r#"
@version
enum Versions { v1, v2 };

struct Pet {
    @renamedFrom(Versions::v2, "old")
    string name;
};
"#,
    );

    assert!(matches!(
        error,
        VersioningError::UnsupportedVersionAnnotation { .. }
    ));
}

#[test]
fn unsupported_annotations_cannot_bypass_validation_without_a_version_marker() {
    for annotation in [
        "versioned",
        "renamedFrom",
        "RENAMEDFROM",
        "madeOptional",
        "madeRequired",
        "typeChangedFrom",
        "returnTypeChangedFrom",
        "defaultVersion",
    ] {
        let source = format!("enum V {{ v1, v2 }}; struct Pet {{ @{annotation} string name; }};");
        assert!(
            matches!(
                analyze_error(&source),
                VersioningError::UnsupportedVersionAnnotation { .. }
            ),
            "{annotation}"
        );
    }
}

#[test]
fn malformed_lifecycle_arguments_are_rejected() {
    let error = analyze_error(
        r#"
@version
enum Versions { v1, v2 };

struct Pet {
    @added("2.0")
    int32 age;
};
"#,
    );

    assert!(matches!(
        error,
        VersioningError::MalformedVersionReference { .. }
    ));
}