Skip to main content

adam_rs/template/
builder.rs

1//! Ordered recipe construction and local inclusion remapping.
2
3use std::any::{Any, TypeId};
4use std::collections::{HashMap, HashSet};
5use std::rc::Rc;
6
7use super::{
8    CellFactory, CellRecipe, CellSlot, ConditionalRecipe, Item, MethodRecipe, PolicyRecipe,
9    RelationshipRecipe, RelationshipSlot, SheetTemplate, TemplateError, TemplateInclusion,
10};
11use crate::conditional::MatchSource;
12use crate::{CellId, CellKind, Filter, MatchExpr, Requirement, Sheet};
13
14/// Owns declarations and validates all local references before accepting them.
15///
16/// Private included names do not enter this builder's declaration namespace.
17///
18/// # Examples
19/// See the [module example](super).
20pub struct TemplateBuilder {
21    recipe: SheetTemplate,
22    names: HashSet<String>,
23    /// Tracks local groups already owned by a conditional.
24    conditional_groups: HashSet<usize>,
25    /// Tracks private cells already assigned a filter.
26    filtered: HashSet<usize>,
27}
28
29impl Default for TemplateBuilder {
30    /// Creates an empty builder with a fresh local owner.
31    fn default() -> Self {
32        Self::new()
33    }
34}
35
36impl TemplateBuilder {
37    /// Creates an empty recipe with its own local handle namespace.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// # use adam_rs::TemplateBuilder;
43    /// let template = TemplateBuilder::new().finish()?;
44    /// assert!(template.is_relationship_only());
45    /// # Ok::<(), adam_rs::TemplateError>(())
46    /// ```
47    pub fn new() -> Self {
48        Self {
49            recipe: SheetTemplate {
50                owner: Rc::new(()),
51                cells: Vec::new(),
52                relationships: Vec::new(),
53                items: Vec::new(),
54                written_cells: HashSet::new(),
55                relationship_only: true,
56                expanded_size: Some(0),
57                depth: Some(1),
58            },
59            names: HashSet::new(),
60            conditional_groups: HashSet::new(),
61            filtered: HashSet::new(),
62        }
63    }
64
65    /// Declares a typed formal that allocates and writes no destination cell.
66    ///
67    /// # Errors
68    ///
69    /// Returns [`TemplateError::DuplicateParameter`] for a repeated declaration
70    /// name, or [`TemplateError::Capacity`] when storage cannot be reserved.
71    ///
72    /// - Complexity: Amortized O(n) time and space for a name of length n.
73    ///
74    /// # Examples
75    ///
76    /// See the [module example](super).
77    pub fn parameter<T: 'static>(&mut self, name: &str) -> Result<CellSlot, TemplateError> {
78        self.add_cell_recipe(name, TypeId::of::<T>(), CellKind::Cell, None)
79    }
80
81    /// Declares a private cell initialized afresh for every live instance.
82    ///
83    /// The initializer owns its captures and runs only during instantiation.
84    ///
85    /// # Errors
86    ///
87    /// Returns [`TemplateError::DuplicateParameter`] for a repeated declaration
88    /// name, or [`TemplateError::Capacity`] when storage cannot be reserved.
89    ///
90    /// - Complexity: Amortized O(n) time and space for a name of length n.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// # use adam_rs::{Sheet, TemplateBuilder};
96    /// let mut builder = TemplateBuilder::new();
97    /// builder.cell("count", || Ok(3i32))?;
98    /// let mut sheet = Sheet::new();
99    /// let instance = builder.finish()?.instantiate(&mut sheet, &[])?;
100    /// assert_eq!(*sheet.read::<i32>(instance.cells()[0])?, 3);
101    /// # Ok::<(), Box<dyn std::error::Error>>(())
102    /// ```
103    pub fn cell<T, F>(&mut self, name: &str, initial: F) -> Result<CellSlot, TemplateError>
104    where
105        T: Any + PartialEq + 'static,
106        F: Fn() -> anyhow::Result<T> + 'static,
107    {
108        self.add_cell_recipe(
109            name,
110            TypeId::of::<T>(),
111            CellKind::Cell,
112            Some(Box::new(move |sheet: &mut Sheet| {
113                Ok(sheet.add_cell(initial()?))
114            })),
115        )
116    }
117
118    /// Declares a private source initialized afresh for every live instance.
119    ///
120    /// Specialization removes every whole method that writes a source.
121    ///
122    /// # Errors
123    ///
124    /// Returns [`TemplateError::DuplicateParameter`] for a repeated declaration
125    /// name, or [`TemplateError::Capacity`] when storage cannot be reserved.
126    ///
127    /// - Complexity: Amortized O(n) time and space for a name of length n.
128    ///
129    /// # Examples
130    ///
131    /// ```
132    /// # use adam_rs::{CellKind, Sheet, TemplateBuilder};
133    /// let mut builder = TemplateBuilder::new();
134    /// builder.source("constant", || Ok(7i32))?;
135    /// let mut sheet = Sheet::new();
136    /// let instance = builder.finish()?.instantiate(&mut sheet, &[])?;
137    /// assert_eq!(sheet.cell_kind(instance.cells()[0]), Some(CellKind::Source));
138    /// # Ok::<(), adam_rs::TemplateError>(())
139    /// ```
140    pub fn source<T, F>(&mut self, name: &str, initial: F) -> Result<CellSlot, TemplateError>
141    where
142        T: Any + PartialEq + 'static,
143        F: Fn() -> anyhow::Result<T> + 'static,
144    {
145        self.add_cell_recipe(
146            name,
147            TypeId::of::<T>(),
148            CellKind::Source,
149            Some(Box::new(move |sheet: &mut Sheet| {
150                Ok(sheet.add_source(initial()?))
151            })),
152        )
153    }
154
155    /// Appends a checked local cell declaration and its ordered body item.
156    ///
157    /// - Complexity: Amortized O(n) time and space for a name of length n.
158    fn add_cell_recipe(
159        &mut self,
160        name: &str,
161        type_id: TypeId,
162        kind: CellKind,
163        make: Option<Box<CellFactory>>,
164    ) -> Result<CellSlot, TemplateError> {
165        if self.names.contains(name) {
166            return Err(TemplateError::DuplicateParameter { name: name.into() });
167        }
168        self.recipe
169            .cells
170            .try_reserve(1)
171            .map_err(|_| TemplateError::Capacity)?;
172        self.recipe
173            .items
174            .try_reserve(1)
175            .map_err(|_| TemplateError::Capacity)?;
176        let index = self.recipe.cells.len();
177        self.names.insert(name.into());
178        self.recipe.relationship_only &= make.is_none();
179        self.recipe.cells.push(CellRecipe {
180            name: name.into(),
181            type_id,
182            kind,
183            make,
184        });
185        self.recipe.items.push(Item::Cell(index));
186        Ok(CellSlot {
187            owner: self.recipe.owner.clone(),
188            index,
189        })
190    }
191
192    /// Appends a relationship after validating owners and native edge-set rules.
193    ///
194    /// Methods must reference the same cell set and have distinct, non-nested
195    /// output sets. Source-writing methods remain authored until specialization.
196    ///
197    /// # Errors
198    ///
199    /// Returns [`TemplateError::EmptyRelationship`], [`TemplateError::ForeignHandle`],
200    /// [`TemplateError::InvalidDefinition`], or [`TemplateError::Capacity`].
201    ///
202    /// - Complexity: O(m² × e) time and O(m × e) space for m methods of at most e edges.
203    ///
204    /// # Examples
205    ///
206    /// See the [module example](super).
207    pub fn relationship(
208        &mut self,
209        methods: Vec<MethodRecipe>,
210    ) -> Result<RelationshipSlot, TemplateError> {
211        self.validate_relationship(&methods)?;
212        self.recipe
213            .relationships
214            .try_reserve(1)
215            .map_err(|_| TemplateError::Capacity)?;
216        self.recipe
217            .items
218            .try_reserve(1)
219            .map_err(|_| TemplateError::Capacity)?;
220        let index = self.recipe.relationships.len();
221        self.recipe
222            .written_cells
223            .extend(methods.iter().flat_map(|m| &m.outputs).map(|s| s.index));
224        self.recipe
225            .relationships
226            .push(RelationshipRecipe::Methods(methods));
227        self.recipe.items.push(Item::Relationship(index));
228        Ok(RelationshipSlot {
229            owner: self.recipe.owner.clone(),
230            index,
231        })
232    }
233
234    /// Checks ownership and native relationship invariants without running factories.
235    ///
236    /// - Complexity: O(m² × e) time and O(m × e) space for m methods of at most e edges.
237    fn validate_relationship(&self, methods: &[MethodRecipe]) -> Result<(), TemplateError> {
238        if methods.is_empty() {
239            return Err(TemplateError::EmptyRelationship);
240        }
241        for slot in methods
242            .iter()
243            .flat_map(|m| m.inputs.iter().chain(&m.outputs))
244        {
245            self.check_slot(slot)?;
246        }
247        if let Some(slot) = methods
248            .iter()
249            .flat_map(|m| &m.outputs)
250            .find(|s| self.recipe.cells[s.index].kind == CellKind::Out)
251        {
252            return Err(TemplateError::InvalidDefinition {
253                detail: "an out cell already has its sole writer",
254                slot: Some(slot.clone()),
255            });
256        }
257        let edges: Vec<_> = methods
258            .iter()
259            .map(|method| (method.inputs.as_slice(), method.outputs.as_slice()))
260            .collect();
261        super::check_relationship_edges(&edges).map_err(|detail| TemplateError::InvalidDefinition {
262            detail,
263            slot: Some(methods[0].outputs[0].clone()),
264        })
265    }
266
267    /// Rejects a local reference not owned by this recipe.
268    fn check_slot(&self, slot: &CellSlot) -> Result<(), TemplateError> {
269        if !Rc::ptr_eq(&self.recipe.owner, &slot.owner) || slot.index >= self.recipe.cells.len() {
270            return Err(TemplateError::ForeignHandle { slot: slot.clone() });
271        }
272        Ok(())
273    }
274
275    /// Promotes a private ordinary cell through its sole, single-output writer.
276    ///
277    /// Formal parameters cannot become outputs. Requirements attach separately,
278    /// in declaration order, through [`Self::require`].
279    ///
280    /// # Errors
281    ///
282    /// Returns [`TemplateError::InvalidDefinition`] for a non-private, non-ordinary,
283    /// multiply-written, or non-single output; also returns relationship validation errors.
284    ///
285    /// - Complexity: O(i + r) time and O(i) space for inputs i and prior output summaries r.
286    ///
287    /// # Examples
288    ///
289    /// ```
290    /// # use adam_rs::{TemplateBuilder, MethodRecipe, Method};
291    /// let mut b = TemplateBuilder::new();
292    /// let input = b.parameter::<i32>("input")?;
293    /// let output = b.cell("output", || Ok(0i32))?;
294    /// b.out(MethodRecipe::new(&[input], &[output], |i, o| {
295    ///     Ok(Method::from_fn_1_1(i[0], o[0], |x: &i32| Ok(*x)))
296    /// })?)?;
297    /// # Ok::<(), adam_rs::TemplateError>(())
298    /// ```
299    pub fn out(&mut self, writer: MethodRecipe) -> Result<RelationshipSlot, TemplateError> {
300        self.validate_relationship(std::slice::from_ref(&writer))?;
301        let output = &writer.outputs[0];
302        self.check_private(output)?;
303        if writer.outputs.len() != 1
304            || self.recipe.cells[output.index].kind != CellKind::Cell
305            || self.recipe.written_cells.contains(&output.index)
306        {
307            return Err(TemplateError::InvalidDefinition {
308                detail: "out requires one private ordinary cell with no prior writer",
309                slot: Some(output.clone()),
310            });
311        }
312        self.recipe
313            .relationships
314            .try_reserve(1)
315            .map_err(|_| TemplateError::Capacity)?;
316        self.recipe
317            .items
318            .try_reserve(1)
319            .map_err(|_| TemplateError::Capacity)?;
320        let index = self.recipe.relationships.len();
321        self.recipe.cells[output.index].kind = CellKind::Out;
322        self.recipe.written_cells.insert(output.index);
323        self.recipe
324            .relationships
325            .push(RelationshipRecipe::Out(writer));
326        self.recipe.items.push(Item::Relationship(index));
327        self.recipe.relationship_only = false;
328        Ok(RelationshipSlot {
329            owner: self.recipe.owner.clone(),
330            index,
331        })
332    }
333
334    /// Rejects policy targets that are not private declarations of this owner.
335    fn check_private(&self, cell: &CellSlot) -> Result<(), TemplateError> {
336        self.check_slot(cell)?;
337        if self.recipe.cells[cell.index].make.is_none() {
338            return Err(TemplateError::InvalidDefinition {
339                detail: "component policies and out writers require private targets",
340                slot: Some(cell.clone()),
341            });
342        }
343        Ok(())
344    }
345
346    /// Attaches one freshly constructed filter to a private cell.
347    ///
348    /// The factory receives the live target and positional dynamic arguments.
349    /// Its filter must declare those exact arguments and their concrete types.
350    ///
351    /// # Errors
352    ///
353    /// Returns ownership, capacity, or [`TemplateError::InvalidDefinition`] errors
354    /// for a formal target, duplicate filter, or self-dependent filter.
355    ///
356    /// - Complexity: O(i) time and space for i inputs.
357    ///
358    /// # Examples
359    ///
360    /// ```
361    /// # use adam_rs::{TemplateBuilder, Filter};
362    /// let mut b = TemplateBuilder::new();
363    /// let x = b.cell("x", || Ok(0i32))?;
364    /// b.filter(&x, &[], |_, _| Ok(Filter::from_fn_0(|x: &i32| Ok((*x).max(0)))))?;
365    /// # Ok::<(), adam_rs::TemplateError>(())
366    /// ```
367    pub fn filter<F>(
368        &mut self,
369        cell: &CellSlot,
370        inputs: &[CellSlot],
371        make: F,
372    ) -> Result<(), TemplateError>
373    where
374        F: Fn(CellId, &[CellId]) -> anyhow::Result<Filter> + 'static,
375    {
376        self.check_private(cell)?;
377        if self.filtered.contains(&cell.index) || inputs.contains(cell) {
378            return Err(TemplateError::InvalidDefinition {
379                detail: "duplicate or self-dependent filter",
380                slot: Some(cell.clone()),
381            });
382        }
383        let slot = cell.clone();
384        self.add_policy(
385            cell,
386            inputs,
387            Box::new(move |sheet, target, ids| {
388                let filter = make(target, ids).map_err(|source| TemplateError::Factory {
389                    source,
390                    slot: slot.clone(),
391                })?;
392                if filter.0.args != ids
393                    || filter.0.value_type != sheet.cell_type_id(target).unwrap()
394                    || !super::instantiate::signature_matches(sheet, ids, &filter.0.arg_types)
395                {
396                    return Err(TemplateError::InvalidDefinition {
397                        detail: "filter factory signature differs from its recipe",
398                        slot: Some(slot.clone()),
399                    });
400                }
401                sheet
402                    .add_filter(target, filter)
403                    .map_err(TemplateError::Runtime)
404            }),
405        )?;
406        self.filtered.insert(cell.index);
407        Ok(())
408    }
409
410    /// Attaches a freshly constructed requirement to a private cell.
411    ///
412    /// The factory must declare exactly the supplied positional inputs and types.
413    /// Native attachment evaluates ordinary/source requirements immediately;
414    /// output requirements wait until propagation.
415    ///
416    /// # Errors
417    ///
418    /// Returns ownership, capacity, or [`TemplateError::InvalidDefinition`] errors
419    /// for a formal target. Factory and native errors occur during instantiation.
420    ///
421    /// - Complexity: O(i) time and space for i inputs.
422    ///
423    /// # Examples
424    ///
425    /// ```
426    /// # use adam_rs::{TemplateBuilder, Requirement};
427    /// let mut b = TemplateBuilder::new();
428    /// let x = b.cell("x", || Ok(1i32))?;
429    /// b.require(&x, &[x.clone()], |i| Ok(Requirement::from_fn_1(i[0], |x: &i32| Ok(*x > 0))))?;
430    /// # Ok::<(), adam_rs::TemplateError>(())
431    /// ```
432    pub fn require<F>(
433        &mut self,
434        cell: &CellSlot,
435        inputs: &[CellSlot],
436        make: F,
437    ) -> Result<(), TemplateError>
438    where
439        F: Fn(&[CellId]) -> anyhow::Result<Requirement> + 'static,
440    {
441        let slot = cell.clone();
442        self.add_policy(
443            cell,
444            inputs,
445            Box::new(move |sheet, target, ids| {
446                let requirement = make(ids).map_err(|source| TemplateError::Factory {
447                    source,
448                    slot: slot.clone(),
449                })?;
450                if requirement.inputs != ids
451                    || !super::instantiate::signature_matches(sheet, ids, &requirement.input_types)
452                {
453                    return Err(TemplateError::InvalidDefinition {
454                        detail: "requirement factory signature differs from its recipe",
455                        slot: Some(slot.clone()),
456                    });
457                }
458                sheet
459                    .add_requirement(target, requirement)
460                    .map(|_| ())
461                    .map_err(TemplateError::Runtime)
462            }),
463        )
464    }
465
466    /// Appends a private policy after validating all local input owners.
467    ///
468    /// - Complexity: O(i) time and space for i inputs.
469    fn add_policy(
470        &mut self,
471        cell: &CellSlot,
472        inputs: &[CellSlot],
473        make: Box<super::PolicyFactory>,
474    ) -> Result<(), TemplateError> {
475        self.check_private(cell)?;
476        for input in inputs {
477            self.check_slot(input)?;
478        }
479        self.recipe
480            .items
481            .try_reserve(1)
482            .map_err(|_| TemplateError::Capacity)?;
483        self.recipe.items.push(Item::Policy(PolicyRecipe {
484            cell: cell.clone(),
485            inputs: inputs.to_vec(),
486            make,
487        }));
488        self.recipe.relationship_only = false;
489        Ok(())
490    }
491
492    /// Registers native conditional branches over direct relationships or included groups.
493    ///
494    /// Included groups must be transitively relationship-only before specialization.
495    /// Each constituent remains a distinct native relationship; no extra activation
496    /// layer is introduced. Keys follow native first-match semantics.
497    ///
498    /// # Errors
499    ///
500    /// Returns [`TemplateError::ConditionalComponent`] for stateful groups, or
501    /// [`TemplateError::InvalidDefinition`] for foreign, reused, or duplicate groups
502    /// or empty branch keys. Input ownership and capacity errors are also reported.
503    ///
504    /// - Complexity: O(i + g + k) time and space for inputs i, groups g, and keys k.
505    ///
506    /// # Examples
507    ///
508    /// ```
509    /// # use adam_rs::{TemplateBuilder, MatchExpr};
510    /// let mut b = TemplateBuilder::new();
511    /// let guard = b.parameter::<bool>("guard")?;
512    /// b.conditional(&[guard], |i| Ok(MatchExpr::cell(i[0])), vec![(vec![true], vec![])], vec![])?;
513    /// # Ok::<(), adam_rs::TemplateError>(())
514    /// ```
515    pub fn conditional<T, F>(
516        &mut self,
517        inputs: &[CellSlot],
518        make: F,
519        branches: Vec<(Vec<T>, Vec<RelationshipSlot>)>,
520        default: Vec<RelationshipSlot>,
521    ) -> Result<(), TemplateError>
522    where
523        T: Clone + Any + PartialEq + 'static,
524        F: Fn(&[CellId]) -> anyhow::Result<MatchExpr> + 'static,
525    {
526        let (keys, groups): (Vec<_>, Vec<_>) = branches.into_iter().unzip();
527        if keys.iter().any(Vec::is_empty) {
528            return Err(TemplateError::InvalidDefinition {
529                detail: "conditional branch requires keys",
530                slot: None,
531            });
532        }
533        self.conditional_factory(inputs, make, move || Ok(keys.clone()), groups, default)
534    }
535
536    /// Registers conditional groups with fresh keys constructed once per instance.
537    ///
538    /// Neither factory executes during preparation. Group ownership, uniqueness and
539    /// relationship-only eligibility are checked before mutation. Keys follow native
540    /// first-match semantics and must have one nonempty vector per declared branch.
541    ///
542    /// # Errors
543    /// Reports invalid inputs/groups before mutation; reports failed factories, invalid
544    /// key counts, empty keys or native conditional errors during construction.
545    /// - Complexity: O(i + b + g) preparation for inputs i, branches b and groups g;
546    ///   construction adds factory costs and O(i + b + k + g + r) for keys k and
547    ///   expanded relationship members r.
548    ///
549    /// # Examples
550    /// ```
551    /// let mut b = adam_rs::TemplateBuilder::new();
552    /// let flag = b.parameter::<bool>("flag")?;
553    /// b.conditional_factory::<bool, _, _>(&[flag],
554    ///     |ids| Ok(adam_rs::MatchExpr::cell(ids[0])),
555    ///     || Ok(vec![vec![true]]), vec![vec![]], vec![])?;
556    /// # Ok::<(), adam_rs::TemplateError>(())
557    /// ```
558    pub fn conditional_factory<T, F, K>(
559        &mut self,
560        inputs: &[CellSlot],
561        make: F,
562        keys: K,
563        branches: Vec<Vec<RelationshipSlot>>,
564        default: Vec<RelationshipSlot>,
565    ) -> Result<(), TemplateError>
566    where
567        T: Any + PartialEq + 'static,
568        F: Fn(&[CellId]) -> anyhow::Result<MatchExpr> + 'static,
569        K: Fn() -> anyhow::Result<Vec<Vec<T>>> + 'static,
570    {
571        for input in inputs {
572            self.check_slot(input)?;
573        }
574        let mut seen = HashSet::new();
575        for group in branches.iter().flatten().chain(&default) {
576            if !Rc::ptr_eq(&self.recipe.owner, &group.owner)
577                || group.index >= self.recipe.relationships.len()
578                || self.conditional_groups.contains(&group.index)
579                || !seen.insert(group.index)
580            {
581                return Err(TemplateError::InvalidDefinition {
582                    detail: "foreign or already conditional relationship group",
583                    slot: None,
584                });
585            }
586            let eligible = match &self.recipe.relationships[group.index] {
587                RelationshipRecipe::Methods(_) => true,
588                RelationshipRecipe::Out(_) => false,
589                RelationshipRecipe::Include { child, .. } => child.relationship_only,
590            };
591            if !eligible {
592                return Err(TemplateError::ConditionalComponent);
593            }
594        }
595        self.recipe
596            .items
597            .try_reserve(1)
598            .map_err(|_| TemplateError::Capacity)?;
599        self.recipe.items.push(Item::Conditional(ConditionalRecipe {
600            inputs: inputs.to_vec(),
601            make: Box::new(move |sheet, ids, groups, relationships| {
602                let source =
603                    make(ids).map_err(|source| TemplateError::ConditionalFactory { source })?;
604                let valid = match &source.0 {
605                    MatchSource::Cell(id) => {
606                        ids == [*id] && sheet.cell_type_id(*id) == Some(TypeId::of::<T>())
607                    }
608                    MatchSource::Expr(expr) => {
609                        expr.inputs == ids
610                            && expr.output_type == TypeId::of::<T>()
611                            && super::instantiate::signature_matches(sheet, ids, &expr.input_types)
612                    }
613                };
614                if !valid {
615                    return Err(TemplateError::InvalidDefinition {
616                        detail: "conditional factory signature differs from its recipe",
617                        slot: None,
618                    });
619                }
620                let mut live_branches = Vec::new();
621                live_branches
622                    .try_reserve(branches.len())
623                    .map_err(|_| TemplateError::Capacity)?;
624                let keys = keys().map_err(|source| TemplateError::ConditionalFactory { source })?;
625                if keys.len() != branches.len() || keys.iter().any(Vec::is_empty) {
626                    return Err(TemplateError::InvalidDefinition {
627                        detail: "conditional key factory differs from its branches",
628                        slot: None,
629                    });
630                }
631                for (live_keys, slots) in keys.into_iter().zip(&branches) {
632                    live_branches.push((
633                        live_keys,
634                        super::instantiate::group_members(slots, groups, relationships)?,
635                    ));
636                }
637                let live_default =
638                    super::instantiate::group_members(&default, groups, relationships)?;
639                sheet
640                    .add_conditional(source, live_branches, live_default)
641                    .map_err(TemplateError::Runtime)
642            }),
643        }));
644        self.conditional_groups.extend(seen);
645        self.recipe.relationship_only = false;
646        Ok(())
647    }
648
649    /// Appends a compact child-DAG edge with checked named bindings.
650    ///
651    /// Binding and structural failures leave the builder unchanged. Immutable
652    /// factories are shared, but every live instantiation creates fresh native methods.
653    /// The returned single group slot represents all transitive relationships;
654    /// only instantiation enumerates its distinct live members in inline order.
655    ///
656    /// # Errors
657    ///
658    /// Returns binding errors for unknown, missing, duplicate, aliased, foreign,
659    /// or wrongly typed arguments; returns [`TemplateError::InvalidDefinition`]
660    /// for a private out argument, or [`TemplateError::Capacity`] on reservation failure.
661    ///
662    /// - Complexity: Expected O(c + b) time and space for the child's own local
663    ///   cells c and bindings b, independent of transitive expansion size.
664    ///
665    /// # Examples
666    ///
667    /// ```
668    /// # use std::rc::Rc;
669    /// # use adam_rs::TemplateBuilder;
670    /// let child = Rc::new(TemplateBuilder::new().finish()?);
671    /// let mut parent = TemplateBuilder::new();
672    /// let included = parent.include(child, &[])?;
673    /// assert_eq!(included.relationships().len(), 1);
674    /// # Ok::<(), adam_rs::TemplateError>(())
675    /// ```
676    pub fn include(
677        &mut self,
678        child: Rc<SheetTemplate>,
679        bindings: &[(&str, CellSlot)],
680    ) -> Result<TemplateInclusion, TemplateError> {
681        let bound = child.bind(bindings, |_, slot| {
682            self.check_slot(slot)?;
683            Ok(self.recipe.cells[slot.index].type_id)
684        })?;
685        if let Some(slot) = bound
686            .values()
687            .find(|s| self.recipe.cells[s.index].kind == CellKind::Out)
688        {
689            return Err(TemplateError::InvalidDefinition {
690                detail: "an included parameter cannot bind an out cell",
691                slot: Some(slot.clone()),
692            });
693        }
694        self.recipe
695            .relationships
696            .try_reserve(1)
697            .map_err(|_| TemplateError::Capacity)?;
698        self.recipe
699            .items
700            .try_reserve(1)
701            .map_err(|_| TemplateError::Capacity)?;
702        let index = self.recipe.relationships.len();
703        let handles = vec![RelationshipSlot {
704            owner: self.recipe.owner.clone(),
705            index,
706        }];
707        self.recipe.written_cells.extend(
708            child
709                .written_cells
710                .iter()
711                .filter_map(|i| bound.get(i))
712                .map(|s| s.index),
713        );
714        self.recipe.relationship_only &= child.relationship_only;
715        self.recipe.relationships.push(RelationshipRecipe::Include {
716            child,
717            bindings: bound,
718        });
719        self.recipe.items.push(Item::Relationship(index));
720        Ok(TemplateInclusion {
721            relationships: handles,
722        })
723    }
724
725    /// Finalizes the owning immutable recipe without invoking any factory.
726    ///
727    /// # Errors
728    ///
729    /// Rejects included parameters that became private outputs after inclusion.
730    /// Expanded-size overflow is retained for diagnosis before live expansion.
731    ///
732    /// - Complexity: O(n + e) time for local declarations n and edges e; no DAG expansion.
733    ///
734    /// # Examples
735    ///
736    /// See the [module example](super).
737    pub fn finish(mut self) -> Result<SheetTemplate, TemplateError> {
738        let mut size = self
739            .recipe
740            .cells
741            .len()
742            .checked_add(self.recipe.items.len())
743            .and_then(|n| n.checked_add(self.recipe.relationships.len()))
744            // Every occurrence needs a record, even when its recipe is empty.
745            .and_then(|n| n.checked_add(1));
746        let mut depth = Some(1usize);
747        for relationship in &self.recipe.relationships {
748            match relationship {
749                RelationshipRecipe::Include { child, bindings } => {
750                    if let Some(slot) = bindings
751                        .values()
752                        .find(|s| self.recipe.cells[s.index].kind == CellKind::Out)
753                    {
754                        return Err(TemplateError::InvalidDefinition {
755                            detail: "an included parameter cannot bind an out cell",
756                            slot: Some(slot.clone()),
757                        });
758                    }
759                    size = size.and_then(|n| child.expanded_size.and_then(|c| n.checked_add(c)));
760                    depth = depth
761                        .and_then(|d| child.depth.and_then(|c| c.checked_add(1)).map(|c| d.max(c)));
762                }
763                RelationshipRecipe::Methods(methods) => {
764                    for method in methods {
765                        size = size
766                            .and_then(|n| n.checked_add(method.inputs.len()))
767                            .and_then(|n| n.checked_add(method.outputs.len()))
768                            .and_then(|n| n.checked_add(1));
769                    }
770                }
771                RelationshipRecipe::Out(method) => {
772                    size = size
773                        .and_then(|n| n.checked_add(method.inputs.len()))
774                        .and_then(|n| n.checked_add(method.outputs.len()));
775                }
776            }
777        }
778        self.recipe.expanded_size = size;
779        self.recipe.depth = depth;
780        Ok(self.recipe)
781    }
782}
783
784impl SheetTemplate {
785    /// Builds a local-index binding map after checking names, aliases, and concrete types.
786    ///
787    /// - Complexity: Expected O(c + b) time and O(c + b) space for cells c and bindings b.
788    pub(super) fn bind<A, F>(
789        &self,
790        bindings: &[(&str, A)],
791        type_of: F,
792    ) -> Result<HashMap<usize, A>, TemplateError>
793    where
794        A: Clone + Eq + std::hash::Hash,
795        F: Fn(&str, &A) -> Result<TypeId, TemplateError>,
796    {
797        let parameters: HashMap<&str, usize> = self
798            .cells
799            .iter()
800            .enumerate()
801            .filter(|(_, c)| c.make.is_none())
802            .map(|(i, c)| (c.name.as_str(), i))
803            .collect();
804        let mut result = HashMap::with_capacity(bindings.len());
805        let mut actuals = HashMap::with_capacity(bindings.len());
806        for (name, actual) in bindings {
807            let &index = parameters
808                .get(name)
809                .ok_or_else(|| TemplateError::UnknownParameter {
810                    name: (*name).into(),
811                })?;
812            if result.contains_key(&index) {
813                return Err(TemplateError::DuplicateParameter {
814                    name: (*name).into(),
815                });
816            }
817            if let Some(first) = actuals.insert(actual.clone(), *name) {
818                return Err(TemplateError::AliasedArgument {
819                    first: first.into(),
820                    second: (*name).into(),
821                });
822            }
823            let found = type_of(name, actual)?;
824            let expected = self.cells[index].type_id;
825            if expected != found {
826                return Err(TemplateError::TypeMismatch {
827                    name: (*name).into(),
828                    expected,
829                    found,
830                });
831            }
832            result.insert(index, actual.clone());
833        }
834        for (index, cell) in self
835            .cells
836            .iter()
837            .enumerate()
838            .filter(|(_, c)| c.make.is_none())
839        {
840            if !result.contains_key(&index) {
841                return Err(TemplateError::MissingParameter {
842                    name: cell.name.clone(),
843                });
844            }
845        }
846        Ok(result)
847    }
848}
849
850impl std::hash::Hash for CellSlot {
851    /// Hashes the immutable owner identity and local index consistently with equality.
852    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
853        std::hash::Hash::hash(&Rc::as_ptr(&self.owner), state);
854        std::hash::Hash::hash(&self.index, state);
855    }
856}
857
858#[cfg(test)]
859mod tests {
860    use super::*;
861
862    /// Starts fresh namespaces and keeps the builder usable after a rejected declaration.
863    #[test]
864    fn defaults_and_rejected_declarations_preserve_builder_contract() {
865        let mut builder = TemplateBuilder::default();
866        let slot = builder.parameter::<i32>("input").unwrap();
867        assert!(matches!(
868            builder.parameter::<bool>("input"),
869            Err(TemplateError::DuplicateParameter { .. })
870        ));
871        let foreign = TemplateBuilder::new().parameter::<i32>("input").unwrap();
872        assert_ne!(slot, foreign);
873        assert_eq!(HashSet::from([slot.clone(), slot, foreign]).len(), 2);
874        builder.cell("private", || Ok(9_i32)).unwrap();
875        let template = builder.finish().unwrap();
876        assert_eq!(template.parameter_names().collect::<Vec<_>>(), ["input"]);
877        assert!(!template.is_relationship_only());
878        let mut sheet = Sheet::new();
879        let actual = sheet.add_cell(3_i32);
880        let instance = template
881            .instantiate(&mut sheet, &[("input", actual)])
882            .unwrap();
883        assert_eq!(instance.cells().len(), 2);
884        assert_eq!(*sheet.read::<i32>(instance.cells()[1]).unwrap(), 9);
885        assert!(
886            TemplateBuilder::default()
887                .finish()
888                .unwrap()
889                .is_relationship_only()
890        );
891    }
892}