Skip to main content

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}