1use std::future::Future;
26use std::marker::PhantomData;
27use std::pin::Pin;
28
29use typeway_core::ApiSpec;
30
31use crate::body::BoxBody;
32use crate::extract::{FromRequest, FromRequestParts};
33use crate::handler::BoxedHandler;
34use crate::handler_for::{BindableEndpoint, BoundHandler};
35use crate::response::IntoResponse;
36
37pub struct Strict<E> {
42 _marker: PhantomData<E>,
43}
44
45impl<E: ApiSpec> ApiSpec for Strict<E> {}
46
47pub trait HasResType {
52 type Res;
53}
54
55impl<M: typeway_core::HttpMethod, P: typeway_core::PathSpec, Req, Res, Q, Err> HasResType
56 for typeway_core::Endpoint<M, P, Req, Res, Q, Err>
57{
58 type Res = Res;
59}
60
61pub trait HasErrType {
63 type Err;
64}
65
66impl<M: typeway_core::HttpMethod, P: typeway_core::PathSpec, Req, Res, Q, Err> HasErrType
67 for typeway_core::Endpoint<M, P, Req, Res, Q, Err>
68{
69 type Err = Err;
70}
71
72pub trait StrictHandler<Res, Args>: Clone + Send + Sync + 'static {
74 fn call(
75 self,
76 parts: http::request::Parts,
77 body: bytes::Bytes,
78 ) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>>;
79}
80
81impl<F, Fut, Res> StrictHandler<Res, ()> for F
83where
84 F: FnOnce() -> Fut + Clone + Send + Sync + 'static,
85 Fut: Future<Output = Res> + Send,
86 Res: IntoResponse,
87{
88 fn call(
89 self,
90 _parts: http::request::Parts,
91 _body: bytes::Bytes,
92 ) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
93 Box::pin(async move { self().await.into_response() })
94 }
95}
96
97macro_rules! impl_strict_handler {
99 ([$($T:ident),+], [$($t:ident),+]) => {
100 #[allow(non_snake_case)]
101 impl<F, Fut, Res, $($T,)+> StrictHandler<Res, ($($T,)+)> for F
102 where
103 F: FnOnce($($T,)+) -> Fut + Clone + Send + Sync + 'static,
104 Fut: Future<Output = Res> + Send,
105 Res: IntoResponse,
106 $($T: FromRequestParts + 'static,)+
107 {
108 fn call(
109 self,
110 parts: http::request::Parts,
111 _body: bytes::Bytes,
112 ) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
113 Box::pin(async move {
114 $(
115 let $t = match $T::from_request_parts(&parts) {
116 Ok(v) => v,
117 Err(e) => return e.into_response(),
118 };
119 )+
120 self($($t,)+).await.into_response()
121 })
122 }
123 }
124 };
125}
126
127impl_strict_handler!([T1], [t1]);
128impl_strict_handler!([T1, T2], [t1, t2]);
129impl_strict_handler!([T1, T2, T3], [t1, t2, t3]);
130impl_strict_handler!([T1, T2, T3, T4], [t1, t2, t3, t4]);
131impl_strict_handler!([T1, T2, T3, T4, T5], [t1, t2, t3, t4, t5]);
132impl_strict_handler!([T1, T2, T3, T4, T5, T6], [t1, t2, t3, t4, t5, t6]);
133
134pub struct StrictWithBody<Parts, Body>(PhantomData<(Parts, Body)>);
136
137impl<F, Fut, Res, B> StrictHandler<Res, StrictWithBody<(), B>> for F
138where
139 F: FnOnce(B) -> Fut + Clone + Send + Sync + 'static,
140 Fut: Future<Output = Res> + Send,
141 Res: IntoResponse,
142 B: FromRequest + 'static,
143{
144 fn call(
145 self,
146 parts: http::request::Parts,
147 body: bytes::Bytes,
148 ) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
149 Box::pin(async move {
150 let b = match B::from_request(&parts, body).await {
151 Ok(v) => v,
152 Err(e) => return e.into_response(),
153 };
154 self(b).await.into_response()
155 })
156 }
157}
158
159macro_rules! impl_strict_handler_body_marker {
160 ([$($T:ident),+], [$($t:ident),+]) => {
161 #[allow(non_snake_case)]
162 impl<F, Fut, Res, $($T,)+ B> StrictHandler<Res, StrictWithBody<($($T,)+), B>> for F
163 where
164 F: FnOnce($($T,)+ B) -> Fut + Clone + Send + Sync + 'static,
165 Fut: Future<Output = Res> + Send,
166 Res: IntoResponse,
167 $($T: FromRequestParts + 'static,)+
168 B: FromRequest + 'static,
169 {
170 fn call(
171 self,
172 parts: http::request::Parts,
173 body: bytes::Bytes,
174 ) -> Pin<Box<dyn Future<Output = http::Response<BoxBody>> + Send>> {
175 Box::pin(async move {
176 $(
177 let $t = match $T::from_request_parts(&parts) {
178 Ok(v) => v,
179 Err(e) => return e.into_response(),
180 };
181 )+
182 let b = match B::from_request(&parts, body).await {
183 Ok(v) => v,
184 Err(e) => return e.into_response(),
185 };
186 self($($t,)+ b).await.into_response()
187 })
188 }
189 }
190 };
191}
192
193impl_strict_handler_body_marker!([T1], [t1]);
194impl_strict_handler_body_marker!([T1, T2], [t1, t2]);
195impl_strict_handler_body_marker!([T1, T2, T3], [t1, t2, t3]);
196impl_strict_handler_body_marker!([T1, T2, T3, T4], [t1, t2, t3, t4]);
197impl_strict_handler_body_marker!([T1, T2, T3, T4, T5], [t1, t2, t3, t4, t5]);
198
199pub trait StrictEndpoint {
205 type Inner: BindableEndpoint + HasResType;
206}
207
208impl<E: BindableEndpoint + HasResType> StrictEndpoint for Strict<E> {
209 type Inner = E;
210}
211
212pub fn bind_strict<S, H, Args>(handler: H) -> BoundHandler<S>
216where
217 S: StrictEndpoint,
218 H: StrictHandler<<S::Inner as HasResType>::Res, Args>,
219 Args: 'static,
220{
221 let method = S::Inner::method();
222 let pattern = S::Inner::pattern();
223 let match_fn = S::Inner::match_fn();
224
225 let boxed: BoxedHandler = std::sync::Arc::new(move |parts, body| {
226 let h = handler.clone();
227 h.call(parts, body)
228 });
229
230 BoundHandler::new(method, pattern, match_fn, boxed)
231}
232
233#[macro_export]
235macro_rules! bind_strict {
236 ($handler:expr) => {
237 $crate::typed_response::bind_strict::<_, _, _>($handler)
238 };
239}