Skip to main content

wasm4pm_compat/
state.rs

1//! Typestate tokens — the evidence lifecycle, tracked at the type level.
2//!
3//! Every piece of process evidence in this crate moves through a small,
4//! strictly-ordered lifecycle. Each stage is an **empty enum** (uninhabited,
5//! zero-cost) used only as a `PhantomData` tag inside
6//! [`crate::evidence::Evidence`]. Because the stages are distinct types, an
7//! illegal stage transition is not a runtime error — it simply **does not
8//! compile**.
9//!
10//! ## The lifecycle
11//!
12//! ```text
13//!   Raw ──parse──▶ Parsed ──admit──▶ Admitted ──▶ {Projected | Exportable | Receipted}
14//!     │                                  ▲
15//!     └────────────── refuse ────────────┴──▶ Refused  (terminal: a named law was broken)
16//! ```
17//!
18//! - You may *construct* [`crate::state::Raw`] evidence freely (it is untrusted input).
19//! - You may only reach [`crate::state::Admitted`] through an [`crate::admission::Admit`]
20//!   impl — there is **no** public free conversion `Raw → Admitted`.
21//! - [`crate::state::Refused`] is terminal and first-class: it carries a *specific named law*,
22//!   never a bare "invalid input".
23//!
24//! These tokens are **structure only**. They mark *where a value is* in the
25//! boundary protocol; they never run discovery, conformance, or replay.
26//!
27//! ## The sealed [`crate::state::EvidenceState`] trait
28//!
29//! All lifecycle stage tokens implement the [`crate::state::EvidenceState`] sealed trait. This
30//! prevents a downstream crate from inventing an arbitrary type and using it as
31//! the `State` parameter of [`crate::evidence::Evidence`]. Only the seven
32//! canonical stages defined here are valid lifecycle positions.
33
34mod private {
35    /// Sealing super-trait — prevents out-of-crate implementations of
36    /// [`super::EvidenceState`].
37    pub trait Sealed {}
38}
39
40/// Marker trait carried by every canonical lifecycle stage token.
41///
42/// This trait is **sealed**: only the seven stage tokens defined in this module
43/// implement it. A downstream crate cannot invent its own stage and pass it as
44/// the `State` type parameter of [`crate::evidence::Evidence`] — the
45/// missing-impl error at compile time is the law-enforcement mechanism.
46///
47/// Structure-only marker. It does not add methods or runtime cost; it only
48/// restricts the set of valid `State` arguments.
49///
50/// # What this is NOT
51///
52/// Not a validator, not a capability, not a runtime discriminant. It is a pure
53/// compile-time constraint that makes illegal stage positions unrepresentable.
54/// Graduate to `wasm4pm` when the *meaning* of a stage needs to be acted upon.
55pub trait EvidenceState: private::Sealed {}
56
57/// Untrusted input as it arrives from the outside world.
58///
59/// `Raw` is the entry stage: bytes/values just parsed off an external format,
60/// not yet judged against any [`crate::witness::Witness`]. A `Raw` value must
61/// **never** be exported as if it were admitted (see
62/// [`crate::diagnostic::CompatDiagnostic::RawEvidenceExportedAsAdmitted`]).
63///
64/// Structure-only marker. Graduate the *checking* of raw evidence to `wasm4pm`;
65/// here it is merely a lifecycle position.
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
67pub enum Raw {}
68
69/// Structurally parsed, but not yet judged at the boundary.
70///
71/// `Parsed` evidence has a well-formed shape (the format decoder accepted it)
72/// but has not been put through admission against a named authority. It is the
73/// staging stage between [`Raw`] and [`Admitted`].
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum Parsed {}
76
77/// Admitted across the boundary against a named [`crate::witness::Witness`].
78///
79/// Reaching `Admitted` means an [`crate::admission::Admit`] impl returned
80/// [`crate::admission::Admission`] rather than [`crate::admission::Refusal`].
81/// Only `Admitted` evidence is eligible to be projected, exported, or receipted.
82#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
83pub enum Admitted {}
84
85/// Terminal refusal: a specific named law was broken at the boundary.
86///
87/// `Refused` is not an error code — it is a *first-class outcome*. A value in
88/// this stage carries the named reason it was refused (e.g.
89/// `DanglingEventObjectLink`, `FlatteningLoss`), so the refusal is auditable.
90/// Refused evidence cannot be silently coerced back into [`Admitted`].
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92pub enum Refused {}
93
94/// Result of a *named, accounted* lossy projection.
95///
96/// `Projected` evidence was produced by a [`crate::loss::Project`] impl under an
97/// explicit [`crate::loss::LossPolicy`], accompanied by a
98/// [`crate::loss::LossReport`]. The projection is therefore on the record:
99/// nothing was flattened in secret.
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101pub enum Projected {}
102
103/// Cleared to leave the crate as an external/`wasm4pm` value.
104///
105/// `Exportable` marks evidence that has been admitted (and possibly projected)
106/// and is now allowed to cross back out through an export contract. This stage
107/// is the boundary's "exit visa".
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
109pub enum Exportable {}
110
111/// Sealed inside a provenance-bearing receipt shape.
112///
113/// `Receipted` evidence has been wrapped in a receipt envelope that records its
114/// provenance and the witness it answered to. It is the strongest structural
115/// stage in this crate — and the natural hand-off point when graduating to a
116/// `wasm4pm` engine that will verify the receipt.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub enum Receipted {}
119
120// ── StateTransition markers ───────────────────────────────────────────────────
121
122/// Zero-sized type-level marker asserting that `Raw → Parsed` is the
123/// transition at hand.
124///
125/// Used as a const/type witness when an API must distinguish *which* boundary
126/// crossing it is operating on without carrying runtime state.
127///
128/// Structure-only marker. Does not implement any logic; it names a transition.
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
130pub struct RawToParsed;
131
132/// Zero-sized type-level marker asserting that `Parsed → Admitted` is the
133/// transition at hand (i.e. the admission gate was passed).
134///
135/// Structure-only marker.
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
137pub struct ParsedToAdmitted;
138
139/// Zero-sized type-level marker asserting that `Parsed → Refused` is the
140/// transition at hand (i.e. the evidence was declined before full admission).
141///
142/// Structure-only marker.
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
144pub struct ParsedToRefused;
145
146/// Zero-sized type-level marker asserting that `Admitted → Projected` is the
147/// transition at hand (i.e. a named lossy projection was applied).
148///
149/// Structure-only marker.
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
151pub struct AdmittedToProjected;
152
153/// Zero-sized type-level marker asserting that `Admitted → Exportable` is the
154/// transition at hand (i.e. the exit-visa was granted directly from admission).
155///
156/// Structure-only marker.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub struct AdmittedToExportable;
159
160/// Zero-sized type-level marker asserting that `Admitted → Receipted` is the
161/// transition at hand (i.e. a receipt envelope was sealed directly on admitted
162/// evidence).
163///
164/// Structure-only marker.
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
166pub struct AdmittedToReceipted;
167
168/// Zero-sized type-level marker asserting that `Projected → Exportable` is
169/// the transition at hand.
170///
171/// Structure-only marker.
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
173pub struct ProjectedToExportable;
174
175/// Zero-sized type-level marker asserting that `Projected → Receipted` is
176/// the transition at hand.
177///
178/// Structure-only marker.
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
180pub struct ProjectedToReceipted;
181
182/// Zero-sized type-level marker asserting that `Exportable → Receipted` is
183/// the transition at hand (i.e. the receipt envelope was sealed on an already
184/// export-cleared value).
185///
186/// Structure-only marker.
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
188pub struct ExportableToReceipted;
189
190// ── Projectible ──────────────────────────────────────────────────────────────
191
192/// Sealed marker trait: only lifecycle stages that may legally enter a named,
193/// accounted projection implement this trait.
194///
195/// Under the one-way-door invariant, a value must be [`Admitted`] before it
196/// can be projected (see [`crate::loss::Project`]). This trait makes that
197/// invariant structural: only `Admitted` and — because a second projection pass
198/// is representable in some pipeline shapes — `Projected` implement it.
199///
200/// A downstream crate cannot add its own stage here; the sealing via
201/// `private::Sealed` ensures only the two stages above are valid.
202///
203/// ## What this is NOT
204///
205/// Not a runtime capability, not a method table. This is a pure compile-time
206/// gate that prevents projecting evidence that was never admitted. Graduate
207/// the actual projection logic to `wasm4pm`.
208pub trait Projectible: EvidenceState + private::Sealed {}
209
210// ── EvidenceState impls ───────────────────────────────────────────────────────
211
212impl private::Sealed for Raw {}
213impl EvidenceState for Raw {}
214
215impl private::Sealed for Parsed {}
216impl EvidenceState for Parsed {}
217
218impl private::Sealed for Admitted {}
219impl EvidenceState for Admitted {}
220
221impl private::Sealed for Refused {}
222impl EvidenceState for Refused {}
223
224impl private::Sealed for Projected {}
225impl EvidenceState for Projected {}
226
227impl private::Sealed for Exportable {}
228impl EvidenceState for Exportable {}
229
230impl private::Sealed for Receipted {}
231impl EvidenceState for Receipted {}
232
233// ── Projectible impls ─────────────────────────────────────────────────────────
234
235impl Projectible for Admitted {}
236impl Projectible for Projected {}