type-bridge-schema-codegen 2.2.1

Deterministic projection-only schema emitters for type-bridge
Documentation
use std::collections::BTreeSet;

use type_bridge_contract::fingerprint::SemanticProfileId;
use type_bridge_contract::projection::{BindingTarget, CodeResourceDigest, ProjectionConfig};
use type_bridge_contract::schema::DocumentId;
use type_bridge_schema::{SchemaDocumentSet, normalize_documents, project, resolve};
use type_bridge_schema_codegen::RustEmitter;

mod support;

fn projected(
    source: &str,
    resources: &[CodeResourceDigest],
) -> (
    type_bridge_contract::projection::RuntimeProjection,
    type_bridge_schema::VerifiedSchemaAuthority,
) {
    let documents =
        SchemaDocumentSet::parse([(DocumentId::new("rust-emitter.yaml").unwrap(), source)])
            .unwrap();
    let declared = normalize_documents(&documents).unwrap();
    let profile = SemanticProfileId::new("typedb-3.12.1/v1").unwrap();
    let resolved = resolve(&declared, &profile).unwrap();
    let projection = project(
        &resolved,
        BindingTarget::Rust,
        &ProjectionConfig::rust(),
        &RustEmitter::new().generator_handlers(),
        resources,
    )
    .unwrap();
    (projection, support::authority(source))
}

#[test]
fn emits_exact_deterministic_single_dependency_crate() {
    let emitter = RustEmitter::new();
    let resources = emitter.code_resources().unwrap();
    let (projection, authority) = projected(include_str!("acceptance/schema.yaml"), &resources);
    let first = emitter.emit(&projection, &authority).unwrap();
    let second = emitter.emit(&projection, &authority).unwrap();
    assert_eq!(first, second);
    assert_eq!(
        first
            .files()
            .keys()
            .map(String::as_str)
            .collect::<BTreeSet<_>>(),
        BTreeSet::from([
            "Cargo.toml",
            "src/create.rs",
            "src/declaration.rs",
            "src/functions.rs",
            "src/lib.rs",
            "src/read.rs",
            "src/reference.rs",
            "src/runtime.rs",
            "src/schema.rs",
            "src/structs.rs",
            "src/tokens.rs",
            "typebridge/migration-history.json",
        ]),
    );
    let schema = String::from_utf8(first.get("src/schema.rs").unwrap().to_vec()).unwrap();
    assert!(schema.contains("pub fn open_migration_catalog()"));
    assert!(schema.contains("include_bytes!(\"../typebridge/migration-history.json\")"));
    let declarations =
        String::from_utf8(first.get("src/declaration.rs").unwrap().to_vec()).unwrap();
    for import in [
        "HydratedRow",
        "HydrationCapability",
        "ValidationError",
        "MaterializeModel",
    ] {
        assert!(declarations.contains(import));
    }
    assert!(declarations.contains("fn __tb_dispatch_subtype"));
    assert!(!declarations.contains("fn dispatch_subtype"));
    let person_start = declarations
        .find("impl SubtypeRootModel for Person")
        .unwrap();
    let person_end = person_start + declarations[person_start..].find(" } }\n").unwrap() + 4;
    let person_impl = &declarations[person_start..person_end];
    let identity = person_impl
        .find("__tb_row.type_id_json() == Person::TYPE_ID_JSON")
        .unwrap();
    let materialize = person_impl.find("Person::materialize").unwrap();
    assert!(identity < materialize);
    assert!(
        person_impl.contains("ValidationError::new(\"type_id\", \"wrong_concrete_model_type\")")
    );
    let membership_start = declarations
        .find("impl SubtypeRootModel for Membership")
        .unwrap();
    let membership_end =
        membership_start + declarations[membership_start..].find(" } }\n").unwrap() + 4;
    let membership_impl = &declarations[membership_start..membership_end];
    assert!(membership_impl.contains("Membership::TYPE_ID_JSON => Membership::materialize(__tb_row, __tb_cap).map(MembershipFamily::Membership)"));
    assert!(membership_impl.contains("Employment::TYPE_ID_JSON => Employment::materialize(__tb_row, __tb_cap).map(MembershipFamily::Employment)"));
    assert!(
        membership_impl
            .contains("_ => Err(ValidationError::new(\"type_id\", \"wrong_concrete_model_type\"))")
    );
    assert!(
        declarations.find("impl Model for Membership").unwrap()
            < declarations.find("impl Model for Employment").unwrap()
    );
    assert!(declarations.contains("RoleUpcast<EmploymentEmployeePlayer, MembershipMemberPlayer>"));
    assert!(declarations.contains(
        "impl RoleTokenCompatible<Employment, EmploymentEmployeePlayer> for Employment {}"
    ));
    assert!(!declarations.contains(
        "impl RoleTokenCompatible<Membership, MembershipMemberPlayer> for Employment {}"
    ));
    let create = String::from_utf8(first.get("src/create.rs").unwrap().to_vec()).unwrap();
    assert!(!create.contains("} if"));
    assert!(!create.contains("allow(clippy::possible_missing_else)"));
    for evidence in [
        "duplicate_scalar_evidence",
        "unexpected_field_evidence",
        "duplicate_role_evidence",
        "unexpected_role_evidence",
    ] {
        assert!(create.contains(evidence));
    }
    assert!(create.contains("pub struct ContainerCreate"));
    assert!(create.contains("pub fn try_new"));
    let read = String::from_utf8(first.get("src/read.rs").unwrap().to_vec()).unwrap();
    assert!(read.contains("impl QueryValued for Identifier { type Domain = String; }"));
    let tokens = String::from_utf8(first.get("src/tokens.rs").unwrap().to_vec()).unwrap();
    assert!(tokens.contains("pub struct ContainerType"));
    assert!(tokens.contains("pub enum ContainerItemPlayer"));
    assert!(tokens.contains("impl RolePlayer<Person> for EmploymentEmployeePlayer {}"));
    assert!(tokens.contains("pub const plays_event_container_item"));
    let manifest = String::from_utf8(first.get("Cargo.toml").unwrap().to_vec()).unwrap();
    assert!(manifest.contains("[dependencies]"));
    assert!(manifest.contains("type-bridge = { version = \"=2.2.1\", default-features = false }"));
    let schema = String::from_utf8(first.get("src/schema.rs").unwrap().to_vec()).unwrap();
    assert!(schema.contains("pub(crate) const SCHEMA_AUTHORITY_JSON"));
    assert!(schema.contains("typebridge.schema-authority/v1"));
    assert!(schema.contains("pub(crate) const DECLARED_SCHEMA_JSON"));
    assert!(schema.contains("pub(crate) const MANAGED_SCOPE_ID"));
    assert!(schema.contains("pub(crate) const SEMANTIC_PROFILE_ID"));
    assert!(manifest.contains("doctest = false"));
    assert!(manifest.contains("rust-version = \"1.88\""));
}

#[test]
fn emits_safely_line_prefixed_type_and_direct_sub_documentation() {
    let emitter = RustEmitter::new();
    let resources = emitter.code_resources().unwrap();
    let (projection, authority) = projected(
        r#"format: typebridge.schema/v2
entities:
  actor: {}
  person:
    doc: |-
      Type "doc".
      closing */ kept
      ```rust
      compile_error!("schema documentation must never execute");
      ```
          compile_error!("indented schema documentation must never execute");
    sub:
      type: actor
      doc: |-
        Edge 'doc' \ path
        closes */ safely
"#,
        &resources,
    );
    let package = emitter.emit(&projection, &authority).unwrap();
    let read = std::str::from_utf8(package.get("src/read.rs").unwrap()).unwrap();
    let reference = std::str::from_utf8(package.get("src/reference.rs").unwrap()).unwrap();
    let manifest = std::str::from_utf8(package.get("Cargo.toml").unwrap()).unwrap();
    let documentation = "/// Type \"doc\".\n/// closing */ kept\n/// ```rust\n/// compile_error!(\"schema documentation must never execute\");\n/// ```\n///     compile_error!(\"indented schema documentation must never execute\");\n/// \n/// Direct subtype of `actor`:\n/// Edge 'doc' \\ path\n/// closes */ safely\n";

    assert!(manifest.contains("doctest = false"));
    assert!(read.contains(&format!(
        "{documentation}#[derive(Clone, PartialEq)]\npub struct Person"
    )));
    assert!(reference.contains(&format!(
        "{documentation}#[derive(Clone, PartialEq)]\npub struct PersonRef"
    )));
    assert!(read.contains("impl core::fmt::Debug for Person"));
    assert!(reference.contains("impl core::fmt::Debug for PersonRef"));
}

#[test]
fn emits_common_value_getter_for_abstract_attribute_family() {
    let emitter = RustEmitter::new();
    let resources = emitter.code_resources().unwrap();
    let (projection, authority) = projected(
        r#"format: typebridge.schema/v2
attributes:
  identifier:
    abstract: true
    value: string
  employee-id:
    sub: identifier
"#,
        &resources,
    );

    let package = emitter.emit(&projection, &authority).unwrap();
    let read = std::str::from_utf8(package.get("src/read.rs").unwrap()).unwrap();
    let declaration = std::str::from_utf8(package.get("src/declaration.rs").unwrap()).unwrap();

    assert!(read.contains("pub enum IdentifierFamily"));
    assert!(read.contains("EmployeeId(EmployeeId)"));
    assert!(read.contains("pub fn value(&self) -> &String"));
    assert!(read.contains("Self::EmployeeId(__tb_inner) => __tb_inner.value()"));
    assert!(read.contains("let Self::EmployeeId(__tb_inner) = self; Some(__tb_inner)"));
    assert!(!read.contains("if let Self::EmployeeId("));
    assert!(!read.contains("impl MaterializeModel for IdentifierFamily"));
    assert!(!read.contains("pub fn iid(&self)"));
    assert!(!declaration.contains("impl ModelFamily for IdentifierFamily"));
}

#[test]
fn rejects_projection_without_exact_resource_evidence() {
    let (projection, authority) = projected(include_str!("acceptance/schema.yaml"), &[]);
    let error = RustEmitter::new()
        .emit(&projection, &authority)
        .unwrap_err();
    assert!(error.to_string().contains("rust_emitter_evidence_mismatch"));
}

#[test]
fn rejects_schema_name_colliding_with_runtime_export() {
    let emitter = RustEmitter::new();
    let resources = emitter.code_resources().unwrap();
    let (projection, authority) = projected(
        "format: typebridge.schema/v2\nentities:\n  cardinality: {}\n",
        &resources,
    );
    let error = emitter.emit(&projection, &authority).unwrap_err();
    assert!(error.to_string().contains("rust_emitter_name_collision"));
    assert!(error.to_string().contains("Cardinality"));
}

#[test]
fn rejects_schema_name_colliding_with_injected_schema_export() {
    let emitter = RustEmitter::new();
    let resources = emitter.code_resources().unwrap();
    let (projection, authority) = projected(
        "format: typebridge.schema/v2\nentities:\n  app_schema: {}\n",
        &resources,
    );
    let error = emitter.emit(&projection, &authority).unwrap_err();
    assert!(error.to_string().contains("rust_emitter_name_collision"));
    assert!(error.to_string().contains("AppSchema"));
}