asupersync/cx/cap.rs
1//! Typed capability sets for `Cx`.
2//!
3//! The capability set is represented at the type level so that operations
4//! requiring certain effects (spawn/time/random/io/remote) can be gated
5//! at compile time.
6//!
7//! # Capability Rows
8//!
9//! A capability row is a fixed-width vector of booleans — one per effect:
10//! `[SPAWN, TIME, RANDOM, IO, REMOTE]`. The [`CapSet`] struct encodes this
11//! row as const generics, making it a zero-sized type with no runtime cost.
12//!
13//! The subset relation ([`SubsetOf`]) is the pointwise ≤ ordering on rows.
14//! Narrowing (dropping capabilities) always succeeds; widening (gaining
15//! capabilities) is a compile-time error.
16//!
17//! # Forging Prevention
18//!
19//! Capability marker traits are sealed to prevent external crates from
20//! implementing them for arbitrary types. This ensures only the runtime's
21//! `CapSet` types can grant access to gated APIs.
22//!
23//! # Narrowing is Monotone
24//!
25//! If `A: SubsetOf<B>`, then every method available on `Cx<A>` is a
26//! subset of those available on `Cx<B>`. Narrowing cannot introduce new
27//! effects because each `Has*` marker is gated on a single boolean
28//! position, and the `SubsetOf` impl requires each bit in the sub to
29//! be ≤ the corresponding bit in the super.
30//!
31//! # Trusted Roots
32//!
33//! - The runtime constructs full contexts internally (e.g., via `RuntimeState`).
34//! - Test-only constructors (e.g., `Cx::for_testing*`) are permitted for harnesses.
35//!
36//! # Compile-time rejection of widening
37//!
38//! ```compile_fail
39//! use asupersync::cx::cap::{CapSet, SubsetOf};
40//!
41//! // WebCaps (no spawn) cannot widen to GrpcCaps (has spawn):
42//! fn widen<Sub: SubsetOf<Super>, Super>() {}
43//! type WebCaps = CapSet<false, true, false, true, false>;
44//! type GrpcCaps = CapSet<true, true, false, true, false>;
45//! widen::<GrpcCaps, WebCaps>(); // ERROR: GrpcCaps is NOT a subset of WebCaps
46//! ```
47//!
48//! ```compile_fail
49//! use asupersync::cx::cap::{CapSet, None, SubsetOf};
50//!
51//! // Cannot widen from None to any capability:
52//! fn widen<Sub: SubsetOf<Super>, Super>() {}
53//! type SpawnOnly = CapSet<true, false, false, false, false>;
54//! widen::<SpawnOnly, None>(); // ERROR: SpawnOnly is NOT a subset of None
55//! ```
56
57mod sealed {
58 pub trait Sealed {}
59
60 /// Type-level capability bit for subset reasoning.
61 ///
62 /// Kept inside `sealed` so external crates cannot construct or
63 /// implement traits on these types, preserving anti-forgery.
64 pub struct Bit<const V: bool>;
65
66 /// Ordering on capability bits: `false ≤ false`, `false ≤ true`, `true ≤ true`.
67 ///
68 /// The missing impl `(Bit<true>, Bit<false>)` encodes that widening
69 /// (gaining a capability you don't have) is statically rejected.
70 pub trait Le {}
71 impl Le for (Bit<false>, Bit<false>) {}
72 impl Le for (Bit<false>, Bit<true>) {}
73 impl Le for (Bit<true>, Bit<true>) {}
74}
75
76/// Type-level capability set.
77///
78/// Each boolean controls whether the capability is present:
79/// - `SPAWN`: spawn tasks/regions
80/// - `TIME`: timers, timeouts
81/// - `RANDOM`: entropy and random values
82/// - `IO`: async I/O capability
83/// - `REMOTE`: remote task spawning
84#[derive(Debug, Clone, Copy, Default)]
85pub struct CapSet<
86 const SPAWN: bool,
87 const TIME: bool,
88 const RANDOM: bool,
89 const IO: bool,
90 const REMOTE: bool,
91>;
92
93impl<const SPAWN: bool, const TIME: bool, const RANDOM: bool, const IO: bool, const REMOTE: bool>
94 sealed::Sealed for CapSet<SPAWN, TIME, RANDOM, IO, REMOTE>
95{
96}
97
98/// Full capability set (default).
99pub type All = CapSet<true, true, true, true, true>;
100
101/// No capabilities.
102pub type None = CapSet<false, false, false, false, false>;
103
104/// Marker: spawn capability.
105///
106/// ```compile_fail
107/// use asupersync::cx::HasSpawn;
108///
109/// struct FakeCaps;
110/// impl HasSpawn for FakeCaps {}
111/// ```
112pub trait HasSpawn: sealed::Sealed {}
113impl<const TIME: bool, const RANDOM: bool, const IO: bool, const REMOTE: bool> HasSpawn
114 for CapSet<true, TIME, RANDOM, IO, REMOTE>
115{
116}
117
118/// Marker: time capability.
119pub trait HasTime: sealed::Sealed {}
120impl<const SPAWN: bool, const RANDOM: bool, const IO: bool, const REMOTE: bool> HasTime
121 for CapSet<SPAWN, true, RANDOM, IO, REMOTE>
122{
123}
124
125/// Marker: random/entropy capability.
126pub trait HasRandom: sealed::Sealed {}
127impl<const SPAWN: bool, const TIME: bool, const IO: bool, const REMOTE: bool> HasRandom
128 for CapSet<SPAWN, TIME, true, IO, REMOTE>
129{
130}
131
132/// Marker: I/O capability.
133pub trait HasIo: sealed::Sealed {}
134impl<const SPAWN: bool, const TIME: bool, const RANDOM: bool, const REMOTE: bool> HasIo
135 for CapSet<SPAWN, TIME, RANDOM, true, REMOTE>
136{
137}
138
139/// Marker: remote capability.
140pub trait HasRemote: sealed::Sealed {}
141impl<const SPAWN: bool, const TIME: bool, const RANDOM: bool, const IO: bool> HasRemote
142 for CapSet<SPAWN, TIME, RANDOM, IO, true>
143{
144}
145
146/// Marker: subset relation between capability sets.
147///
148/// `Sub: SubsetOf<Super>` holds when every capability enabled in `Sub` is
149/// also enabled in `Super`. This is the pointwise ≤ ordering on boolean
150/// capability rows and guarantees that narrowing is **monotone**: you can
151/// only drop capabilities, never gain them.
152///
153/// # Monotonicity argument
154///
155/// Because `sealed::Le` is only implemented for `(false,false)`,
156/// `(false,true)`, and `(true,true)` — but *not* `(true,false)` — the
157/// compiler rejects any attempt to widen a capability set. Combined with
158/// the `Sealed` supertrait, external crates cannot forge `SubsetOf`
159/// implementations.
160///
161/// # Properties
162///
163/// - **Reflexive**: `CapSet<S,T,R,I,Re>: SubsetOf<CapSet<S,T,R,I,Re>>`
164/// - **Transitive**: if `A: SubsetOf<B>` and `B: SubsetOf<C>`, then
165/// `A: SubsetOf<C>` (follows from bit-level ≤ transitivity)
166/// - **Antisymmetric**: `A: SubsetOf<B>` and `B: SubsetOf<A>` implies A = B
167///
168/// ```compile_fail
169/// use asupersync::cx::SubsetOf;
170///
171/// struct FakeCaps;
172/// impl SubsetOf<FakeCaps> for FakeCaps {}
173/// ```
174pub trait SubsetOf<Super>: sealed::Sealed {}
175
176// General pointwise subset: Sub ⊆ Super iff each capability bit in Sub ≤ Super.
177impl<
178 const S1: bool,
179 const T1: bool,
180 const R1: bool,
181 const I1: bool,
182 const RE1: bool,
183 const S2: bool,
184 const T2: bool,
185 const R2: bool,
186 const I2: bool,
187 const RE2: bool,
188> SubsetOf<CapSet<S2, T2, R2, I2, RE2>> for CapSet<S1, T1, R1, I1, RE1>
189where
190 (sealed::Bit<S1>, sealed::Bit<S2>): sealed::Le,
191 (sealed::Bit<T1>, sealed::Bit<T2>): sealed::Le,
192 (sealed::Bit<R1>, sealed::Bit<R2>): sealed::Le,
193 (sealed::Bit<I1>, sealed::Bit<I2>): sealed::Le,
194 (sealed::Bit<RE1>, sealed::Bit<RE2>): sealed::Le,
195{
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 // Helper: assert a type satisfies SubsetOf at compile time.
203 fn assert_subset<Sub: SubsetOf<Super>, Super>() {}
204
205 // Helper: assert a marker trait at compile time.
206 fn assert_has_spawn<C: HasSpawn>() {}
207 fn assert_has_time<C: HasTime>() {}
208 fn assert_has_random<C: HasRandom>() {}
209 fn assert_has_io<C: HasIo>() {}
210 fn assert_has_remote<C: HasRemote>() {}
211
212 // --- Reflexivity ---
213
214 #[test]
215 fn subset_reflexive_all() {
216 assert_subset::<All, All>();
217 }
218
219 #[test]
220 fn subset_reflexive_none() {
221 assert_subset::<None, None>();
222 }
223
224 #[test]
225 fn subset_reflexive_mixed() {
226 // CapSet<true, false, true, false, true> ⊆ itself
227 assert_subset::<
228 CapSet<true, false, true, false, true>,
229 CapSet<true, false, true, false, true>,
230 >();
231 }
232
233 // --- Bottom and top ---
234
235 #[test]
236 fn none_subset_of_all() {
237 assert_subset::<None, All>();
238 }
239
240 #[test]
241 fn none_subset_of_any() {
242 assert_subset::<None, CapSet<false, true, false, false, true>>();
243 assert_subset::<None, CapSet<true, false, false, false, false>>();
244 }
245
246 #[test]
247 fn any_subset_of_all() {
248 assert_subset::<CapSet<true, false, true, false, true>, All>();
249 assert_subset::<CapSet<false, false, false, true, false>, All>();
250 }
251
252 // --- Intermediate narrowing (framework wrapper types) ---
253
254 #[test]
255 fn background_subset_of_grpc() {
256 // Background = <true, true, false, false, false>
257 // Grpc = <true, true, false, true, false>
258 // Background ⊆ Grpc (Background drops IO)
259 type BackgroundCaps = CapSet<true, true, false, false, false>;
260 type GrpcCaps = CapSet<true, true, false, true, false>;
261 assert_subset::<BackgroundCaps, GrpcCaps>();
262 }
263
264 #[test]
265 fn web_subset_of_all() {
266 // Web = <false, true, false, true, false>
267 type WebCaps = CapSet<false, true, false, true, false>;
268 assert_subset::<WebCaps, All>();
269 }
270
271 #[test]
272 fn pure_subset_of_web() {
273 // Pure = None = <false, false, false, false, false>
274 // Web = <false, true, false, true, false>
275 type WebCaps = CapSet<false, true, false, true, false>;
276 assert_subset::<None, WebCaps>();
277 }
278
279 #[test]
280 fn single_cap_subset_of_multi() {
281 // <false, true, false, false, false> ⊆ <true, true, false, true, false>
282 assert_subset::<
283 CapSet<false, true, false, false, false>,
284 CapSet<true, true, false, true, false>,
285 >();
286 }
287
288 // --- Transitivity (demonstrated, not mechanized) ---
289
290 #[test]
291 fn transitive_none_background_grpc() {
292 type BackgroundCaps = CapSet<true, true, false, false, false>;
293 type GrpcCaps = CapSet<true, true, false, true, false>;
294 // None ⊆ Background ⊆ Grpc, therefore None ⊆ Grpc
295 assert_subset::<None, BackgroundCaps>();
296 assert_subset::<BackgroundCaps, GrpcCaps>();
297 assert_subset::<None, GrpcCaps>();
298 }
299
300 // --- Marker traits ---
301
302 #[test]
303 fn all_has_every_capability() {
304 assert_has_spawn::<All>();
305 assert_has_time::<All>();
306 assert_has_random::<All>();
307 assert_has_io::<All>();
308 assert_has_remote::<All>();
309 }
310
311 #[test]
312 fn partial_caps_have_correct_markers() {
313 // <true, true, false, true, false> has Spawn+Time+Io but not Random/Remote
314 assert_has_spawn::<CapSet<true, true, false, true, false>>();
315 assert_has_time::<CapSet<true, true, false, true, false>>();
316 assert_has_io::<CapSet<true, true, false, true, false>>();
317 }
318
319 // --- ZST property ---
320
321 #[test]
322 fn capset_is_zero_sized() {
323 assert_eq!(std::mem::size_of::<All>(), 0);
324 assert_eq!(std::mem::size_of::<None>(), 0);
325 assert_eq!(
326 std::mem::size_of::<CapSet<true, false, true, false, true>>(),
327 0
328 );
329 }
330
331 // --- Compile-fail doctests for anti-forgery are on HasSpawn and SubsetOf above ---
332
333 // =========================================================================
334 // Wave 54 – pure data-type trait coverage
335 // =========================================================================
336
337 #[test]
338 fn capset_debug_clone_copy_default() {
339 let all = All::default();
340 let dbg = format!("{all:?}");
341 assert!(dbg.contains("CapSet"), "{dbg}");
342 let copied = all;
343 let cloned = all;
344 // ZST so all instances are identical
345 let _ = (copied, cloned);
346
347 let none = None::default();
348 let dbg_none = format!("{none:?}");
349 assert!(dbg_none.contains("CapSet"), "{dbg_none}");
350 }
351}