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