Skip to main content

commonware_runtime/utils/
cell.rs

1use std::ops::{Deref, DerefMut};
2
3const MISSING_CONTEXT: &str = "runtime context missing";
4const DUPLICATE_CONTEXT: &str = "runtime context already present";
5
6/// Spawn a task using a [`Cell`] by taking its context, restoring the context synchronously
7/// in the spawned closure, and returning the provided future directly to the runtime.
8///
9/// The macro uses the context's default spawn configuration (supervised, shared executor with
10/// `blocking == false`). If you need to mark the task as blocking or request a dedicated thread,
11/// take the context via [`Cell::take`] and call the appropriate [`crate::Spawner`] methods before spawning.
12#[macro_export]
13macro_rules! spawn_cell {
14    ($cell:expr, $body:expr $(,)?) => {{
15        let __commonware_context = $cell.take();
16        $crate::Spawner::spawn(__commonware_context, move |context| {
17            $cell.restore(context);
18            $body
19        })
20    }};
21}
22
23/// A wrapper around context that allows it to be taken and returned without requiring
24/// all interactions to unwrap (as with `Option<C>`).
25#[derive(Debug)]
26pub enum Cell<C> {
27    /// A context available for use.
28    Present(C),
29    /// The context has been taken elsewhere.
30    Missing,
31}
32
33impl<C> Cell<C> {
34    /// Create a new slot containing `context`.
35    pub const fn new(context: C) -> Self {
36        Self::Present(context)
37    }
38
39    /// Remove the context from the slot, panicking if it is missing.
40    pub fn take(&mut self) -> C {
41        match std::mem::replace(self, Self::Missing) {
42            Self::Present(context) => context,
43            Self::Missing => panic!("{}", MISSING_CONTEXT),
44        }
45    }
46
47    /// Return a context to the slot, panicking if one is already present.
48    pub fn restore(&mut self, context: C) {
49        match self {
50            Self::Present(_) => panic!("{}", DUPLICATE_CONTEXT),
51            Self::Missing => {
52                *self = Self::Present(context);
53            }
54        }
55    }
56
57    /// Returns a reference to the context.
58    ///
59    /// # Panics
60    ///
61    /// Panics if the context is missing.
62    pub fn as_present(&self) -> &C {
63        match self {
64            Self::Present(context) => context,
65            Self::Missing => panic!("{}", MISSING_CONTEXT),
66        }
67    }
68
69    /// Returns a mutable reference to the context.
70    ///
71    /// # Panics
72    ///
73    /// Panics if the context is missing.
74    pub fn as_present_mut(&mut self) -> &mut C {
75        match self {
76            Self::Present(context) => context,
77            Self::Missing => panic!("{}", MISSING_CONTEXT),
78        }
79    }
80
81    /// Consume the slot, returning the context.
82    ///
83    /// # Panics
84    ///
85    /// Panics if the context is missing.
86    pub fn into_present(self) -> C {
87        match self {
88            Self::Present(context) => context,
89            Self::Missing => panic!("{}", MISSING_CONTEXT),
90        }
91    }
92}
93
94impl<C> Deref for Cell<C> {
95    type Target = C;
96
97    fn deref(&self) -> &C {
98        self.as_present()
99    }
100}
101
102impl<C> DerefMut for Cell<C> {
103    fn deref_mut(&mut self) -> &mut C {
104        self.as_present_mut()
105    }
106}
107
108impl<C> AsRef<C> for Cell<C> {
109    fn as_ref(&self) -> &C {
110        self.as_present()
111    }
112}
113
114impl<C> AsMut<C> for Cell<C> {
115    fn as_mut(&mut self) -> &mut C {
116        self.as_present_mut()
117    }
118}