Skip to main content

macroonz_compiler/kind/
types.rs

1//! The kind home's declarations: the open semantic traits a consumer implements, the rosters the compiler owns, the disposition vocabulary, and the complete-set witness.
2//!
3//! Declarations only, with every road that reaches a private field in `type_guard.rs`, this file's own child.
4
5use super::type_contract::slot_in;
6use crate::identity::{GeneratedUnit, Identity, OwnerFact, Profile};
7use core::marker::PhantomData;
8
9#[path = "type_guard.rs"]
10mod guard;
11
12/// What one request produces.
13///
14/// A kind is a marker type in the crate that declares it, and the compiler is generic over it from the first step of the road to the last.
15/// Nothing seals this trait and nothing registers an implementation of it.
16///
17/// # Examples
18///
19/// ```rust
20/// use macroonz_compiler::{Kind, NoQuestions, SoleRole};
21///
22/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
23/// struct GreetImpl;
24///
25/// impl Kind for GreetImpl {
26///     const NAME: &'static str = "greet.impl";
27///     type Content = ();
28///     type Role = SoleRole;
29///     type Question = NoQuestions;
30/// }
31///
32/// assert_eq!(GreetImpl::NAME, "greet.impl");
33/// ```
34pub trait Kind: 'static {
35    /// The name this kind is spelled by wherever a name is written down.
36    ///
37    /// Declared rather than read off the Rust spelling, so renaming the marker renames no identity.
38    const NAME: &'static str;
39
40    /// The facts a request of this kind carries beyond its captured tokens.
41    ///
42    /// Its canonical encoding is the content commitment's material, so changing any fact a renderer may read changes the commitment before a plan exists.
43    type Content: CanonicalContent;
44
45    /// The seats this kind's rendering fills.
46    type Role: Role;
47
48    /// The questions this kind owes beyond the universal ones.
49    type Question: Question;
50}
51
52/// Kind-specific facts with one complete canonical encoding.
53///
54/// The encoding is semantic material rather than a rendering for a person.
55/// A kind owns the implementation for its content, and the compiler frames the complete result before deriving the content commitment.
56pub trait CanonicalContent: Clone + Eq + core::fmt::Debug {
57    /// Append every fact this content carries in its declared order.
58    fn encode_content_into(&self, into: &mut Vec<u8>);
59
60    /// The complete canonical bytes of this content.
61    #[must_use]
62    fn canonical_content_bytes(&self) -> Vec<u8> {
63        let mut bytes = Vec::new();
64        self.encode_content_into(&mut bytes);
65        bytes
66    }
67}
68
69/// One seat a kind's rendering fills.
70///
71/// A rendered unit is matched to a planned one by role, so a rendering that produced the right number of units in the wrong seats is caught by the seat rather than by a count.
72pub trait Role: Copy + Eq + core::fmt::Debug + 'static {
73    /// The complete roster, in the order the kind states it.
74    ///
75    /// Every walk over a rendering quantifies over this, and membership admission refuses a member whose role is absent from it — so a lawful value the roster omits cannot become a planned member a walk would never look at.
76    const ALL: &'static [Self];
77
78    /// This role's declared name.
79    #[must_use]
80    fn name(self) -> &'static str;
81
82    /// Where the unit rendered under this role lands.
83    ///
84    /// A property of the seat, so two plans of one kind cannot disagree about which build compiles their units.
85    #[must_use]
86    fn destination(self) -> Destination;
87
88    /// This role's position in the roster, which a rendered unit's transcript carries.
89    ///
90    /// A role the roster does not carry has no position and reads as the roster's length.
91    #[must_use]
92    fn slot(self) -> u16 {
93        slot_in(Self::ALL, self)
94    }
95}
96
97/// One question a kind owes an answer to, beyond the questions every kind owes.
98pub trait Question: Copy + Eq + core::fmt::Debug + 'static {
99    /// The complete roster, in the order the kind states it.
100    const ALL: &'static [Self];
101
102    /// The typed answer to a question of this roster.
103    type Answer: Answer<Question = Self>;
104
105    /// This question's declared name.
106    #[must_use]
107    fn name(self) -> &'static str;
108
109    /// This question's position in the roster, which an explanation's preimage carries.
110    #[must_use]
111    fn slot(self) -> u16 {
112        slot_in(Self::ALL, self)
113    }
114}
115
116/// One typed answer, and the question it answers.
117pub trait Answer: Clone + Eq + core::fmt::Debug {
118    /// The roster this answer belongs to.
119    type Question: Question;
120
121    /// The question this answer answers.
122    #[must_use]
123    fn question(&self) -> Self::Question;
124
125    /// Append this answer's canonical bytes.
126    fn encode_into(&self, into: &mut Vec<u8>);
127
128    /// This answer rendered for a person.
129    ///
130    /// A projection: no identity, decision, or refusal reads one back.
131    #[must_use]
132    fn human(&self) -> String;
133}
134
135/// The question roster of a kind that owes nothing beyond the universal questions.
136///
137/// Uninhabited, so it is its own answer as well as its own roster: there is no value here to ask about or to answer for.
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
139pub enum NoQuestions {}
140
141/// The role roster of a kind that renders exactly one unit, at the declaration site.
142///
143/// Not a placeholder and not an absence: a kind whose rendering is one unit says so with a roster of one, and a kind whose one unit lands elsewhere declares its own.
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
145pub enum SoleRole {
146    /// The kind's one rendered unit.
147    Sole,
148}
149
150crate::roster! {
151    /// Where a rendered unit lands.
152    ///
153    /// Four deliveries, and a role names exactly one of them.
154    pub enum Destination {
155        /// The tokens the consumer's normal build compiles where the declaration stands.
156        DeclarationSite = "declaration-site",
157        /// The deferred cargo the consumer's test target invokes; the normal build compiles none of it.
158        TestCarrier = "test-carrier",
159        /// The deferred cargo the consumer's bench target invokes, on the same terms and through the same shell.
160        BenchCarrier = "bench-carrier",
161        /// A standalone artifact a publication step writes to its own address.
162        PublicationArtifact = "publication-artifact",
163    }
164}
165
166/// What happened to one kind that could have been generated.
167///
168/// Silence is not a variant: where a projection is absent, the absence has a name and cites the fact that caused it.
169/// There is no refused answer either, because a request that fails a step of the road is refused whole and produces a diagnostic rather than a set.
170#[must_use = "a disposition is what happened to a kind, and silence is not a variant"]
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
172pub enum Disposition {
173    /// It was generated, and this is the unit it produced.
174    Generated {
175        /// The generated unit's semantic key.
176        unit: Identity<GeneratedUnit>,
177    },
178    /// It does not apply here, and this is the fact that makes it inapplicable.
179    NotApplicable {
180        /// The fact the answer rests on.
181        because: OwnerFact,
182    },
183    /// Nobody asked for it, and this is the fact that says so.
184    NotRequested {
185        /// The fact the answer rests on.
186        because: OwnerFact,
187    },
188    /// The profile the request ran under does not offer it.
189    UnavailableUnderProfile {
190        /// The profile that does not offer it.
191        profile: Profile,
192        /// The fact naming what that profile could not furnish.
193        because: OwnerFact,
194    },
195}
196
197/// A consumer-owned record that can surrender its named dispositions in kind declaration order.
198///
199/// Implementations state rows, not completeness.
200/// [`DispositionSet::complete`] compares every surrendered name and the whole row count with the owning [`KindSet`] before the record can become the witness an account seats.
201pub trait DispositionRecord: Clone + Eq + core::fmt::Debug {
202    /// Surrender every stated kind name and disposition, in the set's declaration order.
203    fn into_dispositions(self) -> impl Iterator<Item = (&'static str, Disposition)>;
204}
205
206/// One declared set of kinds and the record from which its complete disposition witness is built.
207///
208/// The trait remains open, but naming a record here does not certify its completeness.
209/// Only [`DispositionSet::complete`] can turn the record into the private-field witness [`Accounted`](crate::Accounted) accepts.
210pub trait KindSet {
211    /// The consumer-owned disposition record for this set.
212    type Dispositions: DispositionRecord;
213
214    /// Every kind's declared name, in the order the set states them.
215    const NAMES: &'static [&'static str];
216}
217
218/// A disposition for every declared kind of one set, in declaration order.
219///
220/// The rows are private and the only public constructor checks every name and the complete row count against [`KindSet::NAMES`], so an omitted, doubled, foreign, or reordered seat cannot become this value and cannot be seated beside an expansion.
221#[must_use = "a complete disposition set is the witness an accounted expansion requires"]
222#[derive(Debug, Clone, PartialEq, Eq)]
223pub struct DispositionSet<Set: KindSet> {
224    dispositions: Vec<Disposition>,
225    kind_set: PhantomData<fn() -> Set>,
226}
227
228/// How a disposition record refuses to become a complete set witness.
229#[must_use = "a disposition-set refusal names the count or kind-name disagreement"]
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231pub enum DispositionSetError {
232    /// The record surrendered a different number of rows than the kind set declares.
233    CountMismatch {
234        /// How many kind names the set declares.
235        expected: usize,
236        /// How many disposition rows the record surrendered.
237        observed: usize,
238    },
239    /// One surrendered row names a kind other than the kind declared at that position.
240    KindMismatch {
241        /// The kind name the set declares at this position.
242        expected: &'static str,
243        /// The kind name the record surrendered at this position.
244        observed: &'static str,
245    },
246}