phon_codegen/source.rs
1//! The codegen input: a set of root Rust types, resolved (via the facet bridge)
2//! into one schema registry plus the roots' content-derived ids.
3//!
4//! Every target language renders from a [`Module`]: the registry gives the type
5//! graph, the roots name the entry points.
6
7use std::collections::BTreeMap;
8
9use facet::{Facet, Shape};
10use phon::derive::{DeriveError, of_shape};
11use phon_schema::{Schema, SchemaId};
12
13/// A root entry type the caller asked to generate.
14#[derive(Clone, Debug)]
15pub struct Root {
16 /// The Rust type identifier (used as the generated type name).
17 pub name: String,
18 /// The root type's content-derived schema id.
19 pub id: SchemaId,
20}
21
22/// A resolved set of types to generate from.
23#[derive(Clone, Debug, Default)]
24pub struct Module {
25 /// Every composite schema reachable from the roots, deduped and sorted by id
26 /// (primitives are intrinsic and not listed). This is the registry a peer
27 /// rebuilds from the emitted schema-bytes.
28 pub schemas: Vec<Schema>,
29 /// The root entry types, in the order they were added.
30 pub roots: Vec<Root>,
31}
32
33impl Module {
34 /// Resolve a set of root shapes into one module. Schemas reachable from more
35 /// than one root are deduped by their content-derived id.
36 ///
37 /// # Errors
38 /// [`DeriveError`] if any shape uses a kind the bridge does not handle.
39 pub fn from_shapes(roots: &[&'static Shape]) -> Result<Module, DeriveError> {
40 let mut by_id: BTreeMap<u64, Schema> = BTreeMap::new();
41 let mut root_list = Vec::with_capacity(roots.len());
42 for &shape in roots {
43 let derived = of_shape(shape)?;
44 for s in derived.schemas {
45 by_id.entry(s.id.as_u64()).or_insert(s);
46 }
47 root_list.push(Root {
48 name: shape.type_identifier.to_string(),
49 id: derived.root,
50 });
51 }
52 Ok(Module {
53 schemas: by_id.into_values().collect(),
54 roots: root_list,
55 })
56 }
57
58 /// Look up a composite schema by id (primitives are not stored here).
59 #[must_use]
60 pub fn schema(&self, id: SchemaId) -> Option<&Schema> {
61 self.schemas.iter().find(|s| s.id == id)
62 }
63}
64
65/// Builder for accumulating root types by their Rust type, sidestepping the
66/// `&'static Shape` lifetime dance at the call site.
67#[derive(Default)]
68pub struct Builder {
69 shapes: Vec<&'static Shape>,
70}
71
72impl Builder {
73 #[must_use]
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 /// Add a root type `T`.
79 #[must_use]
80 pub fn add<'a, T: Facet<'a>>(mut self) -> Self {
81 self.shapes.push(T::SHAPE);
82 self
83 }
84
85 /// Resolve the accumulated roots into a [`Module`].
86 ///
87 /// # Errors
88 /// As [`Module::from_shapes`].
89 pub fn build(&self) -> Result<Module, DeriveError> {
90 Module::from_shapes(&self.shapes)
91 }
92}