intrepid_core/system/
open.rs

1use tower::Service;
2
3use crate::{ActionContext, Frame, FrameFuture, Handler};
4
5use super::{
6    direct_batch::Batch, dispatch_batch::DispatchBatch, first_match::FirstMatch, routed::Routed,
7    Stateful, StatelessSystem, System,
8};
9
10/// A system that is still being initialized.
11#[derive(Clone, Copy, Debug, Default)]
12pub struct Open;
13
14/// A system that is still being initialized. Its status is open because its state is not yet
15/// determined. This is where systems begin, and most constructor types are defined here because
16/// that enables us to pre-define the opening status.
17///
18pub type OpenSystem<State> = System<Open, State>;
19
20impl<State> OpenSystem<State>
21where
22    State: Clone + Send + Sync + 'static,
23{
24    /// Create a new batch system. Batch systems invoke all actions with a frame,
25    /// returning a Frame that contains a list of frames.
26    pub fn batch() -> Self {
27        Self::Batch(Batch::init())
28    }
29
30    /// Create a new dispatched system that aggregates responses from all matching actions
31    /// into a list. Dispatch systems match actions exactly using a full path.
32    pub fn dispatch_batch() -> Self {
33        Self::DispatchBatch(DispatchBatch::init())
34    }
35
36    /// Create a new routed system which aggregates responses from all matching actions.
37    /// Routed systems match actions using a composed path fragment.
38    pub fn routed() -> Self {
39        Self::Routed(Routed::init())
40    }
41
42    /// Create a new first match system.
43    pub fn first_match() -> Self {
44        Self::FirstMatch(FirstMatch::init())
45    }
46
47    /// Transition to a stateful system with a given state.
48    pub fn with_state(&self, state: State) -> System<Stateful<State>, State> {
49        match self {
50            Self::Batch(system) => System::Batch(system.with_state(state)),
51            Self::DispatchBatch(system) => System::DispatchBatch(system.with_state(state)),
52            Self::Routed(system) => System::Routed(system.with_state(state)),
53            Self::FirstMatch(system) => System::FirstMatch(system.with_state(state)),
54        }
55    }
56
57    fn handle_frame_with_state(&self, frame: Frame, state: State) -> FrameFuture {
58        match self {
59            Self::Batch(system) => system.handle_frame_with_state(frame, state),
60            Self::DispatchBatch(system) => system.handle_frame_with_state(frame, state),
61            Self::Routed(system) => system.handle_frame_with_state(frame, state),
62            Self::FirstMatch(system) => system.handle_frame_with_state(frame, state),
63        }
64    }
65}
66
67impl OpenSystem<()> {
68    /// If we know that the state is empty, we know that we can transition to a stateless system.
69    /// Transition to a stateless system.
70    pub fn without_state(self) -> StatelessSystem {
71        match self {
72            Self::Batch(system) => System::Batch(system.without_state()),
73            Self::DispatchBatch(system) => System::DispatchBatch(system.without_state()),
74            Self::Routed(system) => System::Routed(system.without_state()),
75            Self::FirstMatch(system) => System::FirstMatch(system.without_state()),
76        }
77    }
78
79    /// Call the system with a frame.
80    pub fn handle_frame(self, frame: Frame) -> FrameFuture {
81        self.handle_frame_with_state(frame, ())
82    }
83}
84
85impl<State> Handler<Frame, State> for OpenSystem<State>
86where
87    State: Clone + Send + Sync + 'static,
88{
89    type Future = FrameFuture;
90
91    fn invoke(&self, frame: impl Into<Frame>, state: State) -> Self::Future {
92        self.handle_frame_with_state(frame.into(), state)
93    }
94
95    fn context(&self) -> ActionContext<State> {
96        match self {
97            Self::Batch(system) => system.action_context(),
98            Self::DispatchBatch(system) => system.action_context(),
99            Self::Routed(system) => system.action_context(),
100            Self::FirstMatch(system) => system.action_context(),
101        }
102    }
103}
104
105impl<IntoFrame> Service<IntoFrame> for System<Open, ()>
106where
107    IntoFrame: Into<Frame> + Clone + Send + 'static,
108{
109    type Response = Frame;
110    type Error = crate::Error;
111    type Future = FrameFuture;
112
113    fn poll_ready(
114        &mut self,
115        _: &mut std::task::Context<'_>,
116    ) -> std::task::Poll<Result<(), Self::Error>> {
117        std::task::Poll::Ready(Ok(()))
118    }
119
120    fn call(&mut self, frame: IntoFrame) -> Self::Future {
121        let frame: Frame = frame.into();
122        let instance = self.clone();
123
124        instance.handle_frame(frame)
125    }
126}