1use crate::WarpRequest;
4use alux_http::{FromPartsAlg, read_cookies, read_header_name};
5use alux_http::{HttpErrorAlg, HttpStatus};
6use alux_http_parts::ReadParts;
7use core::error::Error;
8use core::fmt::{self, Display};
9use core::future::Future;
10use core::marker::PhantomData;
11use core::str::FromStr;
12use serde::de::DeserializeOwned;
13use warp::http::HeaderMap;
14use warp::http::header;
15
16pub struct WarpPathInput<Input>(PhantomData<Input>);
18pub struct WarpQueryInput<Input>(PhantomData<Input>);
20pub struct WarpBodyInput<Input>(PhantomData<Input>);
22pub struct WarpFormInput<Input>(PhantomData<Input>);
24pub struct WarpRawBodyInput<Input>(PhantomData<Input>);
26pub struct WarpHeadInput<Input>(PhantomData<Input>);
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct WarpError {
32 message: String,
33}
34
35impl WarpError {
36 fn unreadable(role: &str, reason: &str) -> Self {
37 Self { message: format!("the {role} could not be read: {reason}") }
38 }
39}
40
41impl HttpErrorAlg for WarpError {
42 const HTTP_STATUSES: &'static [HttpStatus] = &[HttpStatus::BAD_REQUEST];
43
44 fn http_status(&self) -> HttpStatus {
45 HttpStatus::BAD_REQUEST
46 }
47
48 fn http_message(&self) -> String {
49 self.message.clone()
50 }
51}
52
53impl Display for WarpError {
54 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55 formatter.write_str(&self.message)
56 }
57}
58
59impl Error for WarpError {}
60
61pub trait FromCapturedAlg: Sized {
63 fn from_captured(captures: &[String]) -> Option<Self>;
65}
66
67macro_rules! captured {
68 ($($input:ty),+ $(,)?) => {
69 $(
70 impl FromCapturedAlg for $input {
71 fn from_captured(captures: &[String]) -> Option<Self> {
72 captures.first()?.parse().ok()
73 }
74 }
75 )+
76 };
77}
78
79captured!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool, char, String);
80
81macro_rules! captured_product {
82 ($($input:ident => $index:tt),+ $(,)?) => {
83 impl<$($input),+> FromCapturedAlg for ($($input,)+)
84 where
85 $($input: FromStr,)+
86 {
87 fn from_captured(captures: &[String]) -> Option<Self> {
88 Some(($(captures.get($index)?.parse::<$input>().ok()?,)+))
89 }
90 }
91 };
92}
93
94captured_product!(A => 0, B => 1);
95captured_product!(A => 0, B => 1, C => 2);
96captured_product!(A => 0, B => 1, C => 2, D => 3);
97captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4);
98captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5);
99captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6);
100captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7);
101captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8);
102captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9);
103captured_product!(A => 0, B => 1, C => 2, D => 3, E => 4, F => 5, G => 6, H => 7, I => 8, J => 9, K => 10);
104captured_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);
105captured_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);
106captured_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);
107captured_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);
108captured_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);
109
110pub trait FromRawAlg: Sized {
112 fn from_raw(body: &[u8]) -> Option<Self>;
114}
115
116impl FromRawAlg for Vec<u8> {
117 fn from_raw(body: &[u8]) -> Option<Self> {
118 Some(body.to_vec())
119 }
120}
121
122impl FromRawAlg for String {
123 fn from_raw(body: &[u8]) -> Option<Self> {
124 Self::from_utf8(body.to_vec()).ok()
125 }
126}
127
128pub trait FromHeadersAlg: Sized {
130 fn from_headers(headers: &HeaderMap) -> Option<Self>;
132}
133
134impl FromHeadersAlg for HeaderMap {
135 fn from_headers(headers: &HeaderMap) -> Option<Self> {
136 Some(headers.clone())
137 }
138}
139
140pub(crate) trait WarpInputAlg<Output> {
142 fn extract(request: &WarpRequest) -> impl Future<Output = Result<Output, WarpError>> + Send;
143}
144
145impl<Input> WarpInputAlg<Input> for WarpPathInput<Input>
146where
147 Input: FromCapturedAlg,
148{
149 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
150 Input::from_captured(request.captures())
151 .ok_or_else(|| WarpError::unreadable("path", "it states something else"))
152 }
153}
154
155impl<Input> WarpInputAlg<Input> for WarpQueryInput<Input>
156where
157 Input: DeserializeOwned,
158{
159 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
160 serde_urlencoded::from_str(request.query()).map_err(|error| WarpError::unreadable("query", &error.to_string()))
161 }
162}
163
164impl<Input> WarpInputAlg<Input> for WarpBodyInput<Input>
165where
166 Input: DeserializeOwned,
167{
168 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
169 serde_json::from_slice(request.body()).map_err(|error| WarpError::unreadable("body", &error.to_string()))
170 }
171}
172
173impl<Input> WarpInputAlg<Input> for WarpFormInput<Input>
174where
175 Input: DeserializeOwned,
176{
177 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
178 serde_urlencoded::from_bytes(request.body()).map_err(|error| WarpError::unreadable("form", &error.to_string()))
179 }
180}
181
182impl<Input> WarpInputAlg<Input> for WarpRawBodyInput<Input>
183where
184 Input: FromRawAlg,
185{
186 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
187 Input::from_raw(request.body()).ok_or_else(|| WarpError::unreadable("body", "it states something else"))
188 }
189}
190
191impl<Input> WarpInputAlg<Input> for WarpHeadInput<Input>
192where
193 Input: FromHeadersAlg,
194{
195 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
196 Input::from_headers(request.headers())
197 .ok_or_else(|| WarpError::unreadable("headers", "they state something else"))
198 }
199}
200
201pub struct WarpCookieInput<Input>(PhantomData<Input>);
203
204pub struct WarpHeaderInput<Input>(PhantomData<Input>);
206
207fn headers_of<Input>(stated: impl Iterator<Item = (String, String)>) -> Result<Input, String>
212where
213 Input: DeserializeOwned,
214{
215 let named = stated.map(|(name, value)| (read_header_name(&name), value)).collect::<Vec<_>>();
216 let encoded = serde_urlencoded::to_string(&named).map_err(|error| error.to_string())?;
217
218 serde_urlencoded::from_str(&encoded).map_err(|error| error.to_string())
219}
220
221fn cookies_of<Input>(header: Option<&str>) -> Result<Input, String>
225where
226 Input: DeserializeOwned,
227{
228 let stated = read_cookies(header.unwrap_or_default());
229 let encoded = serde_urlencoded::to_string(&stated).map_err(|error| error.to_string())?;
230
231 serde_urlencoded::from_str(&encoded).map_err(|error| error.to_string())
232}
233
234impl<Input> WarpInputAlg<Input> for WarpCookieInput<Input>
235where
236 Input: DeserializeOwned,
237{
238 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
239 let header = request.headers().get(header::COOKIE).and_then(|value| value.to_str().ok());
240
241 cookies_of(header).map_err(|error| WarpError::unreadable("cookies", &error))
242 }
243}
244
245impl<Input> WarpInputAlg<Input> for WarpHeaderInput<Input>
246where
247 Input: DeserializeOwned,
248{
249 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
250 let stated = request
251 .headers()
252 .iter()
253 .filter_map(|(name, value)| Some((name.to_string(), value.to_str().ok()?.to_owned())));
254
255 headers_of(stated).map_err(|error| WarpError::unreadable("headers", &error))
256 }
257}
258
259pub struct WarpMultipartInput<Input>(PhantomData<Input>);
261
262impl<Input> WarpInputAlg<Input> for WarpMultipartInput<Input>
263where
264 Input: FromPartsAlg<ReadParts> + Send,
265 Input::Error: Display,
266{
267 async fn extract(request: &WarpRequest) -> Result<Input, WarpError> {
268 let media_type = request
269 .headers()
270 .get(header::CONTENT_TYPE)
271 .and_then(|value| value.to_str().ok())
272 .ok_or_else(|| WarpError::unreadable("parts", "the body states no media type"))?;
273 let parts = ReadParts::new(media_type, request.body().to_vec())
274 .map_err(|error| WarpError::unreadable("parts", &error.to_string()))?;
275
276 Input::from_parts(parts).await.map_err(|error| WarpError::unreadable("parts", &error.to_string()))
277 }
278}
279
280pub(crate) trait WarpInputsAlg<Outputs> {
282 fn extract(request: &WarpRequest) -> impl Future<Output = Result<Outputs, WarpError>> + Send;
283}
284
285impl WarpInputsAlg<()> for () {
286 async fn extract(_request: &WarpRequest) -> Result<(), WarpError> {
287 Ok(())
288 }
289}
290
291macro_rules! warp_inputs {
292 ($($input:ident => $output:ident),+ $(,)?) => {
293 impl<$($input, $output),+> WarpInputsAlg<($($output,)+)> for ($($input,)+)
294 where
295 $($input: WarpInputAlg<$output>, $output: Send,)+
296 {
297 async fn extract(request: &WarpRequest) -> Result<($($output,)+), WarpError> {
298 Ok(($($input::extract(request).await?,)+))
299 }
300 }
301 };
302}
303
304warp_inputs!(I1 => O1);
305warp_inputs!(I1 => O1, I2 => O2);
306warp_inputs!(I1 => O1, I2 => O2, I3 => O3);
307warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4);
308warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5);
309warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6);
310warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7);
311warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8);
312warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9);
313warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10);
314warp_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11);
315warp_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);
316warp_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);
317warp_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);
318warp_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);
319warp_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);