Skip to main content

magicstatemachines/shared/
storage.rs

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