type-bridge-contract 2.2.3

Versioned binding-neutral contract primitives for type-bridge
Documentation
use type_bridge_contract::capability::CapabilitySet;
use type_bridge_contract::codec::{FormatVersion, to_canonical_json};
use type_bridge_contract::id::{AttributeId, Label, StructId, TypeId, TypeKind};
use type_bridge_contract::schema::{
    DeclaredSchema, DocumentId, OwnsFact, OwnsFactId, SchemaFact, SourceSpan, SourcedSchemaFact,
    StructFact, StructField, TypeFact, ValueFact, ValueFactId,
};
use type_bridge_contract::value::ValueTypeTag;

fn span(line: u32) -> SourceSpan {
    SourceSpan::new(
        DocumentId::new("schema/types.yaml").unwrap(),
        u64::from(line - 1),
        u64::from(line),
        line,
        1,
        line,
        2,
    )
    .unwrap()
}

fn overlap_facts() -> Vec<SourcedSchemaFact> {
    let person = TypeId::new(TypeKind::Entity, "person").unwrap();
    let age_type = TypeId::new(TypeKind::Attribute, "age").unwrap();
    let age = AttributeId::new("age").unwrap();
    vec![
        SourcedSchemaFact::new(
            SchemaFact::Type(TypeFact::new(person.clone()).unwrap()),
            span(1),
        ),
        SourcedSchemaFact::new(SchemaFact::Type(TypeFact::new(age_type).unwrap()), span(2)),
        SourcedSchemaFact::new(
            SchemaFact::Value(ValueFact::new(
                ValueFactId::new(age.clone()),
                ValueTypeTag::Long,
            )),
            span(3),
        ),
        SourcedSchemaFact::new(
            SchemaFact::Owns(OwnsFact::new(OwnsFactId::new(person, age).unwrap())),
            span(4),
        ),
    ]
}

#[test]
fn declared_identity_is_order_and_source_invariant() {
    let first =
        DeclaredSchema::from_facts(FormatVersion::V1, CapabilitySet::new(), overlap_facts())
            .unwrap();
    let mut reordered = overlap_facts();
    reordered.reverse();
    let second =
        DeclaredSchema::from_facts(FormatVersion::V1, CapabilitySet::new(), reordered).unwrap();
    assert_eq!(
        first.declared_identity_fingerprint(),
        second.declared_identity_fingerprint(),
    );
    assert_eq!(
        first.canonical_identity_bytes().unwrap(),
        second.canonical_identity_bytes().unwrap(),
    );
}

#[test]
fn duplicate_facts_report_both_sources() {
    let mut facts = overlap_facts();
    let duplicate = facts[0].fact().clone();
    facts.push(SourcedSchemaFact::new(duplicate, span(9)));
    let diagnostics =
        DeclaredSchema::from_facts(FormatVersion::V1, CapabilitySet::new(), facts).unwrap_err();
    let diagnostic = diagnostics.iter().next().unwrap();
    assert_eq!(
        diagnostic.diagnostic().code().as_str(),
        "duplicate_schema_fact"
    );
    assert_eq!(diagnostic.primary().unwrap().line(), 9);
    assert_eq!(diagnostic.related()[0].span().line(), 1);
}

#[test]
fn dangling_references_fail_before_provider_io() {
    let person = TypeId::new(TypeKind::Entity, "person").unwrap();
    let missing = AttributeId::new("missing").unwrap();
    let facts = vec![
        SourcedSchemaFact::new(
            SchemaFact::Type(TypeFact::new(person.clone()).unwrap()),
            span(1),
        ),
        SourcedSchemaFact::new(
            SchemaFact::Owns(OwnsFact::new(OwnsFactId::new(person, missing).unwrap())),
            span(2),
        ),
    ];
    let diagnostics =
        DeclaredSchema::from_facts(FormatVersion::V1, CapabilitySet::new(), facts).unwrap_err();
    assert_eq!(
        diagnostics
            .iter()
            .next()
            .unwrap()
            .diagnostic()
            .code()
            .as_str(),
        "unknown_schema_fact_reference",
    );
}

#[test]
fn struct_fact_preserves_order_and_canonical_bytes() {
    let fact = StructFact::new(
        StructId::new("player-stats").unwrap(),
        vec![
            StructField::new(Label::new("wins").unwrap(), ValueTypeTag::Long, false),
            StructField::new(Label::new("nickname").unwrap(), ValueTypeTag::String, true),
        ],
    )
    .unwrap();

    assert_eq!(fact.fields()[0].name().as_str(), "wins");
    assert_eq!(fact.fields()[1].name().as_str(), "nickname");
    assert_eq!(
        String::from_utf8(to_canonical_json(&fact).unwrap()).unwrap(),
        r#"{"fields":[{"name":"wins","optional":false,"value_type":"long"},{"name":"nickname","optional":true,"value_type":"string"}],"id":"player-stats"}"#,
    );
}

#[test]
fn struct_fact_rejects_empty_and_duplicate_fields() {
    let empty = StructFact::new(StructId::new("empty").unwrap(), Vec::new()).unwrap_err();
    assert_eq!(empty.code().as_str(), "empty_struct_fields");

    let field = StructField::new(Label::new("wins").unwrap(), ValueTypeTag::Long, false);
    let duplicate = StructFact::new(
        StructId::new("duplicate").unwrap(),
        vec![field.clone(), field],
    )
    .unwrap_err();
    assert_eq!(duplicate.code().as_str(), "duplicate_struct_field");
}