Skip to main content

id_effect/capability/
set.rs

1//! Type-level capability set markers and [`CapList`] wrappers.
2
3use super::env::Env;
4use super::error::CapabilityError;
5use super::key::CapabilityKey;
6use std::marker::PhantomData;
7use std::ops::{Deref, DerefMut};
8
9/// Marker: effect requires capabilities listed in the `caps!` macro.
10pub trait CapabilitySet {
11  /// Verify `env` contains all required capabilities.
12  fn verify(env: &Env) -> Result<(), CapabilityError>;
13}
14
15/// Build runtime `R` from a constructed [`Env`].
16pub trait FromEnv: CapabilitySet + Sized {
17  /// Wrap or pass through `env` for effect execution.
18  fn from_env(env: Env) -> Self;
19}
20
21/// Tuple of capability keys — used as `CapList` type parameter.
22pub trait CapKeys {
23  /// Verify all keys in this tuple are present in `env`.
24  fn verify_all(env: &Env) -> Result<(), CapabilityError>;
25}
26
27/// Empty capability set (pure effects use `()`).
28pub struct NoCaps;
29
30impl CapabilitySet for NoCaps {
31  fn verify(_env: &Env) -> Result<(), CapabilityError> {
32    Ok(())
33  }
34}
35
36impl FromEnv for NoCaps {
37  fn from_env(_env: Env) -> Self {
38    Self
39  }
40}
41
42impl CapabilitySet for () {
43  fn verify(_env: &Env) -> Result<(), CapabilityError> {
44    Ok(())
45  }
46}
47
48impl FromEnv for () {
49  fn from_env(_env: Env) -> Self {}
50}
51
52impl CapabilitySet for Env {
53  fn verify(_env: &Env) -> Result<(), CapabilityError> {
54    Ok(())
55  }
56}
57
58impl FromEnv for Env {
59  fn from_env(env: Env) -> Self {
60    env
61  }
62}
63
64impl CapKeys for () {
65  fn verify_all(_env: &Env) -> Result<(), CapabilityError> {
66    Ok(())
67  }
68}
69
70macro_rules! impl_cap_keys {
71  ($($k:ident),*) => {
72    impl<$($k: CapabilityKey),*> CapKeys for ($($k,)*) {
73      fn verify_all(env: &Env) -> Result<(), CapabilityError> {
74        $( env.try_get::<$k>()?; )*
75        Ok(())
76      }
77    }
78  };
79}
80
81impl_cap_keys!(K0);
82impl_cap_keys!(K0, K1);
83impl_cap_keys!(K0, K1, K2);
84impl_cap_keys!(K0, K1, K2, K3);
85impl_cap_keys!(K0, K1, K2, K3, K4);
86impl_cap_keys!(K0, K1, K2, K3, K4, K5);
87impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6);
88impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6, K7);
89impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6, K7, K8);
90impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6, K7, K8, K9);
91impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10);
92impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11);
93impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12);
94impl_cap_keys!(K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12, K13);
95impl_cap_keys!(
96  K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12, K13, K14
97);
98impl_cap_keys!(
99  K0, K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, K12, K13, K14, K15
100);
101
102/// Environment typed with a tuple of required capability keys.
103#[derive(Clone, Debug, Default, PartialEq, Eq)]
104pub struct CapList<Ks> {
105  inner: Env,
106  _marker: PhantomData<Ks>,
107}
108
109impl<Ks: CapKeys> CapList<Ks> {
110  /// Wrap an already-built environment (after verification).
111  #[inline]
112  pub fn new(inner: Env) -> Self {
113    Self {
114      inner,
115      _marker: PhantomData,
116    }
117  }
118
119  /// Borrow inner [`Env`].
120  #[inline]
121  pub fn env(&self) -> &Env {
122    &self.inner
123  }
124
125  /// Mutably borrow inner [`Env`].
126  #[inline]
127  pub fn env_mut(&mut self) -> &mut Env {
128    &mut self.inner
129  }
130}
131
132impl<Ks: CapKeys> Deref for CapList<Ks> {
133  type Target = Env;
134
135  fn deref(&self) -> &Env {
136    &self.inner
137  }
138}
139
140impl<Ks: CapKeys> DerefMut for CapList<Ks> {
141  fn deref_mut(&mut self) -> &mut Env {
142    &mut self.inner
143  }
144}
145
146impl<Ks: CapKeys> CapabilitySet for CapList<Ks> {
147  fn verify(env: &Env) -> Result<(), CapabilityError> {
148    Ks::verify_all(env)
149  }
150}
151
152impl<Ks: CapKeys> FromEnv for CapList<Ks> {
153  fn from_env(env: Env) -> Self {
154    Self::new(env)
155  }
156}
157
158/// Helper trait: environment `R` exposes capability `K`.
159pub trait HasCap<K: CapabilityKey> {}
160
161impl<K: CapabilityKey> HasCap<K> for Env {}
162
163impl<K: CapabilityKey, T: Deref<Target = Env>> HasCap<K> for T {}
164
165/// Widen a capability set to a subset (subtyping).
166pub trait CapWiden<Target> {
167  /// Widen to a capability subset (structural subtyping).
168  fn widen(self) -> Target;
169}
170
171impl<Ks: CapKeys> CapWiden<CapList<Ks>> for CapList<Ks> {
172  fn widen(self) -> CapList<Ks> {
173    self
174  }
175}
176
177impl<K0: CapabilityKey, K1: CapabilityKey> CapWiden<CapList<(K0,)>> for CapList<(K0, K1)> {
178  fn widen(self) -> CapList<(K0,)> {
179    CapList::new(self.inner)
180  }
181}
182
183/// Per-index single-key projection (see ADR 0005).
184impl<K0: CapabilityKey> CapList<(K0,)> {
185  #[inline]
186  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
187  pub fn project_at_0(self) -> CapList<(K0,)> {
188    CapList::new(self.inner)
189  }
190}
191
192impl<K0: CapabilityKey, K1: CapabilityKey> CapList<(K0, K1)> {
193  #[inline]
194  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
195  pub fn project_at_0(self) -> CapList<(K0,)> {
196    CapList::new(self.inner)
197  }
198  #[inline]
199  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
200  pub fn project_at_1(self) -> CapList<(K1,)> {
201    CapList::new(self.inner)
202  }
203}
204
205impl<K0: CapabilityKey, K1: CapabilityKey, K2: CapabilityKey> CapList<(K0, K1, K2)> {
206  #[inline]
207  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
208  pub fn project_at_0(self) -> CapList<(K0,)> {
209    CapList::new(self.inner)
210  }
211  #[inline]
212  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
213  pub fn project_at_1(self) -> CapList<(K1,)> {
214    CapList::new(self.inner)
215  }
216  #[inline]
217  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
218  pub fn project_at_2(self) -> CapList<(K2,)> {
219    CapList::new(self.inner)
220  }
221}
222
223impl<K0: CapabilityKey, K1: CapabilityKey, K2: CapabilityKey, K3: CapabilityKey>
224  CapList<(K0, K1, K2, K3)>
225{
226  #[inline]
227  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
228  pub fn project_at_0(self) -> CapList<(K0,)> {
229    CapList::new(self.inner)
230  }
231  #[inline]
232  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
233  pub fn project_at_1(self) -> CapList<(K1,)> {
234    CapList::new(self.inner)
235  }
236  #[inline]
237  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
238  pub fn project_at_2(self) -> CapList<(K2,)> {
239    CapList::new(self.inner)
240  }
241  #[inline]
242  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
243  pub fn project_at_3(self) -> CapList<(K3,)> {
244    CapList::new(self.inner)
245  }
246}
247
248impl<K0: CapabilityKey, K1: CapabilityKey, K2: CapabilityKey, K3: CapabilityKey, K4: CapabilityKey>
249  CapList<(K0, K1, K2, K3, K4)>
250{
251  #[inline]
252  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
253  pub fn project_at_0(self) -> CapList<(K0,)> {
254    CapList::new(self.inner)
255  }
256  #[inline]
257  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
258  pub fn project_at_1(self) -> CapList<(K1,)> {
259    CapList::new(self.inner)
260  }
261  #[inline]
262  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
263  pub fn project_at_2(self) -> CapList<(K2,)> {
264    CapList::new(self.inner)
265  }
266  #[inline]
267  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
268  pub fn project_at_3(self) -> CapList<(K3,)> {
269    CapList::new(self.inner)
270  }
271  #[inline]
272  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
273  pub fn project_at_4(self) -> CapList<(K4,)> {
274    CapList::new(self.inner)
275  }
276}
277
278impl<
279  K0: CapabilityKey,
280  K1: CapabilityKey,
281  K2: CapabilityKey,
282  K3: CapabilityKey,
283  K4: CapabilityKey,
284  K5: CapabilityKey,
285> CapList<(K0, K1, K2, K3, K4, K5)>
286{
287  #[inline]
288  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
289  pub fn project_at_0(self) -> CapList<(K0,)> {
290    CapList::new(self.inner)
291  }
292  #[inline]
293  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
294  pub fn project_at_1(self) -> CapList<(K1,)> {
295    CapList::new(self.inner)
296  }
297  #[inline]
298  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
299  pub fn project_at_2(self) -> CapList<(K2,)> {
300    CapList::new(self.inner)
301  }
302  #[inline]
303  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
304  pub fn project_at_3(self) -> CapList<(K3,)> {
305    CapList::new(self.inner)
306  }
307  #[inline]
308  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
309  pub fn project_at_4(self) -> CapList<(K4,)> {
310    CapList::new(self.inner)
311  }
312  #[inline]
313  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
314  pub fn project_at_5(self) -> CapList<(K5,)> {
315    CapList::new(self.inner)
316  }
317}
318
319impl<
320  K0: CapabilityKey,
321  K1: CapabilityKey,
322  K2: CapabilityKey,
323  K3: CapabilityKey,
324  K4: CapabilityKey,
325  K5: CapabilityKey,
326  K6: CapabilityKey,
327> CapList<(K0, K1, K2, K3, K4, K5, K6)>
328{
329  #[inline]
330  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
331  pub fn project_at_0(self) -> CapList<(K0,)> {
332    CapList::new(self.inner)
333  }
334  #[inline]
335  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
336  pub fn project_at_1(self) -> CapList<(K1,)> {
337    CapList::new(self.inner)
338  }
339  #[inline]
340  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
341  pub fn project_at_2(self) -> CapList<(K2,)> {
342    CapList::new(self.inner)
343  }
344  #[inline]
345  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
346  pub fn project_at_3(self) -> CapList<(K3,)> {
347    CapList::new(self.inner)
348  }
349  #[inline]
350  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
351  pub fn project_at_4(self) -> CapList<(K4,)> {
352    CapList::new(self.inner)
353  }
354  #[inline]
355  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
356  pub fn project_at_5(self) -> CapList<(K5,)> {
357    CapList::new(self.inner)
358  }
359  #[inline]
360  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
361  pub fn project_at_6(self) -> CapList<(K6,)> {
362    CapList::new(self.inner)
363  }
364}
365
366impl<
367  K0: CapabilityKey,
368  K1: CapabilityKey,
369  K2: CapabilityKey,
370  K3: CapabilityKey,
371  K4: CapabilityKey,
372  K5: CapabilityKey,
373  K6: CapabilityKey,
374  K7: CapabilityKey,
375> CapList<(K0, K1, K2, K3, K4, K5, K6, K7)>
376{
377  #[inline]
378  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
379  pub fn project_at_0(self) -> CapList<(K0,)> {
380    CapList::new(self.inner)
381  }
382  #[inline]
383  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
384  pub fn project_at_1(self) -> CapList<(K1,)> {
385    CapList::new(self.inner)
386  }
387  #[inline]
388  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
389  pub fn project_at_2(self) -> CapList<(K2,)> {
390    CapList::new(self.inner)
391  }
392  #[inline]
393  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
394  pub fn project_at_3(self) -> CapList<(K3,)> {
395    CapList::new(self.inner)
396  }
397  #[inline]
398  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
399  pub fn project_at_4(self) -> CapList<(K4,)> {
400    CapList::new(self.inner)
401  }
402  #[inline]
403  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
404  pub fn project_at_5(self) -> CapList<(K5,)> {
405    CapList::new(self.inner)
406  }
407  #[inline]
408  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
409  pub fn project_at_6(self) -> CapList<(K6,)> {
410    CapList::new(self.inner)
411  }
412  #[inline]
413  /// Project to the capability at this index (shared [`Env`], type-level single-key subset).
414  pub fn project_at_7(self) -> CapList<(K7,)> {
415    CapList::new(self.inner)
416  }
417}
418
419impl<K0: CapabilityKey, K1: CapabilityKey, K2: CapabilityKey> CapWiden<CapList<(K0,)>>
420  for CapList<(K0, K1, K2)>
421{
422  fn widen(self) -> CapList<(K0,)> {
423    CapList::new(self.inner)
424  }
425}
426
427impl<K0: CapabilityKey, K1: CapabilityKey, K2: CapabilityKey> CapWiden<CapList<(K0, K1)>>
428  for CapList<(K0, K1, K2)>
429{
430  fn widen(self) -> CapList<(K0, K1)> {
431    CapList::new(self.inner)
432  }
433}
434
435#[cfg(test)]
436#[allow(dead_code, clippy::new_ret_no_self)]
437mod tests {
438  use super::*;
439  use crate::Cap;
440  #[derive(Clone, Copy, PartialEq, Eq, Debug)]
441  struct Test(pub u32);
442
443  #[test]
444  fn cap_list_verify_missing() {
445    let env = Env::new();
446    let err = CapList::<(Cap<Test>,)>::verify(&env).unwrap_err();
447    assert!(matches!(err, CapabilityError::Missing(_)));
448  }
449
450  #[test]
451  fn cap_list_verify_ok() {
452    let mut env = Env::new();
453    env.insert::<Cap<Test>>(Test(7u32));
454    CapList::<(Cap<Test>,)>::verify(&env).unwrap();
455  }
456
457  #[test]
458  fn cap_list_eight_keys() {
459    #[derive(Clone, Copy)]
460    struct Cap0(pub u8);
461    #[derive(Clone, Copy)]
462    struct Cap1(pub u8);
463    #[derive(Clone, Copy)]
464    struct Cap2(pub u8);
465    #[derive(Clone, Copy)]
466    struct Cap3(pub u8);
467    #[derive(Clone, Copy)]
468    struct Cap4(pub u8);
469    #[derive(Clone, Copy)]
470    struct Cap5(pub u8);
471    #[derive(Clone, Copy)]
472    struct Cap6(pub u8);
473    #[derive(Clone, Copy)]
474    struct Cap7(pub u8);
475    let mut env = Env::new();
476    env.insert::<Cap<Cap0>>(Cap0(0u8));
477    env.insert::<Cap<Cap1>>(Cap1(1u8));
478    env.insert::<Cap<Cap2>>(Cap2(2u8));
479    env.insert::<Cap<Cap3>>(Cap3(3u8));
480    env.insert::<Cap<Cap4>>(Cap4(4u8));
481    env.insert::<Cap<Cap5>>(Cap5(5u8));
482    env.insert::<Cap<Cap6>>(Cap6(6u8));
483    env.insert::<Cap<Cap7>>(Cap7(7u8));
484    CapList::<(
485      Cap<Cap0>,
486      Cap<Cap1>,
487      Cap<Cap2>,
488      Cap<Cap3>,
489      Cap<Cap4>,
490      Cap<Cap5>,
491      Cap<Cap6>,
492      Cap<Cap7>,
493    )>::verify(&env)
494    .unwrap();
495  }
496
497  #[test]
498  fn no_caps_and_unit_from_env() {
499    assert!(NoCaps::verify(&Env::new()).is_ok());
500    assert!(<() as CapabilitySet>::verify(&Env::new()).is_ok());
501    let _ = NoCaps::from_env(Env::new());
502    let _ = <() as FromEnv>::from_env(Env::new());
503  }
504
505  #[test]
506  fn env_as_capability_set() {
507    let env = Env::new();
508    assert!(Env::verify(&env).is_ok());
509    assert_eq!(Env::from_env(env.clone()).len(), env.len());
510  }
511
512  #[test]
513  fn cap_list_accessors_and_deref() {
514    let mut env = Env::new();
515    env.insert::<Cap<Test>>(Test(3u32));
516    let mut caps = CapList::<(Cap<Test>,)>::from_env(env);
517    assert_eq!(caps.env().len(), 1);
518    caps.env_mut().insert::<Cap<Test>>(Test(4u32));
519    assert_eq!(caps.get::<Cap<Test>>().0, 4);
520  }
521
522  #[test]
523  fn cap_list_project_four_through_eight() {
524    #[derive(Clone, Copy)]
525    struct A(pub i32);
526    #[derive(Clone, Copy)]
527    struct B(pub i32);
528    #[derive(Clone, Copy)]
529    struct C(pub i32);
530    #[derive(Clone, Copy)]
531    struct D(pub i32);
532    #[derive(Clone, Copy)]
533    struct E(pub i32);
534    #[derive(Clone, Copy)]
535    struct F(pub i32);
536    #[derive(Clone, Copy)]
537    struct G(pub i32);
538    #[derive(Clone, Copy)]
539    struct H(pub i32);
540    let mut env = Env::new();
541    env.insert::<Cap<A>>(A(1));
542    env.insert::<Cap<B>>(B(2));
543    env.insert::<Cap<C>>(C(3));
544    env.insert::<Cap<D>>(D(4));
545    env.insert::<Cap<E>>(E(5));
546    env.insert::<Cap<F>>(F(6));
547    env.insert::<Cap<G>>(G(7));
548    env.insert::<Cap<H>>(H(8));
549    CapList::<(Cap<A>, Cap<B>, Cap<C>, Cap<D>, Cap<E>)>::verify(&env).unwrap();
550    let wide8 = CapList::<(
551      Cap<A>,
552      Cap<B>,
553      Cap<C>,
554      Cap<D>,
555      Cap<E>,
556      Cap<F>,
557      Cap<G>,
558      Cap<H>,
559    )>::from_env(env.clone());
560    let _ = wide8.clone().project_at_4();
561    let _ = wide8.clone().project_at_5();
562    let _ = wide8.clone().project_at_6();
563    let _ = wide8.clone().project_at_7();
564    let four = CapList::<(Cap<A>, Cap<B>, Cap<C>, Cap<D>)>::from_env(env);
565    let _ = four.clone().project_at_0();
566    let _ = four.clone().project_at_1();
567    let _ = four.clone().project_at_2();
568    let _ = four.project_at_3();
569  }
570  #[test]
571  fn cap_widen_subset() {
572    #[derive(Clone, Copy)]
573    struct Db(pub u32);
574    #[derive(Clone, Copy)]
575    struct Log(pub u32);
576    let mut env = Env::new();
577    env.insert::<Cap<Db>>(Db(1u32));
578    env.insert::<Cap<Log>>(Log(2u32));
579    let wide = CapList::<(Cap<Db>, Cap<Log>)>::from_env(env);
580    let _narrow: CapList<(Cap<Db>,)> = wide.widen();
581  }
582}