intrepid_core/action/
handler.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use futures::Stream;

use crate::{Action, ActionContext, BoxedAction, Context, Extractor, Frame, FrameOutbox, Ready};

use super::{ActiveAction, CandidateAction, ReadyAction};

/// An action is an async function that can be turned into an frame handler.
pub trait Handler<Args, State>: Clone {
    /// Action futures must resolve into an Frame of some kind.
    type Future: std::future::Future<Output = crate::Result<Frame>> + Send + 'static;

    /// Invoke the action with a given frame and state, returning the frameual
    /// frame that will be produced by the action.
    fn invoke(&self, frame: impl Into<Frame>, state: State) -> Self::Future;

    /// Poll for the action context. This is normally a service, but in some cases,
    /// it may be a frame handler or a router.
    fn context(&self) -> ActionContext<State>
    where
        State: Send + Sync + 'static,
    {
        ActionContext::<State>::Unit
    }

    /// Create a new [`RequestHandler`] from the action. This is a struct that implements
    /// the axum [`Handler`] trait, and therefore can be used as a route handler.
    ///
    /// [`RequestHandler`]: crate::RequestHandler
    /// [`Handler`]: axum::handler::Handler
    fn http_handler(&self) -> crate::RequestHandler<Self, Args, State>
    where
        Self: Handler<Args, State> + Clone + Send + Sync + 'static,
        Args: Clone + Send + Sync + 'static,
        State: Clone + Send + Sync + 'static,
    {
        crate::RequestHandler::new(self.clone())
    }

    /// Convert this action into a type erased actionable service.
    fn as_into_actionable(&self) -> BoxedAction<State>
    where
        Self: Clone + Send + Sync + 'static,
        Args: Clone + Send + Sync + 'static,
        State: Clone + Send + Sync + 'static,
    {
        Box::new(self.clone().candidate())
    }

    /// Use `into_stream` to turn the action into a stream and a stream handle. See
    /// [`Actionable::into_stream`] for more information.
    ///
    fn into_stream(self, state: State) -> (impl Stream<Item = crate::Result<Frame>>, FrameOutbox)
    where
        Self: Clone + Send + Sync + 'static,
        Args: Clone + Send + Sync + 'static,
        State: Clone + Send + Sync + 'static,
    {
        self.active(state).into_stream()
    }

    /// Use `with_state` to turn the action into a stateful action, which can be used
    /// as a [`tower::Service`].
    fn ready(self, state: State) -> ReadyAction<Self, Args, State>
    where
        Self: Clone + Send + 'static,
        Args: Clone + Send + 'static,
        State: Clone + Send + 'static,
    {
        Ready::new(self, state).into()
    }

    /// Create a Candidate actionable from the action, with the given state. Candidates
    /// can be "suspended" by boxing them for later.
    fn candidate(self) -> CandidateAction<Self, Args, State>
    where
        Self: Clone + Send + 'static,
        Args: Clone + Send + 'static,
        State: Clone + Send + 'static,
    {
        Action::new(self.clone())
    }

    /// Create an Active actionable from the action, with the given state.
    fn active(self, state: State) -> ActiveAction
    where
        Self: Clone + Send + Sync + 'static,
        Args: Clone + Send + Sync + 'static,
        State: Clone + Send + Sync + 'static,
    {
        self.clone().as_into_actionable().into_actionable(state)
    }
}

macro_rules! define_handler_for_tuple ({ $($param:ident)* } => {
    #[allow(non_snake_case, unused_mut, unused_variables)]
    impl<Func, Future, State, Output, $($param,)*>
        Handler<($($param,)*), State> for Func
    where
        Func: FnOnce($($param,)*) -> Future + Clone + Send + 'static,
        State: Send + Sync + 'static,
        Output: Into<Frame>,
        Future: std::future::Future<Output = Output> + Send + 'static,
        $(
            $param: Extractor<State> + Send,
        )*
    {
        type Future = crate::FrameFuture;

        fn invoke(&self, input: impl Into<Frame>, state: State) -> Self::Future {
            let handler = self.clone();
            let input = input.into();
            let action: ActionContext<State> = handler.context();
            let context = Context::with_action(action, state);

            crate::FrameFuture::from_async_block(async move {
                let input = &input;
                let handler = handler;
                let context = &context;

                $(
                    let $param = match $param::extract_from_frame_and_state(input.clone(), context) {
                        Ok(value) => value,
                        Err(rejection) => return Err(rejection.into()),
                    };
                )*

                let response = handler($($param,)*).await;

                Ok(response.into())
            })
        }
    }
});

define_handler_for_tuple! {}
define_handler_for_tuple! { A }
define_handler_for_tuple! { A B }
define_handler_for_tuple! { A B C }
define_handler_for_tuple! { A B C D }
define_handler_for_tuple! { A B C D E }
define_handler_for_tuple! { A B C D E F }
define_handler_for_tuple! { A B C D E F G }
define_handler_for_tuple! { A B C D E F G H }
define_handler_for_tuple! { A B C D E F G H I }
define_handler_for_tuple! { A B C D E F G H I J }
define_handler_for_tuple! { A B C D E F G H I J K }
define_handler_for_tuple! { A B C D E F G H I J K L }