openmina_core/substate.rs
1use redux::Dispatcher;
2
3pub type SubstateResult<T> = Result<T, String>;
4
5/// A trait for obtaining immutable and mutable references to a substate of type `T`.
6///
7/// # Example
8///
9/// ```ignore
10/// impl SubstateAccess<P2pState> for State {
11/// fn substate(&self) -> SubstateResult<&P2pState> {
12/// self.p2p
13/// .ready()
14/// .ok_or_else(|| "P2P state unavailable. P2P layer is not ready".to_owned())
15/// }
16///
17/// fn substate_mut(&mut self) -> SubstateResult<&mut P2pState> {
18/// self.p2p
19/// .ready_mut()
20/// .ok_or_else(|| "P2P state unavailable. P2P layer is not ready".to_owned())
21/// }
22/// }
23/// ```
24pub trait SubstateAccess<T> {
25 /// Attempts to obtain an immutable reference to the substate.
26 ///
27 /// In case of failure, an error `String` describing the reason is returned.
28 fn substate(&self) -> SubstateResult<&T>;
29
30 /// Attempts to obtain a mutable reference to the substate.
31 ///
32 /// In case of failure, an error `String` describing the reason is returned.
33 fn substate_mut(&mut self) -> SubstateResult<&mut T>;
34}
35
36/// Substate context which provides mutable access to a substate of type `S`, and can
37/// be consumed to obtain a [redux::Dispatcher] and a parent state of type `T`.
38pub struct Substate<'a, A, T, S> {
39 state: &'a mut T,
40 dispatcher: &'a mut Dispatcher<A, T>,
41 _marker: std::marker::PhantomData<S>,
42}
43
44impl<'a, A, T, S> Substate<'a, A, T, S>
45where
46 T: SubstateAccess<S>,
47{
48 /// Creates a new instance from a parent state and dispatcher.
49 pub fn new(state: &'a mut T, dispatcher: &'a mut Dispatcher<A, T>) -> Self {
50 Self {
51 state,
52 dispatcher,
53 _marker: Default::default(),
54 }
55 }
56
57 /// Creates a new instance from an already existing [Substate] for the same parent state.
58 pub fn from_compatible_substate<OS>(other: Substate<'a, A, T, OS>) -> Substate<'a, A, T, S> {
59 let Substate {
60 state, dispatcher, ..
61 } = other;
62
63 Self::new(state, dispatcher)
64 }
65
66 /// Obtain an immutable reference to the state.
67 ///
68 ///
69 /// WARNING: Should only be used in tests and for debugging
70 pub fn unsafe_get_state(&self) -> &T {
71 self.state
72 }
73
74 /// Attempts to obtain an immutable reference to the substate.
75 ///
76 /// In case of failure, an error `String` describing the reason is returned.
77 pub fn get_substate(&self) -> SubstateResult<&S> {
78 self.state.substate()
79 }
80
81 /// Attempts to obtain a mutable reference to the substate.
82 ///
83 /// In case of failure, an error `String` describing the reason is returned.
84 pub fn get_substate_mut(&mut self) -> SubstateResult<&mut S> {
85 self.state.substate_mut()
86 }
87
88 /// Consumes itself to produce a reference to a dispatcher and parent state.
89 pub fn into_dispatcher_and_state(self) -> (&'a mut Dispatcher<A, T>, &'a T) {
90 (self.dispatcher, self.state)
91 }
92
93 /// Consumes itself to produce a reference to a dispatcher.
94 pub fn into_dispatcher(self) -> &'a mut Dispatcher<A, T> {
95 self.dispatcher
96 }
97}
98
99/// Helper macro for the trivial substate access pattern.
100///
101/// # Example:
102///
103/// ```ignore
104/// impl_substate_access!(State, TransitionFrontierSyncState, transition_frontier.sync);
105/// ```
106#[macro_export]
107macro_rules! impl_substate_access {
108 ($state:ty, $substate_type:ty, $($substate_path:tt)*) => {
109 impl $crate::SubstateAccess<$substate_type> for $state {
110 fn substate(&self) -> $crate::SubstateResult<&$substate_type> {
111 Ok(&self.$($substate_path)*)
112 }
113
114 fn substate_mut(&mut self) -> $crate::SubstateResult<&mut $substate_type> {
115 Ok(&mut self.$($substate_path)*)
116 }
117 }
118 };
119}