Skip to main content

type_bridge_schema_codegen/
lib.rs

1//! Deterministic emitters over validated binding-neutral runtime projections.
2
3#![deny(missing_docs)]
4
5use std::collections::BTreeMap;
6
7mod package;
8mod python;
9mod rust;
10mod typescript;
11
12pub use package::GeneratedPackage;
13pub use python::PythonEmitter;
14pub use rust::RustEmitter;
15pub use typescript::TypeScriptEmitter;
16
17use type_bridge_contract::diagnostic::{Diagnostic, DiagnosticCategory, DiagnosticCode};
18use type_bridge_contract::projection::{ModelProjection, ProjectedAnnotation};
19use type_bridge_contract::schema::{
20    AnnotationFactId, AnnotationKindId, SchemaAnnotationValue, encode_declared_schema,
21};
22use type_bridge_schema::{VerifiedSchemaAuthority, encode_schema_authority};
23
24#[derive(Clone, Debug, Eq, PartialEq)]
25struct EmbeddedAuthority {
26    canonical_envelope_json: String,
27    declared_schema_json: String,
28    managed_scope_id: String,
29    semantic_profile_id: String,
30}
31
32fn embedded_authority(
33    projection: &type_bridge_contract::projection::RuntimeProjection,
34    authority: &VerifiedSchemaAuthority,
35) -> Result<EmbeddedAuthority, Diagnostic> {
36    if projection.semantic_fingerprint() != authority.resolved_schema().semantic_fingerprint() {
37        return Err(invalid(
38            "schema_codegen_authority_mismatch",
39            "runtime projection and verified schema authority have different semantic fingerprints",
40        ));
41    }
42
43    let canonical_envelope_json =
44        String::from_utf8(encode_schema_authority(authority)).map_err(|_| {
45            invalid(
46                "schema_codegen_non_utf8_authority",
47                "canonical schema-authority JSON must be UTF-8",
48            )
49        })?;
50    let declared_schema_json =
51        String::from_utf8(encode_declared_schema(authority.declared_schema())?).map_err(|_| {
52            invalid(
53                "schema_codegen_non_utf8_declared_schema",
54                "canonical declared-schema JSON must be UTF-8",
55            )
56        })?;
57
58    Ok(EmbeddedAuthority {
59        canonical_envelope_json,
60        declared_schema_json,
61        managed_scope_id: authority.managed_scope().id().as_str().to_owned(),
62        semantic_profile_id: authority.semantic_profile().id().as_str().to_owned(),
63    })
64}
65
66fn invalid(code: &'static str, message: impl Into<String>) -> Diagnostic {
67    Diagnostic::new(
68        DiagnosticCategory::InvalidContract,
69        DiagnosticCode::new(code).expect("schema-codegen diagnostic code is valid"),
70        message,
71    )
72}
73
74fn model_documentation(model: &ModelProjection) -> Option<String> {
75    let type_doc = documentation_annotation(model.declaration().annotations());
76    let direct_sub_doc = model
77        .declaration()
78        .direct_sub()
79        .and_then(|sub| documentation_annotation(sub.annotations()).map(|doc| (sub, doc)));
80
81    match (type_doc, direct_sub_doc) {
82        (None, None) => None,
83        (Some(type_doc), None) => Some(type_doc.to_owned()),
84        (type_doc, Some((sub, sub_doc))) => {
85            let edge_doc = format!(
86                "Direct subtype of `{}`:\n{sub_doc}",
87                sub.id().supertype().label()
88            );
89            Some(match type_doc {
90                Some(type_doc) => format!("{type_doc}\n\n{edge_doc}"),
91                None => edge_doc,
92            })
93        }
94    }
95}
96
97fn documentation_annotation(
98    annotations: &BTreeMap<AnnotationFactId, ProjectedAnnotation>,
99) -> Option<&str> {
100    annotations.values().find_map(|annotation| {
101        if annotation.id().kind() != &AnnotationKindId::Doc {
102            return None;
103        }
104        match annotation.value() {
105            SchemaAnnotationValue::Doc(doc) => Some(doc.as_str()),
106            _ => None,
107        }
108    })
109}