Skip to main content

macroonz_compiler/expansion/
type_guard.rs

1//! The expansion home's invariant nucleus: every road that reaches a private field.
2//!
3//! Declared inside `types.rs` as its own child, which is what makes the home's central claim structural rather than reviewed.
4//! An expansion is bound here, after the three values agreed about their parentage, and it is the only value in the crate that hands a closure's proved deliveries out.
5//! An account is built here too, and it accepts only the kind home's complete-set witness so a consumer-owned record cannot smuggle silence past the accounting boundary.
6
7use super::{Accounted, BindError, Expansion};
8use crate::closure::{Closure, PartitionCargo, PartitionedEmission};
9use crate::explanation::View;
10use crate::identity::{self, ClosedExpansionId, Provenance, Transcript, encode_bytes};
11use crate::kind::{Destination, DispositionSet, Kind, KindSet};
12use crate::plan::Plan;
13use crate::render::RenderedUnit;
14
15impl<K: Kind> Expansion<K> {
16    /// Bind one expansion: the plan, the closure proved against it, and the explanation answered over the two.
17    ///
18    /// The road every kind's request terminates at.
19    /// A caller that walked the steps arrives with three unforgeable values and leaves with the one account emission is reachable from; a caller that skipped a step has nothing to hand in.
20    ///
21    /// # Construction
22    ///
23    /// The identity is derived at [`Role::ClosedExpansion`](crate::identity::Role::ClosedExpansion), anchored on the CLOSURE — an expansion exists only where a closure does — over a content transcript of exactly two members: the plan's identity, then the explanation's.
24    ///
25    /// Nothing else enters, and each absence is the no-double-entry law.
26    /// The deliveries are inside the anchor, because a closure's identity commits to their digests; the kind and the account are inside member one, because a plan's identity commits to its intent.
27    /// A second spelling of either here would write one fact twice and let the two spellings drift.
28    ///
29    /// # Errors
30    ///
31    /// Returns [`BindError`] naming the pair that disagreed and both of its identities.
32    /// Nothing is elected out of any pair: an expansion naming one plan while carrying another's proof, or another's explanation, would answer every question correctly about the wrong expansion.
33    pub fn bound(
34        plan: Plan<K>,
35        closure: Closure<K::Role>,
36        explanation: View<K>,
37    ) -> Result<Self, BindError> {
38        let planned = plan.identity();
39        let proved = closure.plan();
40        if planned != proved {
41            return Err(BindError::ClosureProvedAgainstAnotherPlan { planned, proved });
42        }
43        let answered_over_plan = explanation.plan();
44        if planned != answered_over_plan {
45            return Err(BindError::ExplanationAnsweredOverAnotherPlan {
46                planned,
47                answered: answered_over_plan,
48            });
49        }
50        let anchor = closure.identity();
51        let answered_over_closure = explanation.closure();
52        if anchor != answered_over_closure {
53            return Err(BindError::ExplanationAnsweredOverAnotherClosure {
54                proved: anchor,
55                answered: answered_over_closure,
56            });
57        }
58        let mut content = Vec::new();
59        encode_bytes(planned.as_bytes(), &mut content);
60        encode_bytes(explanation.identity().as_bytes(), &mut content);
61        let (derived, provenance) = ClosedExpansionId::derived_with_provenance(
62            Transcript::under_projection(identity::Role::ClosedExpansion, &anchor, &content, 0),
63        );
64        Ok(Self {
65            identity: derived,
66            provenance,
67            plan,
68            closure,
69            explanation,
70        })
71    }
72
73    /// This expansion's own identity: the name of the whole account.
74    #[must_use]
75    pub const fn identity(&self) -> ClosedExpansionId {
76        self.identity
77    }
78
79    /// The record of how that identity was derived.
80    #[must_use]
81    pub const fn provenance(&self) -> &Provenance {
82        &self.provenance
83    }
84
85    /// The complete plan: account, context, content, membership, watch set, trace, trail, and nonclaims.
86    pub const fn plan(&self) -> &Plan<K> {
87        &self.plan
88    }
89
90    /// The proof that what was rendered is what was planned.
91    pub const fn closure(&self) -> &Closure<K::Role> {
92        &self.closure
93    }
94
95    /// Every question this kind owes, answered over that plan and that proof.
96    pub const fn explain(&self) -> &View<K> {
97        &self.explanation
98    }
99
100    /// The deliveries this expansion carries, split by destination.
101    ///
102    /// The closure's own proved value, borrowed rather than copied: this expansion keeps no second emission, so what is delivered is what was proved and there is no pair of values to drift apart.
103    pub const fn emission(&self) -> &PartitionedEmission {
104        self.closure.emission()
105    }
106
107    /// What the declaration site expands into — the only tokens the consumer's normal build compiles.
108    pub const fn emit(&self) -> &PartitionCargo {
109        self.emission().declaration_site()
110    }
111
112    /// The deferred cargo the consumer's test target invokes.
113    pub const fn test_carrier(&self) -> &PartitionCargo {
114        self.emission().test_carrier()
115    }
116
117    /// The deferred cargo the consumer's bench target invokes.
118    pub const fn bench_carrier(&self) -> &PartitionCargo {
119        self.emission().bench_carrier()
120    }
121
122    /// Every unit this expansion publishes as a standalone artifact, in seat order, each carrying the address its own planned output names.
123    ///
124    /// Read off the proved rendering rather than copied into a record beside it: a published artifact IS its rendered unit at an address, and a second value restating that unit's tree, digest, and seat would be a second answer to one question.
125    pub fn published(&self) -> impl Iterator<Item = &RenderedUnit<K::Role>> {
126        self.closure
127            .rendered()
128            .units_to(Destination::PublicationArtifact)
129    }
130}
131
132impl<K: Kind, Set: KindSet> Accounted<K, Set> {
133    /// Seat one door's complete disposition witness beside the expansion that door produced.
134    ///
135    /// Public, because a door is the consumer's: a crate-internal road here would mean only this crate could ever answer for a set of kinds, and this compiler declares none.
136    /// Which row says generated remains the caller's claim — which is exactly what a door decides — while [`DispositionSet`] makes omission structurally unavailable here.
137    pub const fn seated(expansion: Expansion<K>, dispositions: DispositionSet<Set>) -> Self {
138        Self {
139            expansion,
140            dispositions,
141        }
142    }
143
144    /// What this door produced, whole.
145    ///
146    /// Read through, never restated: what it planned, what it proved, what it explains, and what each build receives are that value's own answers.
147    pub const fn expansion(&self) -> &Expansion<K> {
148        &self.expansion
149    }
150
151    /// What happened to every kind of the set, whole and declaration-ordered.
152    ///
153    /// The witness pairs each declared name with the disposition surrendered at that position only after every surrendered name and the whole row count matched the complete set.
154    pub const fn dispositions(&self) -> &DispositionSet<Set> {
155        &self.dispositions
156    }
157}