macroonz-compiler 0.1.0

Deterministic Rust code generation for procedural macros: plan, render, close, explain, and bind one sealed expansion from declared input.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
//! The kind home's declarations: the open semantic traits a consumer implements, the rosters the compiler owns, the disposition vocabulary, the complete-set witness, and the two stamps that write declarations down.
//!
//! Declarations only, with every road that reaches a private field in `type_guard.rs`, this file's own child.

use crate::identity::{GeneratedUnit, Identity, OwnerFact, Profile};
use core::marker::PhantomData;

#[path = "type_guard.rs"]
mod guard;

/// What one request produces.
///
/// 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.
/// Nothing seals this trait and nothing registers an implementation of it.
///
/// # Examples
///
/// ```rust
/// use macroonz_compiler::{Kind, NoQuestions, SoleRole};
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct GreetImpl;
///
/// impl Kind for GreetImpl {
///     const NAME: &'static str = "greet.impl";
///     type Content = ();
///     type Role = SoleRole;
///     type Question = NoQuestions;
/// }
///
/// assert_eq!(GreetImpl::NAME, "greet.impl");
/// ```
pub trait Kind: 'static {
    /// The name this kind is spelled by wherever a name is written down.
    ///
    /// Declared rather than read off the Rust spelling, so renaming the marker renames no identity.
    const NAME: &'static str;

    /// The facts a request of this kind carries beyond its captured tokens.
    ///
    /// Its canonical encoding is the content commitment's material, so changing any fact a renderer may read changes the commitment before a plan exists.
    type Content: CanonicalContent;

    /// The seats this kind's rendering fills.
    type Role: Role;

    /// The questions this kind owes beyond the universal ones.
    type Question: Question;
}

/// Kind-specific facts with one complete canonical encoding.
///
/// The encoding is semantic material rather than a rendering for a person.
/// A kind owns the implementation for its content, and the compiler frames the complete result before deriving the content commitment.
pub trait CanonicalContent: Clone + Eq + core::fmt::Debug {
    /// Append every fact this content carries in its declared order.
    fn encode_content_into(&self, into: &mut Vec<u8>);

    /// The complete canonical bytes of this content.
    #[must_use]
    fn canonical_content_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::new();
        self.encode_content_into(&mut bytes);
        bytes
    }
}

/// One seat a kind's rendering fills.
///
/// 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.
pub trait Role: Copy + Eq + core::fmt::Debug + 'static {
    /// The complete roster, in the order the kind states it.
    ///
    /// 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.
    const ALL: &'static [Self];

    /// This role's declared name.
    #[must_use]
    fn name(self) -> &'static str;

    /// Where the unit rendered under this role lands.
    ///
    /// A property of the seat, so two plans of one kind cannot disagree about which build compiles their units.
    #[must_use]
    fn destination(self) -> Destination;

    /// This role's position in the roster, which a rendered unit's transcript carries.
    ///
    /// A role the roster does not carry has no position and reads as the roster's length.
    #[must_use]
    fn slot(self) -> u16 {
        slot_in(Self::ALL, self)
    }
}

/// One question a kind owes an answer to, beyond the questions every kind owes.
pub trait Question: Copy + Eq + core::fmt::Debug + 'static {
    /// The complete roster, in the order the kind states it.
    const ALL: &'static [Self];

    /// The typed answer to a question of this roster.
    type Answer: Answer<Question = Self>;

    /// This question's declared name.
    #[must_use]
    fn name(self) -> &'static str;

    /// This question's position in the roster, which an explanation's preimage carries.
    #[must_use]
    fn slot(self) -> u16 {
        slot_in(Self::ALL, self)
    }
}

/// One typed answer, and the question it answers.
pub trait Answer: Clone + Eq + core::fmt::Debug {
    /// The roster this answer belongs to.
    type Question: Question;

    /// The question this answer answers.
    #[must_use]
    fn question(&self) -> Self::Question;

    /// Append this answer's canonical bytes.
    fn encode_into(&self, into: &mut Vec<u8>);

    /// This answer rendered for a person.
    ///
    /// A projection: no identity, decision, or refusal reads one back.
    #[must_use]
    fn human(&self) -> String;
}

/// The question roster of a kind that owes nothing beyond the universal questions.
///
/// 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.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NoQuestions {}

/// The role roster of a kind that renders exactly one unit, at the declaration site.
///
/// 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.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SoleRole {
    /// The kind's one rendered unit.
    Sole,
}

crate::roster! {
    /// Where a rendered unit lands.
    ///
    /// Four deliveries, and a role names exactly one of them.
    pub enum Destination {
        /// The tokens the consumer's normal build compiles where the declaration stands.
        DeclarationSite = "declaration-site",
        /// The deferred cargo the consumer's test target invokes; the normal build compiles none of it.
        TestCarrier = "test-carrier",
        /// The deferred cargo the consumer's bench target invokes, on the same terms and through the same shell.
        BenchCarrier = "bench-carrier",
        /// A standalone artifact a publication step writes to its own address.
        PublicationArtifact = "publication-artifact",
    }
}

/// What happened to one kind that could have been generated.
///
/// Silence is not a variant: where a projection is absent, the absence has a name and cites the fact that caused it.
/// 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.
#[must_use = "a disposition is what happened to a kind, and silence is not a variant"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Disposition {
    /// It was generated, and this is the unit it produced.
    Generated {
        /// The generated unit's semantic key.
        unit: Identity<GeneratedUnit>,
    },
    /// It does not apply here, and this is the fact that makes it inapplicable.
    NotApplicable {
        /// The fact the answer rests on.
        because: OwnerFact,
    },
    /// Nobody asked for it, and this is the fact that says so.
    NotRequested {
        /// The fact the answer rests on.
        because: OwnerFact,
    },
    /// The profile the request ran under does not offer it.
    UnavailableUnderProfile {
        /// The profile that does not offer it.
        profile: Profile,
        /// The fact naming what that profile could not furnish.
        because: OwnerFact,
    },
}

/// A consumer-owned record that can surrender its named dispositions in kind declaration order.
///
/// Implementations state rows, not completeness.
/// [`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.
pub trait DispositionRecord: Clone + Eq + core::fmt::Debug {
    /// Surrender every stated kind name and disposition, in the set's declaration order.
    fn into_dispositions(self) -> impl Iterator<Item = (&'static str, Disposition)>;
}

/// One declared set of kinds and the record from which its complete disposition witness is built.
///
/// The trait remains open, but naming a record here does not certify its completeness.
/// Only [`DispositionSet::complete`] can turn the record into the private-field witness [`Accounted`](crate::Accounted) accepts.
pub trait KindSet {
    /// The consumer-owned disposition record for this set.
    type Dispositions: DispositionRecord;

    /// Every kind's declared name, in the order the set states them.
    const NAMES: &'static [&'static str];
}

/// A disposition for every declared kind of one set, in declaration order.
///
/// 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.
#[must_use = "a complete disposition set is the witness an accounted expansion requires"]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DispositionSet<Set: KindSet> {
    dispositions: Vec<Disposition>,
    kind_set: PhantomData<fn() -> Set>,
}

/// How a disposition record refuses to become a complete set witness.
#[must_use = "a disposition-set refusal names the count or kind-name disagreement"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DispositionSetError {
    /// The record surrendered a different number of rows than the kind set declares.
    CountMismatch {
        /// How many kind names the set declares.
        expected: usize,
        /// How many disposition rows the record surrendered.
        observed: usize,
    },
    /// One surrendered row names a kind other than the kind declared at that position.
    KindMismatch {
        /// The kind name the set declares at this position.
        expected: &'static str,
        /// The kind name the record surrendered at this position.
        observed: &'static str,
    },
}

/// One row's position in its roster, or the roster's length where the roster does not carry it.
fn slot_in<T: Copy + Eq>(roster: &[T], row: T) -> u16 {
    let position = roster
        .iter()
        .position(|other| *other == row)
        .unwrap_or(roster.len());
    u16::try_from(position).unwrap_or(u16::MAX)
}

/// Declares one closed vocabulary: the enum, its complete roster, and one declared name per row.
///
/// For a list of names and nothing else: a role is written by hand instead, because a role also names a destination and an implementation says that better than a stamp with an extra column.
///
/// # Examples
///
/// ```rust
/// macroonz_compiler::roster! {
///     /// Which direction a codec covers.
///     pub enum Direction {
///         /// Typed value to canonical bytes.
///         Encode = "encode",
///         /// Canonical bytes to typed value.
///         Decode = "decode",
///     }
/// }
///
/// assert_eq!(Direction::ALL, &[Direction::Encode, Direction::Decode]);
/// assert_eq!(Direction::Decode.name(), "decode");
/// ```
#[macro_export]
macro_rules! roster {
    (
        $(#[$note:meta])*
        $vis:vis enum $name:ident {
            $( $(#[$row:meta])* $variant:ident = $declared:literal ),+ $(,)?
        }
    ) => {
        $(#[$note])*
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        $vis enum $name {
            $( $(#[$row])* $variant, )+
        }

        impl $name {
            /// The complete roster, in declaration order.
            $vis const ALL: &'static [Self] = &[$( Self::$variant ),+];

            /// This row's declared name.
            #[must_use]
            $vis const fn name(self) -> &'static str {
                match self {
                    $( Self::$variant => $declared, )+
                }
            }
        }
    };
}

/// Declares one set of kinds: a marker type and its [`Kind`] implementation per row, the enumerated set, its [`KindSet`] implementation, and its [`DispositionRecord`].
///
/// One declaration, so the marker, the set, and the record cannot drift apart.
/// A kind added to a declaration grows all three together and stops the compiler at every construction of the record until somebody says what happens to it.
/// The record then becomes a [`DispositionSet`] only after the compiler independently checks every surrendered name and the whole row count against the set's declaration.
///
/// The seat is the field name the record carries a row's answer under, declared beside the kind rather than composed from the marker's spelling, for the same reason the declared name beside it is: a field renamed by every refactor of a Rust identifier is a field nobody can rely on.
///
/// # Examples
///
/// ```rust
/// pub type Greeting = &'static str;
///
/// macroonz_compiler::kinds! {
///     set = GreetKinds;
///     dispositions = GreetDispositions;
///
///     /// Projects a declaration into the implementation that greets.
///     GreetImpl = "greet.impl", greet_impl => Greeting, SoleRole, NoQuestions;
/// }
///
/// use macroonz_compiler::{Disposition, DispositionSet, KindSet, NoQuestions, OwnerFact, SoleRole};
///
/// assert_eq!(<GreetKinds as KindSet>::NAMES, &["greet.impl"]);
/// assert_eq!(GreetKinds::GreetImpl.name(), "greet.impl");
///
/// let record = GreetDispositions {
///     greet_impl: Disposition::NotApplicable {
///         because: OwnerFact { home: "greet", name: "not-applicable" },
///     },
/// };
/// assert!(DispositionSet::<GreetKinds>::complete(record).is_ok());
/// ```
#[macro_export]
macro_rules! kinds {
    (
        set = $set:ident;
        dispositions = $record:ident;
        $(
            $(#[$note:meta])*
            $kind:ident = $declared:literal, $seat:ident => $content:ty, $role:ty, $question:ty
        );+ $(;)?
    ) => {
        $(
            $(#[$note])*
            #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
            pub struct $kind;

            impl $crate::kind::Kind for $kind {
                const NAME: &'static str = $declared;
                type Content = $content;
                type Role = $role;
                type Question = $question;
            }
        )+

        /// The kinds this set names, one row each.
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub enum $set {
            $( $(#[$note])* $kind ),+
        }

        impl $set {
            /// The complete set, in declaration order.
            pub const ALL: &'static [Self] = &[$( Self::$kind ),+];

            /// This row's kind's declared name, read off the kind itself.
            #[must_use]
            pub const fn name(self) -> &'static str {
                match self {
                    $( Self::$kind => <$kind as $crate::kind::Kind>::NAME ),+
                }
            }
        }

        impl $crate::kind::KindSet for $set {
            type Dispositions = $record;

            const NAMES: &'static [&'static str] =
                &[$( <$kind as $crate::kind::Kind>::NAME ),+];
        }

        /// What happened to every kind of the set: one required seat per row.
        #[must_use = "a disposition record is what happened to every kind of the set"]
        #[derive(Debug, Clone, PartialEq, Eq)]
        pub struct $record {
            $(
                #[doc = concat!("What happened to the `", $declared, "` kind.")]
                pub $seat: $crate::kind::Disposition
            ),+
        }

        impl $record {
            /// What happened to one kind of the set.
            ///
            /// Total: every row reads to exactly one seat, and a row admitted later stops the compiler here until somebody says which seat carries it.
            #[must_use]
            pub const fn under(&self, row: $set) -> &$crate::kind::Disposition {
                match row {
                    $( $set::$kind => &self.$seat ),+
                }
            }
        }

        impl $crate::kind::DispositionRecord for $record {
            fn into_dispositions(
                self,
            ) -> impl Iterator<Item = (&'static str, $crate::kind::Disposition)> {
                [$( (<$kind as $crate::kind::Kind>::NAME, self.$seat) ),+].into_iter()
            }
        }
    };
}