Skip to main content

proxima_core/
markers.rs

1//! Compile-time marker traits for dispatch units.
2//!
3//! Empty traits — zero runtime cost. Each type impls the markers it
4//! qualifies for; operators (e.g. `AndThen<A, B>` in proxima-process)
5//! propagate via blanket impls.
6//!
7//! Promoted from `proxima-process-protocol::markers` to `proxima-core`
8//! as Phase C.1 of the cliff-extension plan
9//! (the we-want-the-dependency-drifting-bear plan) so the
10//! whole workspace can use the marker vocabulary without depending on
11//! `proxima-process-protocol`. `proxima-process-protocol::markers`
12//! continues to re-export from here for backward compatibility.
13//!
14//! # Categories
15//!
16//! - **Tier compatibility:** [`NoStd`], [`AllocFree`]
17//! - **Effect-absence (negative — prove no effect X):**
18//!   [`WithoutFilesystem`], [`WithoutNetwork`], [`WithoutSpawn`],
19//!   [`WithoutTime`], [`WithoutRandom`]
20//! - **Effect-absence (umbrella):** [`IsPure`] (no effects at all)
21//! - **Determinism hierarchy:** [`Deterministic`], [`Reproducible`],
22//!   [`IdempotentSideEffectFree`], [`Commutative`]
23//!
24//! # Why effect markers are NEGATIVE
25//!
26//! Rust trait-overlap rules make POSITIVE effect propagation
27//! (`HasX` for `AndThen<A, B>` if either A or B has it) impossible
28//! without specialization — two blanket impls
29//! (`impl<A: HasX, B> HasX for AndThen<A, B>` AND
30//! `impl<A, B: HasX> HasX for AndThen<A, B>`) would overlap when
31//! both sides have the marker. Specialization is unstable.
32//!
33//! Negative markers compose via AND (`WithoutX` for `AndThen<A, B>`
34//! requires BOTH sides). AND semantics work with blanket impls; OR
35//! doesn't.
36//!
37//! The negative form is also the load-bearing one for sandbox use
38//! cases: "prove this chain CANNOT touch X" is the security
39//! assertion. The positive form ("this chain MAY touch X") isn't
40//! useful for users — they already know what grounds they put in.
41
42/// Compiles under `#![no_std]` (uses only `core::*` + optionally
43/// `alloc::*`). Orthogonal to [`AllocFree`].
44pub trait NoStd {}
45
46/// No heap allocations on the dispatch hot path (no
47/// `Box`/`Vec`/`String`/`Arc` allocated per call). Construction-time
48/// allocations are fine; per-dispatch allocations are not.
49///
50/// Stronger than [`NoStd`]: an `AllocFree` type may still link `std`,
51/// it just can't allocate per call. Typically used in tandem.
52pub trait AllocFree {}
53
54/// No side effects at all. Pure data flow: same input → same output,
55/// no external state read, no external state written.
56///
57/// Stronger than [`Deterministic`] (which only requires same
58/// in→same out): `IsPure` also forbids any I/O or kernel
59/// interaction. Implies all `Without*` markers (a `IsPure` ground
60/// is without filesystem, without network, without spawn, etc.).
61pub trait IsPure {}
62
63/// Type does not read or write the host filesystem.
64///
65/// Assert via `T: WithoutFilesystem` to prove at compile time that a
66/// chain has no filesystem access. Grounds that touch the real
67/// filesystem (e.g. `host_read`, `host_write`) do NOT impl this; pure
68/// grounds (`canned`, `empty`, `deny`) do. Operators propagate via
69/// AND: `AndThen<A, B>: WithoutFilesystem` requires both sides.
70pub trait WithoutFilesystem {}
71
72/// Type does not open or accept network connections.
73pub trait WithoutNetwork {}
74
75/// Type does not spawn subprocesses or send signals.
76pub trait WithoutSpawn {}
77
78/// Type does not read the system clock or depend on wall-clock time.
79pub trait WithoutTime {}
80
81/// Type does not read OS entropy or depend on randomness.
82pub trait WithoutRandom {}
83
84/// Same input → same output, modulo construction-time configuration.
85/// Required for any dispatch unit in a replay chain.
86///
87/// A ground that reads time is `Deterministic` only if its clock is
88/// fixed at construction; `Real` clocks disqualify it. Same rule for
89/// entropy (`Seeded { seed }` vs `Os`).
90pub trait Deterministic {}
91
92/// Deterministic AND stable across versions. A `Reproducible` ground
93/// produces the same output today and a year from now. Distinguishes
94/// from [`Deterministic`] grounds whose output may shift on internal
95/// changes (e.g. a hash function whose algorithm changes).
96pub trait Reproducible: Deterministic {}
97
98/// Calling the dispatch twice with the same input is observationally
99/// identical to calling it once. Useful for caching, retry-safety,
100/// at-least-once delivery semantics.
101pub trait IdempotentSideEffectFree {}
102
103/// The order in which two dispatches happen does not affect the
104/// observable outcome. Useful for parallel execution and reordering
105/// optimizations.
106pub trait Commutative {}
107
108/// The dispatch's in-flight future is safe to drop at any await point: a
109/// dropped `call` leaves no observable partial state — the peer/store sees a
110/// complete operation or nothing, never a torn one.
111///
112/// This is the cancellation-safety contract the concurrent fan combinators
113/// (`Race`, `ScatterGather`) require: they cancel losing branches by *dropping*
114/// their futures, so only a `DropSafe` pipe may be raced. The sequential
115/// `FanOut` awaits every branch and drops none, so it does NOT require it.
116///
117/// Justified impls: detached blocking work (`spawn_blocking`/offload completes
118/// regardless of the caller's fate), purely computational pipes (codecs,
119/// in-memory transforms), datagram-atomic transports. NOT justified: a
120/// streaming send whose partial body leaves the peer mid-message.
121pub trait DropSafe {}