intrepid_core/
context.rs

1use crate::Router;
2
3/// An action context, describing any additional context that the action may carry.
4/// This is used to distribute system setup information when invoking them as actions,
5/// for example, when systems have defined action routes.
6#[derive(Clone, Debug, Default)]
7pub enum ActionContext<State> {
8    /// The action is being invoked as a single unit.
9    #[default]
10    Unit,
11    /// The action is a collection of actions.
12    System(SystemContext<State>),
13}
14
15impl<State> From<SystemContext<State>> for ActionContext<State> {
16    fn from(context: SystemContext<State>) -> Self {
17        Self::System(context)
18    }
19}
20
21/// A router for actions that can be invoked as services.
22#[derive(Clone, Debug, Default)]
23pub struct SystemContext<State> {
24    /// Any routes that have been mounted on a routed system.
25    pub router: Router<State>,
26}
27
28impl<State> From<Router<State>> for SystemContext<State> {
29    fn from(router: Router<State>) -> Self {
30        Self { router }
31    }
32}
33
34/// An error indicating that the state is not ready.
35#[derive(Debug, thiserror::Error)]
36#[error("Attempted to access state when it was not ready.")]
37pub struct StateNotReadyError;
38
39/// The context of the action. This is handed into actions when they're invoked,
40/// and is the underpinning mechanism for distributing side-effects, as well as
41/// action composition.
42///
43/// At compile time, the action context grants us, and the consumers, the ability
44/// to structure action systems in typesafe ways. At runtime, the context is used
45/// to distribute the state of the system, as well as any additional crate-specific
46/// plumbing that the action may require.
47///
48#[derive(Clone, Debug, Default)]
49pub struct Context<State> {
50    /// The action context, describing any additional context that the action may carry.
51    pub action: ActionContext<State>,
52    /// The state of the system, determined by the consumer. This can be absent if the
53    /// various actions are called before being properly initialized. The type system
54    /// should prevent this most of the time by requiring the state to be present before
55    /// invoking actions or calling them as services.
56    pub state: Option<State>,
57}
58
59impl<State> Context<State> {
60    /// Create a new context with the given action.
61    pub fn from_action(action: ActionContext<State>) -> Self {
62        Self {
63            action,
64            state: None,
65        }
66    }
67
68    /// Create a new context with the given state.
69    pub fn from_state(state: State) -> Self {
70        Self {
71            action: ActionContext::Unit,
72            state: Some(state),
73        }
74    }
75
76    /// Return the state. Returns an error if the state is not ready.
77    pub fn state(&self) -> Result<State, StateNotReadyError>
78    where
79        State: Clone,
80    {
81        match self.state.clone() {
82            Some(state) => Ok(state),
83            None => Err(StateNotReadyError),
84        }
85    }
86
87    /// Create a new context with the given state and action context.
88    pub fn with_action(action: ActionContext<State>, state: State) -> Self {
89        Self {
90            action,
91            state: Some(state),
92        }
93    }
94}
95
96impl<State> From<ActionContext<State>> for Context<State> {
97    fn from(action: ActionContext<State>) -> Self {
98        Self::from_action(action)
99    }
100}
101
102impl<State> From<State> for Context<State> {
103    fn from(state: State) -> Self {
104        Self::from_state(state)
105    }
106}