caixa_core/dialeto.rs
1//! `defcaixa` is spoken by two unrelated declarations. This module makes that
2//! a **typed fact** instead of an anonymous parse failure.
3//!
4//! # The finding
5//!
6//! Measured 2026-07-31 over the pleme-io org checkout (270 `caixa.lisp` /
7//! `*.caixa.lisp` files found with `rg --no-ignore`; a bare `rg` from the org
8//! root returns 0, which is how this stayed invisible), the corpus splits into
9//! two schemas that share zero required slots:
10//!
11//! * [`CaixaDialeto::Pacote`] — this crate's [`crate::Caixa`]. `:nome
12//! :versao :kind :deps :bibliotecas :exe :servicos` + the supervisor/mesh
13//! slots. It declares a **tatara-lisp package**: the thing `feira` resolves,
14//! builds, links and publishes.
15//! * [`CaixaDialeto::Molde`] — `:name :kind :ecosystem :package {…} :workflows
16//! […] :ci-config {…} :files […]`. It declares a **repo's generated
17//! surface**: which foreign ecosystem (rust / go / python / …), that
18//! ecosystem's own package metadata, the CI shims to emit, and byte-captured
19//! file bodies. Read by `pleme-doc-gen`, never by `feira`.
20//!
21//! `:package`, `:ecosystem`, `:supports` and `:profile` have no counterpart in
22//! [`crate::Caixa`] at all — the theory doc's own D4 note records the same
23//! thing: those manifests "are authored against a schema that does not exist in
24//! Rust". They are not two spellings of one declaration. They are two domains
25//! that collided on one word, because *caixa* names a box and both are boxes.
26//!
27//! # Why this is not a bug report about broken files
28//!
29//! The Molde-dialect files are not malformed. They are correct inputs to their
30//! own consumer, and nothing in the shipped `feira` reads them, so nothing is
31//! failing today. The hazard is **latent and certain**: any new declarative
32//! surface written against "a `.caixa.lisp` is a [`crate::Caixa`]" meets a
33//! corpus where that is false for the large majority of files, and gets a flat
34//! unknown-keyword rejection that reads as "this manifest is broken" rather
35//! than "this manifest is not yours".
36//!
37//! # What this module does about it
38//!
39//! [`classify`] is total: every `(defcaixa …)` form lands in exactly one
40//! [`CaixaDialeto`], including [`CaixaDialeto::Desconhecido`] for one that
41//! matches neither. [`crate::Caixa::from_lisp`] runs it first, so a foreign
42//! dialect is [`crate::ManifestError::DialetoEstrangeiro`] — an error that
43//! names the dialect it found and the consumer that speaks it — rather than an
44//! unknown-kwarg error indistinguishable from a typo.
45//!
46//! Tier-honest: this is **parse-time rejection with a named cause**, not
47//! unrepresentability. A caller that ignores the `Err` still gets nothing
48//! useful; what it can no longer do is mistake "wrong dialect" for "bad file".
49
50use tatara_lisp::{Atom, Sexp};
51
52/// Which `(defcaixa …)` declaration a source speaks.
53///
54/// The [`gen_platform::IsVariant`] derive emits per-arm arm-discriminator
55/// predicates (`is_pacote` / `is_molde` / `is_molde_posicional` /
56/// `is_desconhecido`) as substrate-side typed dispatches on the closed
57/// four-arm dialect-classification discriminator. Peer of the sibling
58/// closed-set fieldless typed enums' [`crate::CaixaKind`] /
59/// [`crate::supervisor::RestartStrategy`] /
60/// [`crate::supervisor::RestartPolicy`] /
61/// [`crate::aplicacao::PlacementStrategy`] /
62/// [`crate::aplicacao::RateLimitUnit`] /
63/// [`crate::dep::DepList`] `IsVariant` derives on the sibling
64/// closed-set typed-enum discriminator axes.
65///
66/// The pre-lift `is_molde_family` predicate hand-rolled its own
67/// `matches!(self, Self::Molde | Self::MoldePosicional)` two-arm literal
68/// with no compile-time link back to the closed set — post-lift it routes
69/// through `self.is_molde() || self.is_molde_posicional()` so a future
70/// arm rename (e.g. `Molde → MoldeKW` under an M4 vocabulary shift) trips
71/// exhaustively at every derive-generated predicate site rather than
72/// leaving the hand-rolled `matches!` silently drifting.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, gen_platform::IsVariant)]
74pub enum CaixaDialeto {
75 /// This crate's [`crate::Caixa`] — a tatara-lisp package manifest.
76 /// Keyword-argument form headed by `:nome`.
77 Pacote,
78 /// `pleme-doc-gen`'s repo-surface declaration, keyword-argument form
79 /// headed by `:name` (plus `:ecosystem` / `:package`).
80 Molde,
81 /// The same declaration as [`Self::Molde`], written with the package name
82 /// as a bare positional symbol — `(defcaixa todoku-go :kind :Biblioteca
83 /// :ecosystem :go …)`. `pleme-doc-gen`'s parser reads the first token
84 /// after the head as the name, so this is one arity of one declaration,
85 /// not a third schema.
86 MoldePosicional,
87 /// A `(defcaixa …)` form matching neither. Kept as a variant rather than
88 /// an error so [`classify`] is total and a census can COUNT the residue —
89 /// a classifier that threw here would report "0 unknown" by construction.
90 Desconhecido,
91}
92
93impl CaixaDialeto {
94 /// Exhaustive iteration surface for every consumer that walks the
95 /// closed four-arm [`CaixaDialeto`] discriminator set — the
96 /// [`feira dialeto`](../../caixa_feira/cmd/dialeto/index.html)
97 /// census counter's per-arm accept-set, a future
98 /// `feira dialeto --list-dialects` CLI listing of the accepted
99 /// classifications, a future M4 `mesh.pleme.io/v1alpha1/Manifesto`
100 /// CR materializer's admission-webhook rejection body naming the
101 /// accepted-dialect set, any future census-report shape probe that
102 /// sweeps every arm to compute per-arm coverage. A future arm
103 /// addition (a fifth dialect the [`crate::dialeto`] module doc's
104 /// "third dialect" hazard actualises — the module explicitly frames
105 /// its purpose as "what stops a third dialect appearing", and this
106 /// slice is the substrate-side answer: the arm-set is one edit and
107 /// every consumer picks up the new entry by construction) extends
108 /// this slice as one edit and every downstream consumer picks up
109 /// the new entry through the shared iteration; the compiler-checked
110 /// exhaustiveness on the sibling method `match` arms
111 /// ([`Self::palavra_canonica`] / [`Self::consumidor`] /
112 /// [`Self::descricao`] / [`std::fmt::Display`]) is the build-time
113 /// guarantee that no arm forgets to grow.
114 ///
115 /// Peer of the sibling closed-set typed enums'
116 /// [`crate::CaixaKind::ALL`] (6b1f4fb) /
117 /// [`crate::aplicacao::PlacementStrategy::ALL`] (18c7342) /
118 /// [`crate::aplicacao::RateLimitUnit::ALL`] (6bce03d) /
119 /// [`crate::dep::DepList::ALL`] (45ee563) /
120 /// [`crate::supervisor::RestartStrategy::ALL`] (4eec29c) /
121 /// [`crate::supervisor::RestartPolicy::ALL`] (dd32ccf)
122 /// exhaustive-iteration surfaces — the seventh closed-set typed
123 /// enum on the caixa surface to converge onto the same
124 /// one-canonical-arm-list-per-enum discipline, and the first
125 /// dialect-classification axis (as distinct from an OTP-shape M2
126 /// slot or an M3 mesh slot) to reach it. Order matches variant
127 /// declaration order verbatim (`Pacote` → `Molde` →
128 /// `MoldePosicional` → `Desconhecido`) so the slice is the
129 /// canonical ordering every listing / rendering consumer defers to.
130 pub const ALL: &'static [Self] = &[
131 Self::Pacote,
132 Self::Molde,
133 Self::MoldePosicional,
134 Self::Desconhecido,
135 ];
136
137 /// Substrate-canonical `PascalCase` variant-name byte-string every consumer
138 /// that formats the dialect as census-facing text lands on. Returns the
139 /// per-arm `PascalCase` name of the variant (`"Pacote"` / `"Molde"` /
140 /// `"MoldePosicional"` / `"Desconhecido"`) — the one canonical
141 /// byte-string the paired [`std::fmt::Display`] impl routes through so
142 /// every downstream consumer (the `feira dialeto` census counter output
143 /// line, a future `feira dialeto --list-dialects` CLI enumeration, a
144 /// future M4 `mesh.pleme.io/v1alpha1/Manifesto` CR materializer's
145 /// admission-webhook rejection body naming the accepted-dialect set)
146 /// reaches for the same substrate primitive rather than the pre-lift
147 /// hand-rolled four-arm literal-string match every [`std::fmt::Display`]
148 /// call previously routed through in place.
149 ///
150 /// Peer of the sibling closed-set typed enums'
151 /// [`crate::CaixaKind::as_str`] / [`crate::supervisor::RestartStrategy::as_str`]
152 /// / [`crate::supervisor::RestartPolicy::as_str`] /
153 /// [`crate::aplicacao::PlacementStrategy::as_str`] /
154 /// [`crate::dep::DepList::as_str`] projections on the sibling closed-set
155 /// typed-enum discriminator axes — the seventh (and last unlifted)
156 /// closed-set fieldless typed enum on the caixa surface to converge
157 /// onto the same one-canonical-byte-string-per-arm-through-`as_str`
158 /// discipline the six siblings already carry. Unlike [`crate::CaixaKind`]
159 /// (which carries two axes: `as_str` returning lowercase Portuguese
160 /// diagnostic form vs `wire_name` returning `PascalCase` tatara-lisp
161 /// author-surface bytes), [`CaixaDialeto`] is an internal
162 /// classification with no wire surface — the `PascalCase` variant name
163 /// is the census-facing form every consumer reads, so `as_str`
164 /// suffices without a paired `wire_name` axis.
165 #[must_use]
166 pub const fn as_str(self) -> &'static str {
167 match self {
168 Self::Pacote => "Pacote",
169 Self::Molde => "Molde",
170 Self::MoldePosicional => "MoldePosicional",
171 Self::Desconhecido => "Desconhecido",
172 }
173 }
174
175 /// Substrate-canonical reverse projection on the [`CaixaDialeto`]
176 /// closed-set dialect-classification axis — parses the `PascalCase`
177 /// variant-name byte-string back to the typed variant, or `None` when
178 /// `s` is outside the closed-set arm-string set [`Self::as_str`]
179 /// emits. Walks the same four `"Pacote"` / `"Molde"` /
180 /// `"MoldePosicional"` / `"Desconhecido"` byte-strings the sibling
181 /// [`Self::as_str`] emitter returns, so the parse and emit halves of
182 /// the round-trip migrate through one caixa-core edit on any future
183 /// arm addition (the module doc's "third dialect" hazard actualising
184 /// as a fifth arm) — the compiler-checked exhaustiveness on
185 /// [`Self::as_str`]'s `match self` arms and the round-trip pin
186 /// [`tests::caixa_dialeto_round_trips_through_as_str_and_from_wire`]
187 /// together lock the two halves mutually.
188 ///
189 /// Prior to this lift the substrate carried only the forward
190 /// `Self → &str` projection on the dialect-classification axis (the
191 /// [`Self::as_str`] emitter, the [`std::fmt::Display`] impl routed
192 /// through it, the [`AsRef<str>`] impl routed through it) — every
193 /// future consumer that wanted to promote the census-facing text back
194 /// to the typed enum (a future `feira dialeto --filter
195 /// <Pacote|Molde|MoldePosicional|Desconhecido>` CLI arg-parse that
196 /// binds the wire form into the typed enum before dispatching to the
197 /// per-arm counter, a future M4 `mesh.pleme.io/v1alpha1/Manifesto`
198 /// CR materializer's admission-time re-parse of the per-dialect
199 /// audit body, a future audit-report re-loader that binds a prior
200 /// [`Self::as_str`] output back to the typed enum for cross-run
201 /// comparison) would have had to re-inline a four-arm `match s`
202 /// cascade that expressed no compile-time link back to the typed
203 /// [`CaixaDialeto`] enum.
204 ///
205 /// Same closed-set-reverse-projection discipline the sibling
206 /// [`crate::CaixaKind::from_wire`] (2aa6d23) /
207 /// [`crate::supervisor::RestartStrategy::from_wire`] (4eec29c) /
208 /// [`crate::supervisor::RestartPolicy::from_wire`] (dd32ccf) /
209 /// [`crate::aplicacao::PlacementStrategy::from_wire`] (18c7342) /
210 /// [`crate::dep::DepList::from_wire`] (45ee563) typed enums carry on
211 /// the peer wire-side `str → Self` axes — extends the family onto
212 /// the seventh closed-set fieldless typed enum on the caixa surface
213 /// (the dialect-classification axis), matching the same
214 /// two-way `str ↔ Self` round-trip every sibling closed-set enum
215 /// already carries. Method-named `from_wire` (not `from_str`) to
216 /// match the peer shapes verbatim and side-step the derived
217 /// [`std::str::FromStr`] impls the sibling
218 /// [`gen_platform::FromStrKind`]-carrying axes install on their
219 /// kebab-case dispatcher-catalog identity. Returns `Option<Self>`
220 /// (rather than `Result<Self, _>`) to match the peer shapes: the
221 /// caller picks the diagnostic form appropriate for its use site.
222 #[must_use]
223 pub fn from_wire(s: &str) -> Option<Self> {
224 match s {
225 "Pacote" => Some(Self::Pacote),
226 "Molde" => Some(Self::Molde),
227 "MoldePosicional" => Some(Self::MoldePosicional),
228 "Desconhecido" => Some(Self::Desconhecido),
229 _ => None,
230 }
231 }
232
233 /// The keyword an author should write for this dialect, once the
234 /// migration named in [`Self::consumidor`] completes.
235 #[must_use]
236 pub const fn palavra_canonica(self) -> &'static str {
237 match self {
238 Self::Pacote => "defcaixa",
239 Self::Molde | Self::MoldePosicional => "defmolde",
240 Self::Desconhecido => "?",
241 }
242 }
243
244 /// Who reads this dialect.
245 #[must_use]
246 pub const fn consumidor(self) -> &'static str {
247 match self {
248 Self::Pacote => "caixa-core / feira",
249 Self::Molde | Self::MoldePosicional => "pleme-doc-gen",
250 Self::Desconhecido => "nobody known",
251 }
252 }
253
254 /// A one-line description for a census row or an error message.
255 #[must_use]
256 pub const fn descricao(self) -> &'static str {
257 match self {
258 Self::Pacote => "tatara-lisp package manifest (:nome :versao :kind :deps …)",
259 Self::Molde => "repo-surface declaration (:name :ecosystem :package {…} …)",
260 Self::MoldePosicional => {
261 "repo-surface declaration, positional name (defcaixa <nome> :kind …)"
262 }
263 Self::Desconhecido => "unrecognised — matches no known defcaixa schema",
264 }
265 }
266
267 /// True when this arm belongs to the `defmolde` declaration family —
268 /// the two-arity closure of [`Self::Molde`] and [`Self::MoldePosicional`]
269 /// under the shared `defmolde` head keyword the sibling
270 /// [`Self::palavra_canonica`] projection already collapses onto
271 /// `"defmolde"` for both arms (and the sibling [`Self::consumidor`]
272 /// projection collapses onto `"pleme-doc-gen"` for the same two arms).
273 /// False on [`Self::Pacote`] (the sibling `defcaixa` tatara-lisp
274 /// package manifest, [`Self::palavra_canonica`] `→ "defcaixa"`) and
275 /// on [`Self::Desconhecido`] (the residue that names no known
276 /// declaration, [`Self::palavra_canonica`] `→ "?"`).
277 ///
278 /// The [`Self::Molde`] / [`Self::MoldePosicional`] split is one
279 /// declaration written two ways ([`Self::MoldePosicional`]'s
280 /// variant-declaration docstring at [`Self::MoldePosicional`] frames
281 /// it exactly: "the same declaration as [`Self::Molde`], written with
282 /// the package name as a bare positional symbol … this is one arity
283 /// of one declaration, not a third schema"). Every downstream gate
284 /// that keys off "does this dialect belong to the `defmolde` family"
285 /// (as distinct from the four-arm-per-arm census-counter axis the
286 /// sibling `feira dialeto` verb already fans on separately at
287 /// `caixa-feira/src/cmd/dialeto.rs:110-127`) previously hand-rolled
288 /// the two-arm collapse inline as `matches!(d, CaixaDialeto::Molde |
289 /// CaixaDialeto::MoldePosicional)` — a compile-time-anonymous
290 /// two-arm literal set with no link back to the [`CaixaDialeto`]
291 /// variant declaration nor to the sibling
292 /// [`Self::palavra_canonica`] / [`Self::consumidor`] projections
293 /// that already carry the same two-arm collapse under the shared
294 /// `defmolde` / `pleme-doc-gen` axis. The `feira dialeto` verb's
295 /// [`caixa-feira/src/cmd/dialeto.rs`] carried the same
296 /// `matches!` twice — once in the `--strict-palavra` gate that
297 /// refuses a repo-surface declaration still written as
298 /// `(defcaixa …)`, once in the wrong-declaration-under-`caixa.lisp`
299 /// gate that refuses a repo-surface declaration under the filename
300 /// `feira` loads as a package manifest — with no compile-time link
301 /// between the two hand-rolled arm sets. A future arm addition (the
302 /// module doc's "third dialect" hazard actualises as a fifth arm
303 /// [`CaixaDialeto`] that belongs to the `defmolde` declaration
304 /// family — a third arity variant, an alias-declaration family
305 /// pleme-doc-gen sharpens as its schema evolves) would silently
306 /// split the two hand-rolled `matches!` arm-sets from each other
307 /// and from the paired [`Self::palavra_canonica`] projection: one
308 /// call site picks up the new arm, one does not, and the disagreement
309 /// surfaces far from the arm-addition commit as a `feira dialeto`
310 /// consumer reporting a repo-surface declaration under one gate but
311 /// not the other. Routing every "belongs to the `defmolde` family"
312 /// predicate through this one substrate primitive closes the axis:
313 /// a future arm addition lands one match arm here (a compile-time
314 /// exhaustiveness error otherwise), not a coordinated per-`matches!`
315 /// rewrite across every caller.
316 ///
317 /// Peer of the sibling [`crate::CaixaKind::requires_lib`] (0421c22)
318 /// per-arm-set predicate on the [`crate::CaixaKind`] closed-set
319 /// discriminator's "kind requires a `lib/` surface" axis — extends
320 /// the same "one canonical typed predicate per per-arm-set gate,
321 /// one dispatch on the substrate primitive" discipline onto the
322 /// [`CaixaDialeto`] closed-set discriminator's "belongs to the
323 /// `defmolde` declaration family" axis. The dialect-classification
324 /// axis's second per-arm-set predicate (first being the implicit
325 /// palavra_canonica-through-consumidor-through-descricao arm-set
326 /// collapse already carried on the sibling projections) — the first
327 /// explicitly-typed per-arm-set predicate on the axis, matching the
328 /// discipline the sibling M2 [`crate::CaixaKind`] closed-set
329 /// discriminator already carries with `requires_lib`.
330 ///
331 /// Three consumers now route through this one typed dispatch: the
332 /// [`caixa-feira`](../../caixa_feira/cmd/dialeto/index.html) verb's
333 /// `--strict-palavra` gate (refusing a repo-surface declaration
334 /// still written as `(defcaixa …)`), the same verb's wrong-
335 /// declaration-under-`caixa.lisp` gate (refusing a repo-surface
336 /// declaration under the filename `feira` loads as a package
337 /// manifest), and [`crate::Caixa::from_lisp`]'s foreign-dialect
338 /// gate (raising [`crate::ManifestError::DialetoEstrangeiro`] before
339 /// the derive's `parse_kwargs_strict` walk on any `defmolde`-family
340 /// classification — the pre-lift hand-rolled three-arm
341 /// `match { Pacote => {}, Desconhecido => {}, foreign => Err(…) }`
342 /// literal whose `foreign =>` wildcard silently absorbed anything
343 /// non-Pacote-non-Desconhecido, now the third external consumer of
344 /// the `defmolde`-family partition).
345 #[must_use]
346 pub const fn is_molde_family(self) -> bool {
347 // Routed through the derive-generated per-arm predicates
348 // [`Self::is_molde`] + [`Self::is_molde_posicional`] so the
349 // two-arm collapse links compile-time back to the closed-set
350 // typed dispatch every peer arm-set predicate on the caixa
351 // surface (e.g. [`crate::CaixaKind::requires_lib`] on the
352 // sibling `:kind` axis) now carries. Byte-equivalent to the
353 // pre-lift `matches!(self, Self::Molde | Self::MoldePosicional)`
354 // form (the derived `is_*` predicates each expand to the same
355 // `matches!(self, Self::X)` shape by construction), but a
356 // future arm rename or IsVariant `#[is_variant(name = "…")]`
357 // override lands at exactly one dispatch on the substrate
358 // primitive rather than a hand-rolled two-arm literal.
359 self.is_molde() || self.is_molde_posicional()
360 }
361}
362
363/// [`std::fmt::Display`] routed through [`CaixaDialeto::as_str`], so the
364/// pretty-printed byte-string every consumer that formats the dialect as
365/// user-facing / census text lands on (the `feira dialeto` per-manifest
366/// `--list` row, the `feira dialeto` census summary line's per-arm
367/// counters, a future M4 admission-webhook's rejection body naming the
368/// accepted-dialect set) reaches for the same `PascalCase` per-arm
369/// byte-string the [`CaixaDialeto::as_str`] helper returns.
370///
371/// Prior to this lift the [`std::fmt::Display`] impl hand-rolled its own
372/// four-arm literal-string match — the one hand-rolled per-arm dispatch
373/// on the closed [`CaixaDialeto`] discriminator that had NO substrate
374/// primitive accessor to defer to (the sibling [`CaixaDialeto::palavra_canonica`] /
375/// [`CaixaDialeto::consumidor`] / [`CaixaDialeto::descricao`] projections
376/// carry distinct byte-shapes per axis, so none of them could serve as
377/// the Display source). A future variant addition (a fifth dialect the
378/// module doc's "third dialect" hazard actualises) would land one arm at
379/// the enum and per-arm returns at the paired accessors, but a hand-rolled
380/// [`std::fmt::Display`] match would silently drop the new arm to compile-
381/// fail-at-the-match-arm-site rather than through the shared substrate
382/// primitive. Routing [`std::fmt::Display`] through [`CaixaDialeto::as_str`]
383/// closes the last unlifted per-arm `PascalCase`-name projection on the
384/// caixa surface — the seventh (and last unlifted) closed-set fieldless
385/// typed enum on the caixa surface to converge onto the same
386/// `Display`-through-`as_str` discipline the six siblings
387/// ([`crate::CaixaKind`] / [`crate::supervisor::RestartStrategy`] /
388/// [`crate::supervisor::RestartPolicy`] /
389/// [`crate::aplicacao::PlacementStrategy`] / [`crate::aplicacao::RateLimitUnit`]
390/// / [`crate::dep::DepList`]) already carry.
391impl std::fmt::Display for CaixaDialeto {
392 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393 f.write_str(self.as_str())
394 }
395}
396
397/// Substrate-canonical [`AsRef<str>`] projection on the [`CaixaDialeto`]
398/// closed-set fieldless typed dialect-classification enum — routes through
399/// the same [`CaixaDialeto::as_str`] `pub const fn` scalar accessor the
400/// paired [`std::fmt::Display`] impl already delegates through, so any
401/// future consumer that binds a [`CaixaDialeto`] through the standard-
402/// library `impl AsRef<str>` bound (a [`std::process::Command::arg`]
403/// shell-out that composes the canonical `PascalCase` variant-name into a
404/// `feira dialeto --strict-palavra <Pacote|Molde|MoldePosicional|Desconhecido>`
405/// diagnostic overlay, a `tracing::field::Value::Str`-arm structured-log
406/// recorder on the [`crate::Caixa::from_lisp`] foreign-dialect
407/// [`crate::ManifestError::DialetoEstrangeiro`] refusal path, a
408/// [`std::collections::HashMap`] lookup keyed on the canonical name
409/// through `map.get::<str>(dialeto.as_ref())` on a future M4 admission-
410/// webhook's per-dialect rejection-body composition table) reaches the
411/// paired `"Pacote"` / `"Molde"` / `"MoldePosicional"` / `"Desconhecido"`
412/// byte-string through one substrate-primitive dispatch rather than an
413/// open-coded `.as_str()` re-inlining at every wire-up.
414///
415/// Same "route the trait impl through the substrate-primitive accessor"
416/// discipline the sibling [`crate::CaixaVersion`] [`AsRef<str>`] impl
417/// (16d5c7e), the paired M2 [`crate::supervisor::RestartStrategy`]
418/// [`AsRef<str>`] impl (63eb1a4), the paired M2
419/// [`crate::supervisor::RestartPolicy`] [`AsRef<str>`] impl (419ea81),
420/// the M3 [`crate::aplicacao::PlacementStrategy`] [`AsRef<str>`] impl
421/// (d86edd2), the M3 [`crate::aplicacao::RateLimitUnit`] [`AsRef<str>`]
422/// impl (d8136db), and the top-level [`crate::CaixaKind`] [`AsRef<str>`]
423/// impl (cd2091f) carry — extends the substrate primitive's
424/// [`AsRef<str>`] projection axis onto the seventh closed-set typed enum
425/// on the caixa surface: the dialect-classification axis previously
426/// carried [`fmt::Display`]-through-`as_str` but not yet the paired
427/// [`AsRef<str>`] impl, so a downstream consumer that bound the enum
428/// through the standard-library `AsRef<str>` trait had to reach the
429/// canonical byte-string through an open-coded `.as_str()` call rather
430/// than the trait-idiomatic `.as_ref()` the peer closed-set typed enums
431/// already admit.
432///
433/// Pinned load-bearing by
434/// [`tests::caixa_dialeto_as_ref_str_routes_through_as_str_accessor`]
435/// (byte-parity pin against [`CaixaDialeto::as_str`] across the four-arm
436/// closed set) and
437/// [`tests::caixa_dialeto_as_ref_str_routes_through_display_via_shared_accessor`]
438/// (three-path convergence: `AsRef<str>` + `Display` + `as_str` all
439/// resolve to the same byte-string per arm) — any future silent detour
440/// that routes the impl through a divergent projection (a per-arm inline
441/// `match self { CaixaDialeto::Pacote => "Pacote", … }` re-inlining that
442/// opens a compile-time link to the un-lifted arm-literal, a swap onto
443/// the second-axis [`CaixaDialeto::palavra_canonica`] /
444/// [`CaixaDialeto::consumidor`] / [`CaixaDialeto::descricao`] accessors
445/// that carry distinct byte-shapes per axis) trips at caixa-core test
446/// time under `assert_eq!` rather than at a downstream
447/// `impl AsRef<str>`-bound consumer's silent split.
448impl AsRef<str> for CaixaDialeto {
449 fn as_ref(&self) -> &str {
450 self.as_str()
451 }
452}
453
454/// A source that is not a `(defcaixa …)` / `(defmolde …)` form at all.
455#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
456pub enum DialetoError {
457 #[error("source has no top-level form")]
458 Vazio,
459 #[error("top-level form is not a list — a manifest is `(defcaixa …)`")]
460 NaoEhLista,
461 #[error(
462 "top-level form is headed by `{encontrado}`, not `defcaixa` or `defmolde` \
463 (a manifest's first form must be the declaration itself)"
464 )]
465 CabecaErrada { encontrado: String },
466 #[error("manifest does not parse as tatara-lisp: {0}")]
467 Leitura(String),
468}
469
470impl DialetoError {
471 /// Construct a [`DialetoError::CabecaErrada`] naming the offending
472 /// head symbol found at the top-level form.
473 ///
474 /// Substrate primitive every [`classify_form`] wrong-head fallthrough
475 /// wire-up site now routes through, folding the pre-lift uniform
476 /// three-line `Self::CabecaErrada { encontrado: <head>.to_string() }`
477 /// one-field struct-literal onto one substrate primitive matching the
478 /// peer `LimitsError::unknown_byte_unit(unit: &str)` /
479 /// `LimitsError::unknown_duration_unit(unit: &str)`
480 /// (`limits_codec_unit_only_ctors!` — 29fac09) single-slot
481 /// discipline on the sibling one-field `{ <field>: String }` envelope
482 /// axis, and matching the peer `ManifestError::code_path_empty` /
483 /// `BehaviorError::empty_path` / `UpgradeError::duplicate_from` /
484 /// `AplicacaoError::placement_cluster_duplicate` (94dabc8 / 0e33b37 /
485 /// 7e52aec / 92b1c92) single-slot inherent-ctor discipline every
486 /// sibling `{ <field>: <T> }` error-envelope variant on caixa-core's
487 /// error surface now carries.
488 ///
489 /// The one open-coded wire-up site — `classify_form`'s wrong-head
490 /// fallthrough arm on the `head: &str` binding read from the
491 /// top-level form via [`tatara_lisp::Sexp::as_symbol`] — opened the
492 /// identical three-line
493 /// `Self::CabecaErrada { encontrado: <head>.to_string() }` block
494 /// against the codec-scoped `<head>: &str` binding. Now routes
495 /// through `DialetoError::cabeca_errada(head)`, byte-equal to the
496 /// pre-lift struct-literal on the same `&str` fixture, so any future
497 /// widening of the diagnostic shape (e.g. carrying the source-file
498 /// path alongside the head symbol, carrying the head symbol's
499 /// position offset for an authoring-surface caret pointer) lands at
500 /// exactly one dispatch on the substrate primitive rather than re-
501 /// inlining the struct-literal at every wrong-head fallthrough
502 /// consumer.
503 #[must_use]
504 pub fn cabeca_errada(encontrado: &str) -> Self {
505 Self::CabecaErrada {
506 encontrado: encontrado.to_string(),
507 }
508 }
509
510 /// Construct a [`DialetoError::Leitura`] carrying the offending
511 /// tatara-lisp reader-error message `reason` verbatim in the
512 /// variant's tuple-newtype payload.
513 ///
514 /// Substrate primitive every [`classify`] tatara-lisp-reader
515 /// map-err wire-up site now routes through, folding the pre-lift
516 /// uniform `Self::Leitura(<into-String-expr>)` tuple-newtype
517 /// construction onto one substrate primitive matching the peer
518 /// `LimitsError::empty_byte_size` / `LimitsError::empty_duration`
519 /// (7a4b003 / 319216c) `(String)` single-slot tuple-newtype
520 /// discipline on the sibling
521 /// [`crate::limits::LimitsError`] envelope's empty-shape axis of
522 /// the paired codec-magnitude family. Peer to the sibling
523 /// [`DialetoError::cabeca_errada`] ctor on the same envelope's
524 /// wrong-head axis but on the tatara-lisp-reader axis rather than
525 /// the classifier-fallthrough axis. Closes the last un-lifted
526 /// variant on [`DialetoError`] — every one of the sole wire-up
527 /// sites (the [`classify`] tatara-lisp-reader `.map_err(|e|
528 /// Self::Leitura(e.to_string()))` arm) opened the identical
529 /// `DialetoError::Leitura(<into-String-expr>)` block against the
530 /// codec-scoped `String` (`e.to_string()`) binding, so the fold
531 /// routes the site through one dispatch on a uniform
532 /// `impl Into<String>` param, byte-equal to the pre-lift
533 /// tuple-newtype construction on the same argument.
534 ///
535 /// The `impl Into<String>` bound covers both wire-up shapes on
536 /// [`classify`] — a `String` binding (`e.to_string()` on the
537 /// [`tatara_lisp::Error`]-carrying `e` binding) and a `&str`
538 /// binding (a future admission-webhook consumer probing a
539 /// caller-scoped `&'static str` fixture, a future
540 /// `feira lint --tatara-reader-round-trip` verb sweeping every
541 /// `tatara_lisp::read` return through the same shape gate) —
542 /// without forcing the caller to spell the conversion at the
543 /// wire-up site. Same shape the peer
544 /// [`crate::limits::LimitsError::empty_byte_size`] /
545 /// [`crate::limits::LimitsError::empty_duration`] /
546 /// [`crate::limits::LimitsError::bad_millicores`] /
547 /// [`crate::limits::LimitsError::bad_byte_magnitude`] /
548 /// [`crate::limits::LimitsError::bad_duration_magnitude`] folds
549 /// carry on the peer bad-magnitude and empty-shape axes of the
550 /// same paired `(String)` tuple-newtype codec-magnitude family.
551 /// `#[must_use]` fires a compile warning at any wire-up that
552 /// mistakenly discards the constructed error.
553 ///
554 /// Every future consumer that wants to construct this variant
555 /// outside [`classify`] (a deferred `feira lint --tatara-reader-
556 /// round-trip` per-caixa admission verb probing each authored
557 /// manifest against the tatara-lisp-reader shape gate, an M4
558 /// typed `mesh.pleme.io/v1alpha1/Servico` CR materializer's
559 /// per-manifest admission validator re-checking one edited
560 /// `caixa.lisp` against the reader floor, a per-`caixa.lisp`
561 /// value-shape pre-emitter probing each declared manifest ahead
562 /// of the operator's admit-cycle) now reaches the variant
563 /// through one call rather than re-inlining the tuple-newtype
564 /// block in lockstep with the pre-existing wire-up.
565 #[must_use]
566 pub fn leitura(reason: impl Into<String>) -> Self {
567 Self::Leitura(reason.into())
568 }
569}
570
571/// Classify a manifest source without committing to either schema.
572///
573/// Deliberately reads only the head symbol and the set of top-level keywords —
574/// enough to route, never enough to half-parse. A classifier that started
575/// validating would grow into a third parser, which is the shape of the problem
576/// it exists to name.
577///
578/// # Errors
579/// [`DialetoError`] when the source is not a manifest declaration at all.
580pub fn classify(src: &str) -> Result<CaixaDialeto, DialetoError> {
581 let forms = tatara_lisp::read(src).map_err(|e| DialetoError::leitura(e.to_string()))?;
582 let first = forms.first().ok_or(DialetoError::Vazio)?;
583 classify_form(first)
584}
585
586/// [`classify`] over an already-read form.
587///
588/// # Errors
589/// [`DialetoError`] when the form is not a manifest declaration.
590pub fn classify_form(form: &Sexp) -> Result<CaixaDialeto, DialetoError> {
591 let list = form.as_list().ok_or(DialetoError::NaoEhLista)?;
592 let head = list
593 .first()
594 .and_then(Sexp::as_symbol)
595 .ok_or(DialetoError::NaoEhLista)?;
596
597 match head {
598 // `defmolde` is unambiguous by construction — it exists precisely so a
599 // consumer never has to infer which declaration it holds. Both arities
600 // are the same declaration; the positional one keeps its own variant
601 // only so a census can report the split.
602 "defmolde" => {
603 return Ok(if starts_with_positional_name(&list[1..]) {
604 CaixaDialeto::MoldePosicional
605 } else {
606 CaixaDialeto::Molde
607 });
608 }
609 "defcaixa" => {}
610 other => {
611 return Err(DialetoError::cabeca_errada(other));
612 }
613 }
614
615 let args = &list[1..];
616
617 // `(defcaixa <symbol> :kind … :ecosystem …)`. Only the Molde dialect has a
618 // positional arity; `Caixa` is keyword-only, so a leading bare symbol
619 // settles it without looking further.
620 if starts_with_positional_name(args) {
621 return Ok(CaixaDialeto::MoldePosicional);
622 }
623
624 let keys = top_level_keywords(args);
625 let has = |k: &str| keys.iter().any(|s| s == k);
626
627 // Order matters, and it is not arbitrary: `:nome` and `:name` are the two
628 // required head slots and no file in the measured corpus carries both.
629 // Checking them FIRST means the decision rests on the one slot each schema
630 // makes mandatory, rather than on optional evidence like `:ecosystem`.
631 if has("nome") {
632 return Ok(CaixaDialeto::Pacote);
633 }
634 if has("name") || has("ecosystem") || has("package") {
635 return Ok(CaixaDialeto::Molde);
636 }
637 Ok(CaixaDialeto::Desconhecido)
638}
639
640/// True when the first argument is a bare symbol rather than a keyword — the
641/// positional-name arity.
642fn starts_with_positional_name(args: &[Sexp]) -> bool {
643 matches!(args.first(), Some(Sexp::Atom(Atom::Symbol(_))))
644}
645
646/// The top-level keyword names (without the leading `:`) of a kwarg list.
647///
648/// Steps in pairs so a keyword appearing as a VALUE — `:kind :Biblioteca`, or a
649/// nested `(:nome "dep" :versao "^0.1")` inside `:deps` — is never counted as a
650/// top-level slot. A naive scan for `:nome` anywhere in the source classifies
651/// every Molde manifest with a `:deps` list as a Pacote.
652fn top_level_keywords(args: &[Sexp]) -> Vec<String> {
653 let mut out = Vec::new();
654 let mut i = 0;
655 while i < args.len() {
656 if let Sexp::Atom(Atom::Keyword(k)) = &args[i] {
657 out.push(k.clone());
658 i += 2;
659 } else {
660 i += 1;
661 }
662 }
663 out
664}
665
666#[cfg(test)]
667mod tests {
668 use super::*;
669
670 const PACOTE: &str = r#"
671 (defcaixa
672 :nome "checkout"
673 :versao "0.1.0"
674 :kind Servico
675 :deps ((:nome "caixa-teia" :versao "^0.1")))
676 "#;
677
678 const MOLDE: &str = r#"
679 (defcaixa
680 :name "base64"
681 :kind :Biblioteca
682 :ecosystem :rust-single-crate
683 :package {:name "base64" :version "0.22.1"}
684 :workflows [:auto-release])
685 "#;
686
687 const MOLDE_POSICIONAL: &str = r#"
688 (defcaixa todoku-go
689 :kind :Biblioteca
690 :ecosystem :go
691 :package {:name "todoku-go" :version "0.3.0"})
692 "#;
693
694 #[test]
695 fn the_package_dialect_is_recognised() {
696 assert_eq!(classify(PACOTE), Ok(CaixaDialeto::Pacote));
697 }
698
699 #[test]
700 fn the_repo_surface_dialect_is_recognised() {
701 assert_eq!(classify(MOLDE), Ok(CaixaDialeto::Molde));
702 }
703
704 #[test]
705 fn the_positional_arity_is_recognised() {
706 assert_eq!(
707 classify(MOLDE_POSICIONAL),
708 Ok(CaixaDialeto::MoldePosicional)
709 );
710 }
711
712 #[test]
713 fn defmolde_classifies_without_inference() {
714 // The whole point of the new keyword: no schema sniffing required.
715 let src = r#"(defmolde :name "x" :kind :Biblioteca :ecosystem :go)"#;
716 assert_eq!(classify(src), Ok(CaixaDialeto::Molde));
717 let pos = r"(defmolde todoku-go :kind :Biblioteca :ecosystem :go)";
718 assert_eq!(classify(pos), Ok(CaixaDialeto::MoldePosicional));
719 }
720
721 #[test]
722 fn a_nested_nome_does_not_make_a_repo_surface_look_like_a_package() {
723 // The exact failure a substring scan produces: `:deps ((:nome …))`
724 // contains `:nome`, but not as a top-level slot.
725 let src = r#"
726 (defcaixa
727 :name "x"
728 :ecosystem :rust-single-crate
729 :deps ((:nome "inner" :versao "^0.1")))
730 "#;
731 assert_eq!(classify(src), Ok(CaixaDialeto::Molde));
732 }
733
734 #[test]
735 fn a_keyword_in_value_position_is_not_a_slot() {
736 // `:kind :Biblioteca` — the value is itself a keyword. Stepping one at
737 // a time would read `:Biblioteca` as a top-level slot.
738 let src = r#"(defcaixa :kind :Biblioteca :name "x")"#;
739 assert_eq!(classify(src), Ok(CaixaDialeto::Molde));
740 }
741
742 #[test]
743 fn an_unrecognised_defcaixa_is_reported_not_guessed() {
744 let src = r#"(defcaixa :licenca "MIT")"#;
745 assert_eq!(classify(src), Ok(CaixaDialeto::Desconhecido));
746 }
747
748 #[test]
749 fn a_form_that_is_not_a_manifest_is_an_error_not_a_dialect() {
750 assert_eq!(
751 classify("(defflake :nome \"x\")"),
752 Err(DialetoError::cabeca_errada("defflake"))
753 );
754 assert_eq!(classify(""), Err(DialetoError::Vazio));
755 }
756
757 #[test]
758 fn every_dialect_names_its_consumer_and_its_canonical_keyword() {
759 // Guards the routing table itself: a new variant added without an arm
760 // here is a compile error in the match, and a variant that claims
761 // `defcaixa` while being read by pleme-doc-gen would re-open the
762 // collision this module closes. Sweeps [`CaixaDialeto::ALL`] rather
763 // than the pre-lift open-coded four-arm literal list — a future arm
764 // addition extends the slice as one edit and this pin picks it up
765 // by construction.
766 for &d in CaixaDialeto::ALL {
767 assert!(!d.descricao().is_empty(), "{d}");
768 assert!(!d.consumidor().is_empty(), "{d}");
769 }
770 assert_eq!(CaixaDialeto::Pacote.palavra_canonica(), "defcaixa");
771 assert_eq!(CaixaDialeto::Molde.palavra_canonica(), "defmolde");
772 assert_ne!(
773 CaixaDialeto::Pacote.palavra_canonica(),
774 CaixaDialeto::Molde.palavra_canonica(),
775 "the two dialects must not share a canonical keyword — that IS the defect"
776 );
777 }
778
779 #[test]
780 fn caixa_dialeto_all_enumerates_every_variant_exactly_once() {
781 // Three-legged exhaustiveness pin, peer of the sibling
782 // `caixa_kind_all_enumerates_every_variant_exactly_once`
783 // (caixa-core/src/kind.rs) /
784 // `restart_strategy_all_enumerates_every_variant_exactly_once`
785 // (caixa-core/src/supervisor.rs) shape.
786 //
787 // 1. arm-count invariant: `ALL.len()` matches the declared arm
788 // count (four — a fifth arm added without extending `ALL`
789 // fails this pin at caixa-core test time);
790 // 2. pairwise-distinctness invariant: every variant appears at
791 // most once in the slice (a duplicate arm would silently
792 // double-count in the census consumer, so the pin rejects
793 // duplicates outright);
794 // 3. coverage invariant: every literal `CaixaDialeto::X` is in
795 // the slice (the compiler-checked exhaustiveness on the peer
796 // per-arm `match self` in the accessors keeps the enum arm
797 // set and the `ALL` slice mutually aligned).
798 assert_eq!(
799 CaixaDialeto::ALL.len(),
800 4,
801 "ALL must list every arm exactly once; a fifth arm added \
802 without extending ALL fails this pin — extend ALL alongside \
803 the new variant"
804 );
805
806 let mut seen: Vec<CaixaDialeto> = Vec::new();
807 for &d in CaixaDialeto::ALL {
808 assert!(
809 !seen.contains(&d),
810 "ALL contains a duplicate arm: {d}. Every variant appears \
811 exactly once — a duplicate would double-count in every \
812 iteration consumer"
813 );
814 seen.push(d);
815 }
816
817 // Coverage: exhaustively assert every literal variant is somewhere
818 // in the slice. Written as an exhaustive `match` so a future arm
819 // addition fails to compile here (missing match arm) until the
820 // corresponding `assert` is added — the compiler enforces the pin's
821 // completeness rather than a hand-maintained variant list.
822 for variant in [
823 CaixaDialeto::Pacote,
824 CaixaDialeto::Molde,
825 CaixaDialeto::MoldePosicional,
826 CaixaDialeto::Desconhecido,
827 ] {
828 let coverage_probe = match variant {
829 CaixaDialeto::Pacote
830 | CaixaDialeto::Molde
831 | CaixaDialeto::MoldePosicional
832 | CaixaDialeto::Desconhecido => variant,
833 };
834 assert!(
835 CaixaDialeto::ALL.contains(&coverage_probe),
836 "ALL is missing variant {coverage_probe} — extend the slice"
837 );
838 }
839 }
840
841 #[test]
842 fn caixa_dialeto_all_is_const_and_matches_iteration_count() {
843 // Pins the const-ness of the slice at const-fold time. A future
844 // change that promoted `ALL` to a non-const initializer (a lazy-
845 // static, a runtime-computed Vec) would fail to compile here —
846 // the pin locks in the compile-time-known iteration surface
847 // every consumer builds against. Peer of the sibling
848 // `caixa_kind_all_is_const_and_matches_iteration_count` (kind.rs)
849 // / `restart_strategy_all_is_const_and_matches_iteration_count`
850 // (supervisor.rs) shape.
851 const ALL: &[CaixaDialeto] = CaixaDialeto::ALL;
852 assert_eq!(ALL.len(), CaixaDialeto::ALL.len());
853 // Sweep the iterator without collapsing to `.len()` so a future
854 // change to `ALL`'s carrier that decouples `.len()` from the
855 // iteration count (a lazy-computed shape, an alias `impl Iterator`
856 // return, a wrapper newtype) still passes here iff the two agree
857 // arm-for-arm; the `#[allow]` opts this local pin out of the
858 // clippy `iter_count` collapse that would defeat the intent.
859 #[allow(clippy::iter_count)]
860 let iterated = ALL.iter().count();
861 assert_eq!(iterated, CaixaDialeto::ALL.len());
862 }
863
864 #[test]
865 fn caixa_dialeto_all_covers_every_variant_by_display_probe() {
866 // Fanning `Display` over the slice sweeps the paired accessors
867 // ([`CaixaDialeto::palavra_canonica`] / [`CaixaDialeto::consumidor`]
868 // / [`CaixaDialeto::descricao`]) at every arm — every returned
869 // byte-string is non-empty (the accessors' contract). A future
870 // arm added without extending its per-arm `match self` return
871 // would compile-fail at the accessor call inside the loop;
872 // together with the `ALL.len() == 4` pin above, this locks the
873 // accessor arm-set and the `ALL` slice mutually.
874 for &d in CaixaDialeto::ALL {
875 let display_form = d.to_string();
876 assert!(
877 !display_form.is_empty(),
878 "Display must render a non-empty byte-string for every \
879 arm; empty: {d:?}"
880 );
881 // Consumidor / descricao / palavra-canonica must each surface
882 // a non-empty scalar; every downstream diagnostic consumer
883 // reaches through these accessors.
884 assert!(!d.palavra_canonica().is_empty(), "{d}");
885 assert!(!d.consumidor().is_empty(), "{d}");
886 assert!(!d.descricao().is_empty(), "{d}");
887 }
888 }
889
890 #[test]
891 fn caixa_dialeto_as_str_returns_pascal_case_variant_name() {
892 // Fail-before-pass-after per-arm shape pin: the four
893 // [`CaixaDialeto::as_str`] arms must return the canonical
894 // `PascalCase` byte-string that names the variant. Pre-lift this
895 // byte-string existed only inside the hand-rolled Display impl's
896 // four-arm literal-string match — every consumer that wanted the
897 // `PascalCase` name reached through `format!("{d}")`'s allocation
898 // path. Pinning the four arms explicitly here refuses a future
899 // regression that ever reroutes an arm to a distinct spelling
900 // (`"pacote"` lowercase, `"MoldePositional"` English rebrand,
901 // `"Unknown"` for `Desconhecido`) — the census output and the
902 // typed accessor would silently disagree until a downstream
903 // consumer surfaced the drift at census time. Peer of the sibling
904 // [`crate::supervisor::tests::restart_strategy_variants_serialize_to_lifted_scalar_values`]
905 // / `placement_strategy_variants_serialize_to_lifted_scalar_values`
906 // / `caixa_kind_as_str_returns_lifted_peer_const` shape on the
907 // sibling closed-set typed-enum discriminator axes — the seventh
908 // (and last unlifted) closed-set typed enum on the caixa surface
909 // to converge onto the same per-arm-shape-pin discipline.
910 for (variant, expected) in [
911 (CaixaDialeto::Pacote, "Pacote"),
912 (CaixaDialeto::Molde, "Molde"),
913 (CaixaDialeto::MoldePosicional, "MoldePosicional"),
914 (CaixaDialeto::Desconhecido, "Desconhecido"),
915 ] {
916 assert_eq!(
917 variant.as_str(),
918 expected,
919 "CaixaDialeto::{variant:?}.as_str() must return the \
920 canonical `PascalCase` variant-name byte-string; drift here \
921 splits the census-facing text from the substrate \
922 primitive every downstream consumer will read"
923 );
924 }
925 }
926
927 #[test]
928 fn caixa_dialeto_display_routes_through_as_str_helper() {
929 // Fail-before-pass-after convergence pin: for every arm in
930 // [`CaixaDialeto::ALL`], the [`std::fmt::Display`] rendered form
931 // must byte-equal [`CaixaDialeto::as_str`]'s return value. Pre-
932 // lift these two paths were structurally independent — the
933 // Display impl hand-rolled its own four-arm literal-string
934 // match with no compile-time link back to any substrate accessor
935 // — so a future variant rename could land at `Display` without
936 // touching a paired accessor (or vice versa), silently splitting
937 // the two paths on the renamed arm. Pinning the byte-equality
938 // here makes any such split a caixa-core build-time failure at
939 // this test rather than surfacing far from the rename commit as
940 // a downstream census consumer emitting one spelling while the
941 // typed accessor returned another. Peer of the sibling
942 // [`crate::kind::tests::caixa_kind_display_routes_through_as_str_helper`]
943 // (which pins the same convergence on the [`crate::CaixaKind`]
944 // closed-set axis) — extends the discipline onto the seventh
945 // (and last unlifted) closed-set fieldless typed enum on the
946 // caixa surface.
947 for &variant in CaixaDialeto::ALL {
948 assert_eq!(
949 variant.to_string(),
950 variant.as_str(),
951 "CaixaDialeto::{variant:?} Display must route through \
952 CaixaDialeto::as_str (single source of truth: the \
953 lifted per-arm `PascalCase` variant-name byte-string)"
954 );
955 }
956 }
957
958 #[test]
959 fn caixa_dialeto_as_ref_str_routes_through_as_str_accessor() {
960 // Fail-before-pass-after byte-parity pin on the lifted
961 // `impl AsRef<str> for CaixaDialeto` — asserts the standard-
962 // library trait impl and the substrate-primitive
963 // [`CaixaDialeto::as_str`] `pub const fn` accessor resolve to
964 // the same `&str` per instance across the four-arm closed set,
965 // so any future silent detour that routes the impl through a
966 // divergent projection (a per-arm inline
967 // `match self { CaixaDialeto::Pacote => "Pacote", … }` re-inlining
968 // that opens a compile-time link to the un-lifted arm-literal,
969 // a swap onto the second-axis
970 // [`CaixaDialeto::palavra_canonica`] /
971 // [`CaixaDialeto::consumidor`] / [`CaixaDialeto::descricao`]
972 // accessors that carry distinct byte-shapes per axis) trips at
973 // caixa-core test time under `PartialEq` rather than at a
974 // downstream `impl AsRef<str>`-bound consumer's silent split.
975 // Sweeps every one of the four arms [`CaixaDialeto::ALL`]
976 // carries so no arm's projection is covered only by the sibling
977 // `Display` path. Peer of the sibling
978 // `rate_limit_unit_as_ref_str_routes_through_as_suffix_accessor`
979 // (d8136db) on the M3 `:politicas :rate-limit` closed-set typed
980 // enum, and the peer
981 // [`crate::kind::tests::caixa_kind_as_ref_str_routes_through_as_str_accessor`]
982 // (cd2091f) pin on the top-level closed-set typed
983 // discriminator — the pins together close the substrate
984 // primitive's `AsRef<str>` projection axis onto the seventh
985 // closed-set fieldless typed enum on the caixa surface.
986 for &variant in CaixaDialeto::ALL {
987 assert_eq!(
988 <CaixaDialeto as AsRef<str>>::as_ref(&variant),
989 variant.as_str(),
990 "AsRef<str> impl on CaixaDialeto::{variant:?} must \
991 byte-equal CaixaDialeto::as_str on the same instance \
992 — divergence signals a silent detour off the \
993 substrate-primitive accessor"
994 );
995 }
996 }
997
998 #[test]
999 fn caixa_dialeto_as_ref_str_routes_through_display_via_shared_accessor() {
1000 // Fail-before-pass-after byte-parity pin on the three-path
1001 // convergence discipline the [`CaixaDialeto`] closed-set
1002 // dialect-classification enum now carries on the `&str`-
1003 // projection axis: `<CaixaDialeto as AsRef<str>>::as_ref(&v)`
1004 // (the newly lifted impl), `format!("{v}")` (the pre-existing
1005 // [`fmt::Display`] impl), and `v.as_str()` (the substrate-
1006 // primitive `pub const fn` accessor both trait impls delegate
1007 // through) must resolve to the same byte-string on every
1008 // instance across the four-arm closed set. Refuses any future
1009 // divergence between the two trait impls (a stray
1010 // [`fmt::Display::fmt`] rewrite that hand-rolls the arms
1011 // rather than delegating through the shared accessor; a
1012 // hypothetical `AsRef<str>` rewrite that inlines a per-arm
1013 // literal cascade) that would silently split the two
1014 // projection paths of the same closed-set typed enum. Mirrors
1015 // the sibling three-path-convergence discipline the peer
1016 // [`crate::aplicacao::RateLimitUnit`] typed enum carries
1017 // (`rate_limit_unit_as_ref_str_routes_through_display_via_shared_accessor`,
1018 // d8136db), the peer [`crate::CaixaKind`] triple
1019 // (`caixa_kind_as_ref_str_routes_through_display_via_shared_accessor`,
1020 // cd2091f), and the [`crate::CaixaVersion`] typed newtype
1021 // triple (`caixa_version_as_ref_str_routes_through_display_via_shared_accessor`,
1022 // 16d5c7e).
1023 for &variant in CaixaDialeto::ALL {
1024 let via_as_ref: &str = <CaixaDialeto as AsRef<str>>::as_ref(&variant);
1025 let via_display: String = format!("{variant}");
1026 let via_accessor: &str = variant.as_str();
1027 assert_eq!(via_as_ref, via_accessor);
1028 assert_eq!(via_display, via_accessor);
1029 assert_eq!(via_as_ref, via_display.as_str());
1030 }
1031 }
1032
1033 #[test]
1034 fn caixa_dialeto_is_molde_family_returns_true_on_molde_and_positional_arms() {
1035 // Fail-before-pass-after per-arm shape pin on the two `defmolde`
1036 // declaration-family arms: [`CaixaDialeto::is_molde_family`] must
1037 // return `true` for [`CaixaDialeto::Molde`] and
1038 // [`CaixaDialeto::MoldePosicional`] — the two-arity closure of
1039 // one declaration ([`CaixaDialeto::MoldePosicional`]'s docstring:
1040 // "same declaration as [`Self::Molde`], written with the package
1041 // name as a bare positional symbol … one arity of one
1042 // declaration, not a third schema"). A future accidental flip that
1043 // reversed a per-arm arm's return without touching the paired
1044 // false-arm pin would silently open the substrate primitive to
1045 // false-positive on either arm — the `feira dialeto` verb's
1046 // `--strict-palavra` gate would then silently accept
1047 // repo-surface declarations under `(defcaixa …)` on one arm and
1048 // reject them on the other. Pinning the two true arms explicitly
1049 // here refuses that split at caixa-core build time.
1050 assert!(
1051 CaixaDialeto::Molde.is_molde_family(),
1052 "CaixaDialeto::Molde.is_molde_family() must return true — \
1053 Molde is the primary `defmolde` arm"
1054 );
1055 assert!(
1056 CaixaDialeto::MoldePosicional.is_molde_family(),
1057 "CaixaDialeto::MoldePosicional.is_molde_family() must return \
1058 true — MoldePosicional is the positional-arity form of the \
1059 same `defmolde` declaration Molde carries"
1060 );
1061 }
1062
1063 #[test]
1064 fn caixa_dialeto_is_molde_family_returns_false_on_pacote_and_desconhecido_arms() {
1065 // Fail-before-pass-after per-arm shape pin on the two non-`defmolde`
1066 // arms: [`CaixaDialeto::is_molde_family`] must return `false` for
1067 // [`CaixaDialeto::Pacote`] (the sibling `defcaixa` tatara-lisp
1068 // package manifest, `palavra_canonica → "defcaixa"`) and for
1069 // [`CaixaDialeto::Desconhecido`] (the residue that names no
1070 // known declaration, `palavra_canonica → "?"`). Pinning the two
1071 // false arms explicitly here refuses a future accidental flip
1072 // that let the predicate widen to include either arm — the
1073 // `feira dialeto` verb's `--strict-palavra` gate would then
1074 // spuriously refuse every `(defcaixa …)` package manifest as if
1075 // it were a repo-surface declaration.
1076 assert!(
1077 !CaixaDialeto::Pacote.is_molde_family(),
1078 "CaixaDialeto::Pacote.is_molde_family() must return false — \
1079 Pacote is the `defcaixa` tatara-lisp package manifest, not \
1080 the `defmolde` repo-surface declaration"
1081 );
1082 assert!(
1083 !CaixaDialeto::Desconhecido.is_molde_family(),
1084 "CaixaDialeto::Desconhecido.is_molde_family() must return \
1085 false — the residue arm names no known declaration; it is \
1086 not silently promoted into the `defmolde` family"
1087 );
1088 }
1089
1090 #[test]
1091 fn caixa_dialeto_is_molde_family_agrees_with_palavra_canonica_defmolde_projection() {
1092 // Load-bearing pin: for every arm in [`CaixaDialeto::ALL`], the
1093 // typed [`CaixaDialeto::is_molde_family`] predicate must agree
1094 // byte-for-byte with the paired [`CaixaDialeto::palavra_canonica`]
1095 // projection's `== "defmolde"` classifier — i.e. the two paths
1096 // partition the four-arm discriminator set into the same
1097 // `{Molde, MoldePosicional}` and `{Pacote, Desconhecido}` halves.
1098 // Pre-lift the sibling [`CaixaDialeto::palavra_canonica`] projection
1099 // (which returns `"defmolde"` for `Molde | MoldePosicional`,
1100 // `"defcaixa"` for `Pacote`, `"?"` for `Desconhecido`) was the
1101 // only substrate-side surface carrying the two-arm collapse; the
1102 // hand-rolled `matches!(d, CaixaDialeto::Molde |
1103 // CaixaDialeto::MoldePosicional)` sites in the `feira dialeto`
1104 // verb expressed no compile-time link back to it. A future arm
1105 // addition — the module doc's "third dialect" hazard actualises
1106 // as a fifth arm belonging to the `defmolde` family — would land
1107 // one match arm at [`Self::palavra_canonica`]'s `defmolde` return
1108 // (extending the sibling projection) but silently split the
1109 // hand-rolled two-arm `matches!` predicate sites if the new arm's
1110 // `is_molde_family` return were forgotten. Pinning byte-equality
1111 // between the two paths here makes any such split a caixa-core
1112 // build-time failure at this test rather than surfacing far from
1113 // the arm-addition commit as a downstream `--strict-palavra` /
1114 // `caixa.lisp`-holds-wrong-declaration gate silently ignoring the
1115 // new arm.
1116 for &d in CaixaDialeto::ALL {
1117 let via_palavra_canonica = d.palavra_canonica() == "defmolde";
1118 let via_is_molde_family = d.is_molde_family();
1119 assert_eq!(
1120 via_is_molde_family, via_palavra_canonica,
1121 "CaixaDialeto::{d:?}.is_molde_family() ({via_is_molde_family}) \
1122 must agree with CaixaDialeto::{d:?}.palavra_canonica() == \
1123 \"defmolde\" ({via_palavra_canonica}) — a split between the \
1124 typed predicate and the sibling keyword projection would let \
1125 a future arm addition land at one path and drift at the other, \
1126 which is exactly the drift this pin refuses"
1127 );
1128 }
1129 }
1130
1131 #[test]
1132 fn caixa_dialeto_is_molde_family_is_const_fn() {
1133 // Const-context pin: [`CaixaDialeto::is_molde_family`] must remain
1134 // `const fn` (its match is a fieldless-arm literal-pattern
1135 // discriminator, so no non-const operation exists on the resolution
1136 // path). Downstream consumers reaching for the predicate from a
1137 // `const` context (a future substrate-wide const-fold-driven audit
1138 // table that materializes per-arm gate-membership at build time,
1139 // a per-arm CR-admission-webhook gate registration in a `const`
1140 // context) rely on the const-ness. A future accidental downgrade
1141 // to non-`const` (an added runtime helper reachable only from a
1142 // non-`const` context) trips at caixa-core build time rather than
1143 // surfacing as a downstream `const`-context regression far from
1144 // the predicate declaration. Peer of the sibling
1145 // [`caixa_dialeto_as_str_is_const_fn`] pin on the paired
1146 // [`CaixaDialeto::as_str`] byte-string axis.
1147 const ARMS: [(CaixaDialeto, bool); 4] = [
1148 (CaixaDialeto::Pacote, CaixaDialeto::Pacote.is_molde_family()),
1149 (CaixaDialeto::Molde, CaixaDialeto::Molde.is_molde_family()),
1150 (
1151 CaixaDialeto::MoldePosicional,
1152 CaixaDialeto::MoldePosicional.is_molde_family(),
1153 ),
1154 (
1155 CaixaDialeto::Desconhecido,
1156 CaixaDialeto::Desconhecido.is_molde_family(),
1157 ),
1158 ];
1159 // Materialize the const-fold-evaluated table into a runtime slice
1160 // assertion — carries the same `bool = const fn call` shape a raw
1161 // `assert!(const_bool)` would, without tripping the
1162 // `assertions_on_constants` clippy lint that a per-arm
1163 // `assert!(CONST)` on a `const bool` triggers when the arm-count
1164 // is enumerated flat rather than compared as a whole-table shape.
1165 assert_eq!(
1166 ARMS,
1167 [
1168 (CaixaDialeto::Pacote, false),
1169 (CaixaDialeto::Molde, true),
1170 (CaixaDialeto::MoldePosicional, true),
1171 (CaixaDialeto::Desconhecido, false),
1172 ],
1173 "CaixaDialeto::is_molde_family() must evaluate in const context \
1174 for every arm and land on the {{false, true, true, false}} \
1175 partition — a future accidental downgrade to non-`const` \
1176 would trip the const-context array-initializer here"
1177 );
1178 }
1179
1180 #[test]
1181 fn caixa_dialeto_as_str_is_const_fn() {
1182 // Const-context pin: [`CaixaDialeto::as_str`] must remain
1183 // `const fn` (its match arms return `pub const` byte-strings, so
1184 // no non-const operation exists on the resolution path).
1185 // Downstream consumers reaching for the accessor from a `const`
1186 // context (a future substrate-wide const-fold-driven audit table
1187 // that materializes every dialect's census label at build time,
1188 // a per-arm CR-admission-webhook message registration in a
1189 // `const` gate) rely on the const-ness. A future accidental
1190 // downgrade to non-`const` (an added runtime helper reachable
1191 // only from a non-`const` context, a manual hand-rolled `impl`
1192 // that shadows this method) trips at caixa-core build time
1193 // rather than surfacing as a downstream `const`-context
1194 // regression far from the accessor declaration. Peer of the
1195 // sibling [`crate::kind::tests::caixa_kind_wire_name_is_const_fn`]
1196 // pin on the paired [`crate::CaixaKind`] byte-string axis.
1197 const PACOTE: &str = CaixaDialeto::Pacote.as_str();
1198 const MOLDE: &str = CaixaDialeto::Molde.as_str();
1199 const MOLDE_POSICIONAL: &str = CaixaDialeto::MoldePosicional.as_str();
1200 const DESCONHECIDO: &str = CaixaDialeto::Desconhecido.as_str();
1201 assert_eq!(PACOTE, "Pacote");
1202 assert_eq!(MOLDE, "Molde");
1203 assert_eq!(MOLDE_POSICIONAL, "MoldePosicional");
1204 assert_eq!(DESCONHECIDO, "Desconhecido");
1205 }
1206
1207 #[test]
1208 fn caixa_dialeto_is_variant_predicates_partition_the_arm_set() {
1209 // Fail-before-pass-after pin on the [`gen_platform::IsVariant`]
1210 // derive: for each of the four variants at [`CaixaDialeto::ALL`]`[idx]`
1211 // the observed four-slot predicate row must equal a one-hot row
1212 // with the `true` at exactly `idx`. Pre-derive the closed four-arm
1213 // dialect-classification partition lived only inside the paired
1214 // per-arm projections' four-arm match resolvers ([`Self::as_str`] /
1215 // [`Self::palavra_canonica`] / [`Self::consumidor`] /
1216 // [`Self::descricao`]) plus the two-arm [`Self::is_molde_family`]
1217 // hand-rolled `matches!` (now routed through the derived
1218 // predicates); a future rebrand (an accidental
1219 // `#[is_variant(name = "…")]` drift, a manual hand-rolled `impl`
1220 // that shadows the derive-generated method, an arm rename that
1221 // reroutes one arm through the wrong predicate lane) trips this
1222 // pin at caixa-core build time rather than surfacing far from the
1223 // derive declaration as a downstream [`Self::is_molde_family`]
1224 // consumer accepting the wrong arm-set. The expected row is
1225 // generated live from the [`Self::ALL`] declaration order rather
1226 // than transcribed by hand so a copy-paste flip reroutes at the
1227 // identity-diagonal assertion.
1228 //
1229 // Peer of the sibling
1230 // [`crate::kind::tests::caixa_kind_is_variant_predicates_partition_the_arm_set`]
1231 // / [`crate::supervisor::tests::restart_strategy_is_variant_predicates_partition_the_arm_set`]
1232 // / [`crate::aplicacao::tests::placement_strategy_is_variant_predicates_partition_the_arm_set`]
1233 // / [`crate::upgrade::tests::upgrade_instruction_is_variant_predicates_partition_the_arm_set`]
1234 // pins on the sibling closed-set typed-enum discriminator axes.
1235 for (idx, &variant) in CaixaDialeto::ALL.iter().enumerate() {
1236 let observed = [
1237 variant.is_pacote(),
1238 variant.is_molde(),
1239 variant.is_molde_posicional(),
1240 variant.is_desconhecido(),
1241 ];
1242 let mut expected = [false; 4];
1243 expected[idx] = true;
1244 assert_eq!(
1245 observed, expected,
1246 "CaixaDialeto::{variant:?} at ALL[{idx}] is_* predicates \
1247 must fire only on their own arm lane (identity diagonal); \
1248 got {observed:?}",
1249 );
1250 }
1251 }
1252
1253 #[test]
1254 fn caixa_dialeto_is_variant_predicates_are_const_fn() {
1255 // The [`gen_platform::IsVariant`] derive emits `const fn`
1256 // predicates on the peer [`crate::CaixaKind`] +
1257 // [`crate::upgrade::UpgradeInstruction`] +
1258 // [`crate::supervisor::RestartStrategy`] +
1259 // [`crate::supervisor::RestartPolicy`] +
1260 // [`crate::aplicacao::PlacementStrategy`] +
1261 // [`crate::aplicacao::RateLimitUnit`] +
1262 // [`crate::dep::DepList`] closed-set typed enums — pin the same
1263 // posture on [`CaixaDialeto`] so a future accidental downgrade
1264 // to non-`const` (an added runtime helper reachable only from a
1265 // non-`const` context, a manual hand-rolled `impl` that shadows
1266 // the derive-generated method) trips at caixa-core build time
1267 // rather than surfacing as a downstream `const`-context
1268 // regression far from the derive declaration.
1269 // Use `const { assert!(…) }` (peer of the sibling
1270 // [`crate::render::PathShapeViolation`] +
1271 // [`crate::aplicacao::RateLimitUnit`] +
1272 // [`caixa_theme::style::Semantic`] const-fn pins) so the
1273 // const-context evaluation trips at const-fold time without
1274 // opening a per-`const bool` `assertions_on_constants` clippy
1275 // debt row this crate does not carry today for `dialeto.rs`.
1276 const { assert!(CaixaDialeto::Pacote.is_pacote()) };
1277 const { assert!(CaixaDialeto::Molde.is_molde()) };
1278 const { assert!(CaixaDialeto::MoldePosicional.is_molde_posicional()) };
1279 const { assert!(CaixaDialeto::Desconhecido.is_desconhecido()) };
1280 }
1281
1282 #[test]
1283 fn caixa_dialeto_from_wire_accepts_every_as_str_output() {
1284 // Fail-before-pass-after per-arm accept pin on the newly lifted
1285 // [`CaixaDialeto::from_wire`] reverse projection: every arm in
1286 // [`CaixaDialeto::ALL`] must parse back through `from_wire` when
1287 // fed its own [`CaixaDialeto::as_str`] output, landing on
1288 // `Some(same_variant)` — a regression that hand-rolled either
1289 // side's per-arm match without threading through the shared
1290 // four-string closed set would silently disagree on any future
1291 // arm rename and this pin flags it at caixa-core build time.
1292 // Peer of the sibling
1293 // [`crate::kind::tests::caixa_kind_wire_round_trips_through_from_wire`]
1294 // (2aa6d23) /
1295 // `placement_strategy_from_wire_accepts_every_lifted_constant`
1296 // (18c7342) /
1297 // `dep_list_round_trips_through_as_str_and_from_wire` (45ee563)
1298 // shape on the sibling closed-set typed-enum reverse-projection
1299 // axes.
1300 for &variant in CaixaDialeto::ALL {
1301 let wire = variant.as_str();
1302 let parsed = CaixaDialeto::from_wire(wire).unwrap_or_else(|| {
1303 panic!(
1304 "CaixaDialeto::from_wire({wire:?}) must accept every \
1305 CaixaDialeto::as_str output — got None for the \
1306 wire byte-string of {variant:?}"
1307 )
1308 });
1309 assert_eq!(
1310 parsed, variant,
1311 "CaixaDialeto::from_wire(CaixaDialeto::{variant:?}.as_str()) \
1312 must return CaixaDialeto::{variant:?} — the (as_str, \
1313 from_wire) pair must form a total round-trip on the \
1314 closed four-arm CaixaDialeto arm-set"
1315 );
1316 }
1317 }
1318
1319 #[test]
1320 fn caixa_dialeto_from_wire_rejects_unknown_byte_strings() {
1321 // Rejection pin on the parser's accept-set: any string outside
1322 // the four-arm [`CaixaDialeto::as_str`] output set must return
1323 // `None`. A future accidental widening of the accept-set (a
1324 // case-insensitive match that accepts `"pacote"` on the wire
1325 // axis, a hand-rolled Levenshtein-forgiving arm-lookup that
1326 // admits `"Pacotee"` typos, a silent acceptance of the sibling
1327 // [`Self::palavra_canonica`] `"defcaixa"` / `"defmolde"`
1328 // byte-shapes on this axis) would silently drift the parser's
1329 // accept-set from the emitter's — a downstream audit-report
1330 // re-loader that bound a prior audit's [`Self::as_str`] output
1331 // back to the typed enum through this parser would then bind a
1332 // malformed byte-string to a plausibly-wrong typed arm the
1333 // caller does not route through any fallback, silently
1334 // misclassifying the reloaded row. Also rejects the sibling
1335 // [`Self::palavra_canonica`] (`"defcaixa"` / `"defmolde"`) and
1336 // the sibling [`Self::consumidor`] (`"caixa-core / feira"`,
1337 // `"pleme-doc-gen"`, `"nobody known"`) byte-shapes, which are
1338 // the substrate's *distinct-axis* projections on the same enum
1339 // — the two-axis split the sibling
1340 // [`Self::palavra_canonica`] / [`Self::consumidor`] /
1341 // [`Self::descricao`] docstrings explicitly frame forbids
1342 // accepting one axis's byte-shapes as parseable on the other
1343 // axis. Peer of the sibling
1344 // [`crate::kind::tests::caixa_kind_from_wire_rejects_unknown_byte_strings`]
1345 // (2aa6d23) /
1346 // `placement_strategy_from_wire_rejects_unknown_byte_strings`
1347 // (18c7342) /
1348 // `dep_list_from_wire_returns_none_on_unknown_wire_scalar`
1349 // (45ee563) rejection pins on the sibling closed-set typed-enum
1350 // reverse-projection axes.
1351 for bad in [
1352 "",
1353 " ",
1354 "pacote",
1355 "PACOTE",
1356 "molde",
1357 "MoldePositional",
1358 "desconhecido",
1359 "Unknown",
1360 "defcaixa",
1361 "defmolde",
1362 "?",
1363 "caixa-core / feira",
1364 "pleme-doc-gen",
1365 "nobody known",
1366 "Pacote ",
1367 " Pacote",
1368 ] {
1369 assert!(
1370 CaixaDialeto::from_wire(bad).is_none(),
1371 "CaixaDialeto::from_wire({bad:?}) must return None — the \
1372 parser's accept-set is exactly the four CaixaDialeto::as_str \
1373 outputs; a widening would silently split the parser's \
1374 accept-set from the emitter's arm-set"
1375 );
1376 }
1377 }
1378
1379 #[test]
1380 fn cabeca_errada_ctor_matches_struct_literal_wrap() {
1381 // Fail-before-pass-after byte-identity pin: the lifted
1382 // [`DialetoError::cabeca_errada`] ctor MUST land on the exact
1383 // same struct-literal shape the pre-lift open-coded wire-up
1384 // block wrote by hand — `DialetoError::CabecaErrada {
1385 // encontrado: <head>.to_string() }`. A future accidental
1386 // divergence (`.into()` swap, per-arm constant substitution, an
1387 // added default field, an `.to_ascii_lowercase()` normalization
1388 // silently injected into the ctor body, a rebrand of the
1389 // `encontrado` field carrying a distinct byte-shape) trips this
1390 // pin at caixa-core build time rather than surfacing far from
1391 // the ctor declaration as a downstream `classify_form`
1392 // wrong-head consumer emitting one diagnostic shape while a
1393 // hand-written test peer opens another. Peer of the sibling
1394 // `unknown_byte_unit_ctor_matches_struct_literal_wrap`
1395 // (limits.rs; 29fac09) / `duplicate_from_ctor_matches_struct_
1396 // literal_wrap` (upgrade.rs; 7e52aec) shape on the sibling
1397 // single-slot `{ <field>: String }` envelope constructors.
1398 assert_eq!(
1399 DialetoError::cabeca_errada("defflake"),
1400 DialetoError::CabecaErrada {
1401 encontrado: "defflake".to_string(),
1402 },
1403 "DialetoError::cabeca_errada must byte-equal the pre-lift \
1404 open-coded struct-literal — a drift here means the ctor \
1405 stopped being a substrate primitive for the wrong-head \
1406 fallthrough site"
1407 );
1408 }
1409
1410 #[test]
1411 fn cabeca_errada_routes_encontrado_verbatim_across_boundary_inputs() {
1412 // Fail-before-pass-after boundary-sweep pin: the lifted
1413 // [`DialetoError::cabeca_errada`] ctor MUST route its
1414 // `encontrado: &str` argument verbatim into the
1415 // [`DialetoError::CabecaErrada`] `encontrado: String` field
1416 // for every boundary-covering `&str` input — empty string, a
1417 // canonical `defcaixa`-adjacent head, a non-ASCII head, a
1418 // whitespace-carrying head, a Unicode-full-width head. Any
1419 // wrapper-side truncation, silent `.trim()`, accidental
1420 // `.to_ascii_lowercase()` normalization, or `.into()` divergence
1421 // on the ctor body surfaces here as a byte-mismatch against the
1422 // input rather than at a downstream
1423 // [`DialetoError::to_string()`] diagnostic-shape drift at a
1424 // wrong-head fallthrough consumer far from the ctor declaration.
1425 // Peer of the sibling `limits_codec_unit_only_ctors_route_unit_
1426 // verbatim_across_every_variant` (limits.rs; 29fac09) shape on
1427 // the sibling single-slot `{ <field>: String }` envelope
1428 // boundary-sweep discipline.
1429 for encontrado in [
1430 "",
1431 "defflake",
1432 "def-molde",
1433 "defcaixa ",
1434 " defcaixa",
1435 "μdefcaixa",
1436 "\u{00A0}defcaixa",
1437 "\u{3000}defcaixa",
1438 "def\u{2028}caixa",
1439 ] {
1440 let via_ctor = DialetoError::cabeca_errada(encontrado);
1441 let via_literal = DialetoError::CabecaErrada {
1442 encontrado: encontrado.to_string(),
1443 };
1444 assert_eq!(
1445 via_ctor, via_literal,
1446 "DialetoError::cabeca_errada({encontrado:?}) must byte- \
1447 equal the open-coded struct-literal on the same input — \
1448 a drift here would let the ctor silently normalize / \
1449 truncate the head symbol before it reached the \
1450 CabecaErrada envelope"
1451 );
1452 let DialetoError::CabecaErrada { encontrado: routed } = via_ctor else {
1453 panic!(
1454 "DialetoError::cabeca_errada must construct the \
1455 CabecaErrada arm — got a different variant on \
1456 input {encontrado:?}"
1457 );
1458 };
1459 assert_eq!(
1460 routed, encontrado,
1461 "DialetoError::cabeca_errada must route the input \
1462 {encontrado:?} verbatim into the encontrado field — \
1463 any wrapper-side truncation / normalization surfaces \
1464 here rather than at a downstream diagnostic shape drift"
1465 );
1466 }
1467 }
1468
1469 #[test]
1470 fn classify_form_wrong_head_routes_through_cabeca_errada_ctor() {
1471 // Fail-before-pass-after routing pin: [`classify`]'s wrong-head
1472 // fallthrough site MUST construct its `Err(DialetoError::…)`
1473 // through the substrate-primitive [`DialetoError::cabeca_errada`]
1474 // ctor rather than through an open-coded struct-literal. Pre-
1475 // lift the wire-up hand-rolled a three-line
1476 // `Self::CabecaErrada { encontrado: other.to_string() }` block
1477 // with no compile-time link back to the substrate primitive; a
1478 // future accidental rebrand of the ctor body (an added
1479 // `.trim()` on `encontrado`, a per-arm constant prefix like
1480 // `"unknown-head:"`, a widening of the field into a
1481 // `(String, usize)` tuple carrying a caret offset) would then
1482 // silently split the two paths — the ctor consumers pick up
1483 // the new shape, the open-coded wire-up does not. Pinning
1484 // byte-equality between the observed `Err` and the ctor-
1485 // constructed `Err` refuses that split at caixa-core build
1486 // time rather than surfacing far from the wire-up commit as a
1487 // downstream diagnostic-consumer split.
1488 for head in ["defflake", "deffoobar", "defcaixaz", "let", "defmoldez"] {
1489 let src = format!("({head} :nome \"x\")");
1490 let observed = classify(&src);
1491 let via_ctor = Err(DialetoError::cabeca_errada(head));
1492 assert_eq!(
1493 observed, via_ctor,
1494 "classify({src:?}) must return the same Err shape as \
1495 DialetoError::cabeca_errada({head:?}) — a drift here \
1496 means the wire-up de-lifted its wrong-head fallthrough \
1497 arm off the substrate primitive"
1498 );
1499 }
1500 }
1501
1502 #[test]
1503 fn leitura_ctor_matches_tuple_literal_wrap_on_str_binding() {
1504 // Fail-before-pass-after byte-identity pin: the lifted
1505 // [`DialetoError::leitura`] ctor MUST land on the exact same
1506 // tuple-newtype wrap the pre-lift open-coded wire-up block wrote by
1507 // hand — `DialetoError::Leitura(<into-String-expr>)`. A future
1508 // accidental divergence (an added `.trim()` on the reader reason,
1509 // a per-arm constant prefix like `"tatara-lisp:"`, a widening of
1510 // the tuple carrying a caret offset, a rebrand of the payload
1511 // carrying a distinct byte-shape) trips this pin at caixa-core
1512 // build time rather than surfacing far from the ctor declaration
1513 // as a downstream [`classify`] tatara-lisp-reader consumer
1514 // emitting one diagnostic shape while a hand-written test peer
1515 // opens another. Peer of the sibling
1516 // `cabeca_errada_ctor_matches_struct_literal_wrap` pin above on
1517 // the same [`DialetoError`] envelope's wrong-head axis, and of
1518 // the peer `LimitsError::empty_byte_size` /
1519 // `LimitsError::empty_duration` (7a4b003 / 319216c) shape on the
1520 // sibling `(String)` single-slot tuple-newtype envelope
1521 // constructors.
1522 let reason: &str = "unclosed paren at 1:12";
1523 assert_eq!(
1524 DialetoError::leitura(reason),
1525 DialetoError::Leitura(reason.to_string()),
1526 "DialetoError::leitura must byte-equal the pre-lift open-coded \
1527 tuple-newtype wrap — a drift here means the ctor stopped \
1528 being a substrate primitive for the tatara-lisp-reader \
1529 fallthrough site"
1530 );
1531 }
1532
1533 #[test]
1534 fn leitura_ctor_matches_tuple_literal_wrap_on_string_binding() {
1535 // Fail-before-pass-after byte-identity pin on the `String` wire-up
1536 // shape: the lifted [`DialetoError::leitura`] ctor MUST land on
1537 // the same tuple-newtype wrap when the caller passes an owned
1538 // `String` (the actual [`classify`] wire-up shape — `e.to_string()`
1539 // on a [`tatara_lisp::Error`]-carrying binding). Pins that the
1540 // `impl Into<String>` param covers the owned-`String` path with no
1541 // silent double-allocation or intermediate `&str` reslicing. Peer
1542 // of the sibling `_on_str_binding` pin above — together they close
1543 // the `impl Into<String>` bound's two authored wire-up shapes on
1544 // the ctor's substrate primitive.
1545 let reason: String = String::from("read: unexpected EOF at 3:1");
1546 let via_ctor = DialetoError::leitura(reason.clone());
1547 let via_literal = DialetoError::Leitura(reason.clone());
1548 assert_eq!(
1549 via_ctor, via_literal,
1550 "DialetoError::leitura must byte-equal the pre-lift open-coded \
1551 tuple-newtype wrap on the same owned-String fixture — a drift \
1552 here would let the ctor silently reshape the reader reason \
1553 before it reached the Leitura envelope"
1554 );
1555 let DialetoError::Leitura(routed) = via_ctor else {
1556 panic!(
1557 "DialetoError::leitura must construct the Leitura arm — \
1558 got a different variant on input {reason:?}"
1559 );
1560 };
1561 assert_eq!(
1562 routed, reason,
1563 "DialetoError::leitura must route the input {reason:?} \
1564 verbatim into the tuple-newtype payload — any wrapper-side \
1565 truncation / normalization surfaces here rather than at a \
1566 downstream diagnostic shape drift"
1567 );
1568 }
1569
1570 #[test]
1571 fn leitura_routes_reason_verbatim_across_boundary_inputs() {
1572 // Fail-before-pass-after boundary-sweep pin: the lifted
1573 // [`DialetoError::leitura`] ctor MUST route its
1574 // `reason: impl Into<String>` argument verbatim into the
1575 // [`DialetoError::Leitura`] tuple-newtype `String` payload for
1576 // every boundary-covering input — empty string, a canonical
1577 // tatara-lisp reader error, a non-ASCII reason, a
1578 // whitespace-carrying reason, a Unicode-full-width reason. Any
1579 // wrapper-side truncation, silent `.trim()`, accidental
1580 // `.to_ascii_lowercase()` normalization, or `.into()` divergence
1581 // on the ctor body surfaces here as a byte-mismatch against the
1582 // input rather than at a downstream [`DialetoError::to_string()`]
1583 // diagnostic-shape drift at a tatara-lisp-reader fallthrough
1584 // consumer far from the ctor declaration. Peer of the sibling
1585 // `cabeca_errada_routes_encontrado_verbatim_across_boundary_inputs`
1586 // pin above on the same [`DialetoError`] envelope's wrong-head
1587 // axis.
1588 for reason in [
1589 "",
1590 "unclosed paren at 1:12",
1591 "unexpected token ')'",
1592 "read: eof",
1593 " leading whitespace",
1594 "trailing whitespace ",
1595 "μnicode reason",
1596 "\u{00A0}NBSP-prefixed reason",
1597 "\u{3000}ideographic-space reason",
1598 "reason\u{2028}with-line-separator",
1599 ] {
1600 let via_ctor = DialetoError::leitura(reason);
1601 let via_literal = DialetoError::Leitura(reason.to_string());
1602 assert_eq!(
1603 via_ctor, via_literal,
1604 "DialetoError::leitura({reason:?}) must byte-equal the \
1605 open-coded tuple-newtype wrap on the same input — a \
1606 drift here would let the ctor silently normalize / \
1607 truncate the reader reason before it reached the \
1608 Leitura envelope"
1609 );
1610 let DialetoError::Leitura(routed) = via_ctor else {
1611 panic!(
1612 "DialetoError::leitura must construct the Leitura \
1613 arm — got a different variant on input {reason:?}"
1614 );
1615 };
1616 assert_eq!(
1617 routed, reason,
1618 "DialetoError::leitura must route the input {reason:?} \
1619 verbatim into the tuple-newtype payload — any \
1620 wrapper-side truncation / normalization surfaces here \
1621 rather than at a downstream diagnostic shape drift"
1622 );
1623 }
1624 }
1625
1626 #[test]
1627 fn classify_reader_error_routes_through_leitura_ctor() {
1628 // Fail-before-pass-after routing pin: [`classify`]'s
1629 // tatara-lisp-reader map-err site MUST construct its
1630 // `Err(DialetoError::…)` through the substrate-primitive
1631 // [`DialetoError::leitura`] ctor rather than through an
1632 // open-coded tuple-newtype wrap. Pre-lift the wire-up hand-rolled
1633 // a `Self::Leitura(e.to_string())` block with no compile-time
1634 // link back to the substrate primitive; a future accidental
1635 // rebrand of the ctor body (an added `.trim()` on the reader
1636 // reason, a per-arm constant prefix like `"tatara-lisp:"`, a
1637 // widening of the payload into a `(String, usize)` tuple
1638 // carrying a caret offset) would then silently split the two
1639 // paths — the ctor consumers pick up the new shape, the
1640 // open-coded wire-up does not. Pinning byte-equality between
1641 // the observed `Err` and the ctor-constructed `Err` refuses
1642 // that split at caixa-core build time rather than surfacing far
1643 // from the wire-up commit as a downstream diagnostic-consumer
1644 // split. Peer of the sibling
1645 // `classify_form_wrong_head_routes_through_cabeca_errada_ctor`
1646 // pin above on the same [`DialetoError`] envelope's wrong-head
1647 // fallthrough axis.
1648 //
1649 // The malformed sources below each name a distinct
1650 // tatara-lisp-reader failure shape (unclosed paren, stray close
1651 // paren, unterminated string), so together they sweep the
1652 // reader's rejection surface rather than pinning against one
1653 // specific error message the reader upstream is free to reword.
1654 for src in [
1655 "(defcaixa :nome \"x\"",
1656 "defcaixa :nome \"x\")",
1657 "(defcaixa :nome \"unterminated",
1658 ] {
1659 let observed = classify(src);
1660 let Err(DialetoError::Leitura(reason)) = observed.clone() else {
1661 panic!(
1662 "classify({src:?}) must return the Leitura arm — got \
1663 {observed:?}"
1664 );
1665 };
1666 let via_ctor: Result<CaixaDialeto, DialetoError> =
1667 Err(DialetoError::leitura(reason.clone()));
1668 assert_eq!(
1669 observed, via_ctor,
1670 "classify({src:?}) must return the same Err shape as \
1671 DialetoError::leitura({reason:?}) — a drift here means \
1672 the wire-up de-lifted its tatara-lisp-reader fallthrough \
1673 arm off the substrate primitive"
1674 );
1675 }
1676 }
1677
1678 #[test]
1679 fn caixa_dialeto_is_molde_family_routes_through_is_variant_derived_predicates() {
1680 // Byte-parity pin on the post-lift [`CaixaDialeto::is_molde_family`]
1681 // convergence: for every arm in [`CaixaDialeto::ALL`], the typed
1682 // predicate must byte-equal the direct
1683 // `self.is_molde() || self.is_molde_posicional()` composition of
1684 // the two derived per-arm predicates. Pre-lift the predicate
1685 // hand-rolled `matches!(self, Self::Molde | Self::MoldePosicional)`
1686 // with no compile-time link back to the closed-set typed dispatch;
1687 // post-lift it routes through the derived predicates so a future
1688 // arm rename or `#[is_variant(name = "…")]` override lands at
1689 // exactly one dispatch on the substrate primitive. Pinning the
1690 // byte-equality here refuses a future accidental split between
1691 // the composed predicate and the paired derived predicates
1692 // (a hand-rolled shadow `impl` that overrides one path but not
1693 // the other, an accidental rebrand of `is_molde_family`'s body
1694 // back to the pre-lift `matches!` form) at caixa-core build time.
1695 for &d in CaixaDialeto::ALL {
1696 let via_derived = d.is_molde() || d.is_molde_posicional();
1697 let via_is_molde_family = d.is_molde_family();
1698 assert_eq!(
1699 via_is_molde_family, via_derived,
1700 "CaixaDialeto::{d:?}.is_molde_family() ({via_is_molde_family}) \
1701 must byte-equal the composed derived predicates \
1702 is_molde() || is_molde_posicional() ({via_derived}) — a \
1703 split between the composed predicate and its derived \
1704 building blocks would let a future arm rename land at one \
1705 path and drift at the other, which is exactly the drift \
1706 the IsVariant lift refuses"
1707 );
1708 }
1709 }
1710}