1use crate::{DirectError, DirectRequest};
7use alux_http::{FromPartsAlg, read_cookies, read_header_name};
8use alux_http_parts::ReadParts;
9use core::fmt::Display;
10use core::future::Future;
11use core::marker::PhantomData;
12use core::str::FromStr;
13use serde::de::DeserializeOwned;
14
15pub struct DirectPathInput<Input>(PhantomData<Input>);
17pub struct DirectQueryInput<Input>(PhantomData<Input>);
19pub struct DirectBodyInput<Input>(PhantomData<Input>);
21pub struct DirectFormInput<Input>(PhantomData<Input>);
23pub struct DirectRawBodyInput<Input>(PhantomData<Input>);
25pub struct DirectHeadInput<Input>(PhantomData<Input>);
27
28pub trait FromCapturesAlg: Sized {
30 fn from_captures(captures: &[String]) -> Option<Self>;
32}
33
34macro_rules! captured {
35 ($($input:ty),+ $(,)?) => {
36 $(
37 impl FromCapturesAlg for $input {
38 fn from_captures(captures: &[String]) -> Option<Self> {
39 captures.first()?.parse().ok()
40 }
41 }
42 )+
43 };
44}
45
46captured!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool, char, String);
47
48macro_rules! captured_product {
49 ($($input:ident => $index:tt),+ $(,)?) => {
50 impl<$($input),+> FromCapturesAlg for ($($input,)+)
51 where
52 $($input: FromStr,)+
53 {
54 fn from_captures(captures: &[String]) -> Option<Self> {
55 Some(($(captures.get($index)?.parse::<$input>().ok()?,)+))
56 }
57 }
58 };
59}
60
61captured_product!(A => 0, B => 1);
62captured_product!(A => 0, B => 1, C => 2);
63captured_product!(A => 0, B => 1, C => 2, D => 3);
64captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4);
65captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5);
66captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6);
67captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7);
68captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8);
69captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9);
70captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9, K => 10);
71captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9, K => 10, L => 11);
72captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9, K => 10, L => 11, M => 12);
73captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9, K => 10, L => 11, M => 12, N => 13);
74captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9, K => 10, L => 11, M => 12, N => 13, O => 14);
75captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9, K => 10, L => 11, M => 12, N => 13, O => 14, P => 15);
76
77pub trait FromBodyAlg: Sized {
79 fn from_body(body: &[u8]) -> Option<Self>;
81}
82
83impl FromBodyAlg for Vec<u8> {
84 fn from_body(body: &[u8]) -> Option<Self> {
85 Some(body.to_vec())
86 }
87}
88
89impl FromBodyAlg for String {
90 fn from_body(body: &[u8]) -> Option<Self> {
91 Self::from_utf8(body.to_vec()).ok()
92 }
93}
94
95pub trait FromHeadAlg: Sized {
97 fn from_head(request: &DirectRequest) -> Option<Self>;
99}
100
101impl FromHeadAlg for DirectRequest {
102 fn from_head(request: &DirectRequest) -> Option<Self> {
103 Some(request.clone())
104 }
105}
106
107pub(crate) trait DirectInputAlg<Output> {
109 fn extract(
110 request: &DirectRequest,
111 captures: &[String],
112 ) -> impl Future<Output = Result<Output, DirectError>> + Send;
113}
114
115impl<Input> DirectInputAlg<Input> for DirectPathInput<Input>
116where
117 Input: FromCapturesAlg,
118{
119 async fn extract(_request: &DirectRequest, captures: &[String]) -> Result<Input, DirectError> {
120 Input::from_captures(captures).ok_or_else(|| DirectError::unreadable("path", "it states something else"))
121 }
122}
123
124impl<Input> DirectInputAlg<Input> for DirectQueryInput<Input>
125where
126 Input: DeserializeOwned,
127{
128 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
129 serde_urlencoded::from_str(request.query())
130 .map_err(|error| DirectError::unreadable("query", &error.to_string()))
131 }
132}
133
134impl<Input> DirectInputAlg<Input> for DirectBodyInput<Input>
135where
136 Input: DeserializeOwned,
137{
138 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
139 serde_json::from_slice(request.body()).map_err(|error| DirectError::unreadable("body", &error.to_string()))
140 }
141}
142
143impl<Input> DirectInputAlg<Input> for DirectFormInput<Input>
144where
145 Input: DeserializeOwned,
146{
147 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
148 serde_urlencoded::from_bytes(request.body())
149 .map_err(|error| DirectError::unreadable("form", &error.to_string()))
150 }
151}
152
153impl<Input> DirectInputAlg<Input> for DirectRawBodyInput<Input>
154where
155 Input: FromBodyAlg,
156{
157 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
158 Input::from_body(request.body()).ok_or_else(|| DirectError::unreadable("body", "it states something else"))
159 }
160}
161
162impl<Input> DirectInputAlg<Input> for DirectHeadInput<Input>
163where
164 Input: FromHeadAlg,
165{
166 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
167 Input::from_head(request).ok_or_else(|| DirectError::unreadable("head", "it states something else"))
168 }
169}
170
171pub struct DirectCookieInput<Input>(PhantomData<Input>);
173
174pub struct DirectHeaderInput<Input>(PhantomData<Input>);
176
177fn headers_of<Input>(stated: impl Iterator<Item = (String, String)>) -> Result<Input, String>
182where
183 Input: DeserializeOwned,
184{
185 let named = stated.map(|(name, value)| (read_header_name(&name), value)).collect::<Vec<_>>();
186 let encoded = serde_urlencoded::to_string(&named).map_err(|error| error.to_string())?;
187
188 serde_urlencoded::from_str(&encoded).map_err(|error| error.to_string())
189}
190
191fn cookies_of<Input>(header: Option<&str>) -> Result<Input, String>
195where
196 Input: DeserializeOwned,
197{
198 let stated = read_cookies(header.unwrap_or_default());
199 let encoded = serde_urlencoded::to_string(&stated).map_err(|error| error.to_string())?;
200
201 serde_urlencoded::from_str(&encoded).map_err(|error| error.to_string())
202}
203
204impl<Input> DirectInputAlg<Input> for DirectCookieInput<Input>
205where
206 Input: DeserializeOwned,
207{
208 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
209 cookies_of(request.header("cookie")).map_err(|error| DirectError::unreadable("cookies", &error))
210 }
211}
212
213impl<Input> DirectInputAlg<Input> for DirectHeaderInput<Input>
214where
215 Input: DeserializeOwned,
216{
217 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
218 let stated = request.headers().map(|(name, value)| (name.to_owned(), value.to_owned()));
219
220 headers_of(stated).map_err(|error| DirectError::unreadable("headers", &error))
221 }
222}
223
224pub struct DirectMultipartInput<Input>(PhantomData<Input>);
226
227impl<Input> DirectInputAlg<Input> for DirectMultipartInput<Input>
228where
229 Input: FromPartsAlg<ReadParts> + Send,
230 Input::Error: Display,
231{
232 async fn extract(request: &DirectRequest, _captures: &[String]) -> Result<Input, DirectError> {
233 let media_type = request
234 .header("content-type")
235 .ok_or_else(|| DirectError::unreadable("parts", "the body states no media type"))?;
236 let parts = ReadParts::new(media_type, request.body().to_vec())
237 .map_err(|error| DirectError::unreadable("parts", &error.to_string()))?;
238
239 Input::from_parts(parts).await.map_err(|error| DirectError::unreadable("parts", &error.to_string()))
240 }
241}
242
243pub(crate) trait DirectInputsAlg<Outputs> {
245 fn extract(
246 request: &DirectRequest,
247 captures: &[String],
248 ) -> impl Future<Output = Result<Outputs, DirectError>> + Send;
249}
250
251impl DirectInputsAlg<()> for () {
252 async fn extract(_request: &DirectRequest, _captures: &[String]) -> Result<(), DirectError> {
253 Ok(())
254 }
255}
256
257macro_rules! direct_inputs {
258 ($($input:ident => $output:ident),+ $(,)?) => {
259 impl<$($input, $output),+> DirectInputsAlg<($($output,)+)> for ($($input,)+)
260 where
261 $($input: DirectInputAlg<$output>, $output: Send,)+
262 {
263 async fn extract(request: &DirectRequest, captures: &[String]) -> Result<($($output,)+), DirectError> {
264 Ok(($($input::extract(request, captures).await?,)+))
265 }
266 }
267 };
268}
269
270direct_inputs!(I1 => O1);
271direct_inputs!(I1 => O1, I2 => O2);
272direct_inputs!(I1 => O1, I2 => O2, I3 => O3);
273direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4);
274direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5);
275direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6);
276direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7);
277direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8);
278direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9);
279direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10);
280direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11);
281direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11, I12 => O12);
282direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11, I12 => O12, I13 => O13);
283direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11, I12 => O12, I13 => O13, I14 => O14);
284direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11, I12 => O12, I13 => O13, I14 => O14, I15 => O15);
285direct_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11, I12 => O12, I13 => O13, I14 => O14, I15 => O15, I16 => O16);