intrepid_core/action/
handler.rs

1use futures::Stream;
2
3use crate::{Action, ActionContext, BoxedAction, Context, Extractor, Frame, FrameOutbox, Ready};
4
5use super::{ActiveAction, CandidateAction, ReadyAction};
6
7/// An action is an async function that can be turned into an frame handler.
8pub trait Handler<Args, State>: Clone {
9    /// Action futures must resolve into an Frame of some kind.
10    type Future: std::future::Future<Output = crate::Result<Frame>> + Send + 'static;
11
12    /// Invoke the action with a given frame and state, returning the frameual
13    /// frame that will be produced by the action.
14    fn invoke(&self, frame: impl Into<Frame>, state: State) -> Self::Future;
15
16    /// Poll for the action context. This is normally a service, but in some cases,
17    /// it may be a frame handler or a router.
18    fn context(&self) -> ActionContext<State>
19    where
20        State: Send + Sync + 'static,
21    {
22        ActionContext::<State>::Unit
23    }
24
25    /// Convert this action into a type erased actionable service.
26    fn as_into_actionable(&self) -> BoxedAction<State>
27    where
28        Self: Clone + Send + Sync + 'static,
29        Args: Clone + Send + Sync + 'static,
30        State: Clone + Send + Sync + 'static,
31    {
32        Box::new(self.clone().candidate())
33    }
34
35    /// Use `into_stream` to turn the action into a stream and a stream handle. See
36    /// [`Actionable::into_stream`] for more information.
37    ///
38    fn into_stream(self, state: State) -> (impl Stream<Item = crate::Result<Frame>>, FrameOutbox)
39    where
40        Self: Clone + Send + Sync + 'static,
41        Args: Clone + Send + Sync + 'static,
42        State: Clone + Send + Sync + 'static,
43    {
44        self.active(state).into_stream()
45    }
46
47    /// Use `with_state` to turn the action into a stateful action, which can be used
48    /// as a [`tower::Service`].
49    fn ready(self, state: State) -> ReadyAction<Self, Args, State>
50    where
51        Self: Clone + Send + 'static,
52        Args: Clone + Send + 'static,
53        State: Clone + Send + 'static,
54    {
55        Ready::new(self, state).into()
56    }
57
58    /// Create a Candidate actionable from the action, with the given state. Candidates
59    /// can be "suspended" by boxing them for later.
60    fn candidate(self) -> CandidateAction<Self, Args, State>
61    where
62        Self: Clone + Send + 'static,
63        Args: Clone + Send + 'static,
64        State: Clone + Send + 'static,
65    {
66        Action::new(self.clone())
67    }
68
69    /// Create an Active actionable from the action, with the given state.
70    fn active(self, state: State) -> ActiveAction
71    where
72        Self: Clone + Send + Sync + 'static,
73        Args: Clone + Send + Sync + 'static,
74        State: Clone + Send + Sync + 'static,
75    {
76        self.clone().as_into_actionable().into_actionable(state)
77    }
78}
79
80macro_rules! define_handler_for_tuple ({ $($param:ident)* } => {
81    #[allow(non_snake_case, unused_mut, unused_variables)]
82    impl<Func, Future, State, Output, $($param,)*>
83        Handler<($($param,)*), State> for Func
84    where
85        Func: FnOnce($($param,)*) -> Future + Clone + Send + 'static,
86        State: Send + Sync + 'static,
87        Output: Into<Frame>,
88        Future: std::future::Future<Output = Output> + Send + 'static,
89        $(
90            $param: Extractor<State> + Send,
91        )*
92    {
93        type Future = crate::FrameFuture;
94
95        fn invoke(&self, input: impl Into<Frame>, state: State) -> Self::Future {
96            let handler = self.clone();
97            let input = input.into();
98            let action: ActionContext<State> = handler.context();
99            let context = Context::with_action(action, state);
100
101            crate::FrameFuture::from_async_block(async move {
102                let input = &input;
103                let handler = handler;
104                let context = &context;
105
106                $(
107                    let $param = match $param::extract_from_frame_and_state(input.clone(), context) {
108                        Ok(value) => value,
109                        Err(rejection) => return Err(rejection.into()),
110                    };
111                )*
112
113                let response = handler($($param,)*).await;
114
115                Ok(response.into())
116            })
117        }
118    }
119});
120
121define_handler_for_tuple! {}
122define_handler_for_tuple! { A }
123define_handler_for_tuple! { A B }
124define_handler_for_tuple! { A B C }
125define_handler_for_tuple! { A B C D }
126define_handler_for_tuple! { A B C D E }
127define_handler_for_tuple! { A B C D E F }
128define_handler_for_tuple! { A B C D E F G }
129define_handler_for_tuple! { A B C D E F G H }
130define_handler_for_tuple! { A B C D E F G H I }
131define_handler_for_tuple! { A B C D E F G H I J }
132define_handler_for_tuple! { A B C D E F G H I J K }
133define_handler_for_tuple! { A B C D E F G H I J K L }