Skip to main content

magicstatemachines/shared/
storage.rs

1use crate::state_trait::ErasedState;
2use core::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
3use core::fmt;
4use core::ops::{Deref, DerefMut};
5#[cfg(feature = "std")]
6use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, TryLockError};
7
8/// The state marker and runtime data held by a shared-storage backend.
9///
10/// Its fields are private so backends can synchronize storage without changing
11/// the authoritative state directly.
12pub struct SharedValue<T> {
13    pub(super) state: ErasedState,
14    pub(super) value: T,
15}
16
17/// Failure caused by asking a shared container for the wrong state marker.
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub struct WrongStateError {
20    /// Requested state or union marker type name.
21    pub expected: &'static str,
22    /// Committed concrete state type name.
23    pub actual: &'static str,
24}
25
26impl fmt::Display for WrongStateError {
27    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
28        write!(
29            formatter,
30            "expected state {}, found {}",
31            self.expected, self.actual
32        )
33    }
34}
35
36#[cfg(feature = "std")]
37impl std::error::Error for WrongStateError {}
38
39/// Failure to acquire a typed view of shared state.
40#[derive(Debug)]
41pub enum SharedStateError<StorageError = core::convert::Infallible> {
42    /// The container was borrowed successfully, but the committed state did not match.
43    WrongState(WrongStateError),
44    /// The backing storage could not be borrowed or locked.
45    Storage(StorageError),
46}
47
48impl<StorageError> fmt::Display for SharedStateError<StorageError>
49where
50    StorageError: fmt::Display,
51{
52    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            Self::WrongState(error) => error.fmt(formatter),
55            Self::Storage(error) => error.fmt(formatter),
56        }
57    }
58}
59
60#[cfg(feature = "std")]
61impl<StorageError> std::error::Error for SharedStateError<StorageError> where
62    StorageError: fmt::Debug + fmt::Display
63{
64}
65
66impl<StorageError> From<WrongStateError> for SharedStateError<StorageError> {
67    fn from(error: WrongStateError) -> Self {
68        Self::WrongState(error)
69    }
70}
71
72/// Guard types used by runtime-erased state views.
73///
74/// This is the part needed by [`StorageStateRef`](crate::StorageStateRef) and
75/// [`StorageStateMut`](crate::StorageStateMut). Full shared containers also
76/// implement [`SharedStorage`], which adds construction and borrow methods.
77pub trait SharedStorageView {
78    /// Concrete cell or lock type containing [`SharedValue<T>`].
79    type Storage<T>;
80
81    /// Guard returned by read access.
82    type ReadGuard<'a, T>: Deref<Target = SharedValue<T>>
83    where
84        Self: 'a,
85        T: 'a;
86
87    /// Guard returned by write access.
88    type WriteGuard<'a, T>: DerefMut<Target = SharedValue<T>>
89    where
90        Self: 'a,
91        T: 'a;
92}
93
94/// Replaceable storage backend for [`super::SharedState`].
95///
96/// Implement this trait when the built-in [`RefCellStorage`],
97/// [`MutexStorage`], and [`RwLockStorage`] do not match the container you want
98/// to use. The backend owns the actual synchronization primitive and returns
99/// guard types that dereference to [`SharedValue<T>`].
100///
101/// The library does not collapse backend errors into a custom borrowed/poisoned
102/// enum. Your `ReadError` and `WriteError` associated types are preserved and
103/// returned as [`SharedStateError::Storage`].
104///
105/// A custom backend has this shape:
106///
107/// ```ignore
108/// use magicstatemachines::{SArc, SharedStorage, SharedValue};
109/// use std::sync::{Mutex, MutexGuard, TryLockError};
110///
111/// pub struct MyMutexStorage;
112///
113/// impl SharedStorage for MyMutexStorage {
114///     type Storage<T> = Mutex<SharedValue<T>>;
115///     type ReadGuard<'a, T> = MutexGuard<'a, SharedValue<T>> where T: 'a;
116///     type WriteGuard<'a, T> = MutexGuard<'a, SharedValue<T>> where T: 'a;
117///     type ReadError<'a, T> = TryLockError<MutexGuard<'a, SharedValue<T>>> where T: 'a;
118///     type WriteError<'a, T> = TryLockError<MutexGuard<'a, SharedValue<T>>> where T: 'a;
119///
120///     fn new<T>(value: SharedValue<T>) -> Self::Storage<T> {
121///         Mutex::new(value)
122///     }
123///
124///     fn read<T>(
125///         storage: &Self::Storage<T>,
126///     ) -> Result<Self::ReadGuard<'_, T>, Self::ReadError<'_, T>> {
127///         storage.try_lock()
128///     }
129///
130///     fn write<T>(
131///         storage: &Self::Storage<T>,
132///     ) -> Result<Self::WriteGuard<'_, T>, Self::WriteError<'_, T>> {
133///         storage.try_lock()
134///     }
135/// }
136///
137/// type SArcMyMutex<T> = SArc<MyMutexStorage, T>;
138/// ```
139pub trait SharedStorage {
140    /// Concrete cell or lock type containing [`SharedValue<T>`].
141    type Storage<T>;
142
143    /// Guard returned by read access.
144    type ReadGuard<'a, T>: Deref<Target = SharedValue<T>>
145    where
146        Self: 'a,
147        T: 'a;
148
149    /// Guard returned by write access.
150    type WriteGuard<'a, T>: DerefMut<Target = SharedValue<T>>
151    where
152        Self: 'a,
153        T: 'a;
154
155    /// Error returned by read access.
156    type ReadError<'a, T>
157    where
158        Self: 'a,
159        T: 'a;
160
161    /// Error returned by write access.
162    type WriteError<'a, T>
163    where
164        Self: 'a,
165        T: 'a;
166
167    /// Creates backend storage containing the authoritative state and runtime data.
168    fn new<T>(value: SharedValue<T>) -> Self::Storage<T>;
169    /// Attempts read access to the backend storage.
170    fn read<T>(
171        storage: &Self::Storage<T>,
172    ) -> Result<Self::ReadGuard<'_, T>, Self::ReadError<'_, T>>;
173    /// Attempts write access to the backend storage.
174    fn write<T>(
175        storage: &Self::Storage<T>,
176    ) -> Result<Self::WriteGuard<'_, T>, Self::WriteError<'_, T>>;
177}
178
179impl<Storage> SharedStorageView for Storage
180where
181    Storage: SharedStorage,
182{
183    type Storage<T> = <Storage as SharedStorage>::Storage<T>;
184    type ReadGuard<'a, T>
185        = <Storage as SharedStorage>::ReadGuard<'a, T>
186    where
187        Storage: 'a,
188        T: 'a;
189    type WriteGuard<'a, T>
190        = <Storage as SharedStorage>::WriteGuard<'a, T>
191    where
192        Storage: 'a,
193        T: 'a;
194}
195
196/// [`SharedStorage`] implementation backed by [`RefCell`].
197pub struct RefCellStorage;
198
199impl SharedStorage for RefCellStorage {
200    type Storage<T> = RefCell<SharedValue<T>>;
201    type ReadGuard<'a, T>
202        = Ref<'a, SharedValue<T>>
203    where
204        T: 'a;
205    type WriteGuard<'a, T>
206        = RefMut<'a, SharedValue<T>>
207    where
208        T: 'a;
209    type ReadError<'a, T>
210        = BorrowError
211    where
212        T: 'a;
213    type WriteError<'a, T>
214        = BorrowMutError
215    where
216        T: 'a;
217
218    fn new<T>(value: SharedValue<T>) -> Self::Storage<T> {
219        RefCell::new(value)
220    }
221
222    fn read<T>(
223        storage: &Self::Storage<T>,
224    ) -> Result<Self::ReadGuard<'_, T>, Self::ReadError<'_, T>> {
225        storage.try_borrow()
226    }
227
228    fn write<T>(
229        storage: &Self::Storage<T>,
230    ) -> Result<Self::WriteGuard<'_, T>, Self::WriteError<'_, T>> {
231        storage.try_borrow_mut()
232    }
233}
234
235/// [`SharedStorage`] implementation backed by [`Mutex`].
236#[cfg(feature = "std")]
237pub struct MutexStorage;
238
239#[cfg(feature = "std")]
240impl SharedStorage for MutexStorage {
241    type Storage<T> = Mutex<SharedValue<T>>;
242    type ReadGuard<'a, T>
243        = MutexGuard<'a, SharedValue<T>>
244    where
245        T: 'a;
246    type WriteGuard<'a, T>
247        = MutexGuard<'a, SharedValue<T>>
248    where
249        T: 'a;
250    type ReadError<'a, T>
251        = TryLockError<MutexGuard<'a, SharedValue<T>>>
252    where
253        T: 'a;
254    type WriteError<'a, T>
255        = TryLockError<MutexGuard<'a, SharedValue<T>>>
256    where
257        T: 'a;
258
259    fn new<T>(value: SharedValue<T>) -> Self::Storage<T> {
260        Mutex::new(value)
261    }
262
263    fn read<T>(
264        storage: &Self::Storage<T>,
265    ) -> Result<Self::ReadGuard<'_, T>, Self::ReadError<'_, T>> {
266        storage.try_lock()
267    }
268
269    fn write<T>(
270        storage: &Self::Storage<T>,
271    ) -> Result<Self::WriteGuard<'_, T>, Self::WriteError<'_, T>> {
272        storage.try_lock()
273    }
274}
275
276/// [`SharedStorage`] implementation backed by [`RwLock`].
277#[cfg(feature = "std")]
278pub struct RwLockStorage;
279
280#[cfg(feature = "std")]
281impl SharedStorage for RwLockStorage {
282    type Storage<T> = RwLock<SharedValue<T>>;
283    type ReadGuard<'a, T>
284        = RwLockReadGuard<'a, SharedValue<T>>
285    where
286        T: 'a;
287    type WriteGuard<'a, T>
288        = RwLockWriteGuard<'a, SharedValue<T>>
289    where
290        T: 'a;
291    type ReadError<'a, T>
292        = TryLockError<RwLockReadGuard<'a, SharedValue<T>>>
293    where
294        T: 'a;
295    type WriteError<'a, T>
296        = TryLockError<RwLockWriteGuard<'a, SharedValue<T>>>
297    where
298        T: 'a;
299
300    fn new<T>(value: SharedValue<T>) -> Self::Storage<T> {
301        RwLock::new(value)
302    }
303
304    fn read<T>(
305        storage: &Self::Storage<T>,
306    ) -> Result<Self::ReadGuard<'_, T>, Self::ReadError<'_, T>> {
307        storage.try_read()
308    }
309
310    fn write<T>(
311        storage: &Self::Storage<T>,
312    ) -> Result<Self::WriteGuard<'_, T>, Self::WriteError<'_, T>> {
313        storage.try_write()
314    }
315}