1use crate::AxumParts;
2use alux_http::{FromPartsAlg, read_cookies, read_header_name};
3use axum::body::Body;
4use axum::extract::{FromRequest, FromRequestParts, Multipart, Path, Query, Request};
5use axum::http::request::Parts;
6use axum::http::{StatusCode, header};
7use axum::response::{IntoResponse, Response};
8use axum::{Form, Json};
9use core::fmt::Display;
10use core::future::Future;
11use core::marker::PhantomData;
12use serde::de::DeserializeOwned;
13
14pub struct AxumPathInput<Input>(PhantomData<Input>);
16pub struct AxumQueryInput<Input>(PhantomData<Input>);
18pub struct AxumBodyInput<Input>(PhantomData<Input>);
20pub struct AxumFormInput<Input>(PhantomData<Input>);
22pub struct AxumRawBodyInput<Input>(PhantomData<Input>);
24pub struct AxumPartsInput<Input>(PhantomData<Input>);
26
27pub(crate) trait AxumInputAlg<Output> {
32 fn extract(parts: &mut Parts, body: &mut Option<Body>) -> impl Future<Output = Result<Output, Response>> + Send;
33}
34
35impl<Input> AxumInputAlg<Input> for AxumPartsInput<Input>
36where
37 Input: FromRequestParts<()> + Send,
38{
39 async fn extract(parts: &mut Parts, _body: &mut Option<Body>) -> Result<Input, Response> {
40 Input::from_request_parts(parts, &()).await.map_err(IntoResponse::into_response)
41 }
42}
43
44macro_rules! axum_parts_input {
45 ($marker:ident, $extractor:ident) => {
46 impl<Input> AxumInputAlg<Input> for $marker<Input>
47 where
48 Input: DeserializeOwned + Send,
49 {
50 async fn extract(parts: &mut Parts, _body: &mut Option<Body>) -> Result<Input, Response> {
51 Ok($extractor::<Input>::from_request_parts(parts, &()).await.map_err(IntoResponse::into_response)?.0)
52 }
53 }
54 };
55}
56
57axum_parts_input!(AxumPathInput, Path);
58axum_parts_input!(AxumQueryInput, Query);
59
60fn taken(parts: &Parts, body: &mut Option<Body>) -> Request {
62 Request::from_parts(parts.clone(), body.take().unwrap_or_default())
63}
64
65macro_rules! axum_body_input {
66 ($marker:ident, $extractor:ident) => {
67 impl<Input> AxumInputAlg<Input> for $marker<Input>
68 where
69 Input: DeserializeOwned + Send,
70 {
71 async fn extract(parts: &mut Parts, body: &mut Option<Body>) -> Result<Input, Response> {
72 let request = taken(parts, body);
73
74 Ok($extractor::<Input>::from_request(request, &()).await.map_err(IntoResponse::into_response)?.0)
75 }
76 }
77 };
78}
79
80axum_body_input!(AxumBodyInput, Json);
81axum_body_input!(AxumFormInput, Form);
82
83impl<Input> AxumInputAlg<Input> for AxumRawBodyInput<Input>
84where
85 Input: FromRequest<()> + Send,
86{
87 async fn extract(parts: &mut Parts, body: &mut Option<Body>) -> Result<Input, Response> {
88 let request = taken(parts, body);
89
90 Input::from_request(request, &()).await.map_err(IntoResponse::into_response)
91 }
92}
93
94pub struct AxumCookieInput<Input>(PhantomData<Input>);
96
97pub struct AxumHeaderInput<Input>(PhantomData<Input>);
99
100fn headers_of<Input>(stated: impl Iterator<Item = (String, String)>) -> Result<Input, String>
105where
106 Input: DeserializeOwned,
107{
108 let named = stated.map(|(name, value)| (read_header_name(&name), value)).collect::<Vec<_>>();
109 let encoded = serde_urlencoded::to_string(&named).map_err(|error| error.to_string())?;
110
111 serde_urlencoded::from_str(&encoded).map_err(|error| error.to_string())
112}
113
114fn cookies_of<Input>(header: Option<&str>) -> Result<Input, String>
118where
119 Input: DeserializeOwned,
120{
121 let stated = read_cookies(header.unwrap_or_default());
122 let encoded = serde_urlencoded::to_string(&stated).map_err(|error| error.to_string())?;
123
124 serde_urlencoded::from_str(&encoded).map_err(|error| error.to_string())
125}
126
127impl<Input> AxumInputAlg<Input> for AxumCookieInput<Input>
128where
129 Input: DeserializeOwned + Send,
130{
131 async fn extract(parts: &mut Parts, _body: &mut Option<Body>) -> Result<Input, Response> {
132 let header = parts.headers.get(header::COOKIE).and_then(|value| value.to_str().ok());
133
134 cookies_of(header).map_err(|error| (StatusCode::BAD_REQUEST, error).into_response())
135 }
136}
137
138impl<Input> AxumInputAlg<Input> for AxumHeaderInput<Input>
139where
140 Input: DeserializeOwned + Send,
141{
142 async fn extract(parts: &mut Parts, _body: &mut Option<Body>) -> Result<Input, Response> {
143 let stated =
144 parts.headers.iter().filter_map(|(name, value)| Some((name.to_string(), value.to_str().ok()?.to_owned())));
145
146 headers_of(stated).map_err(|error| (StatusCode::BAD_REQUEST, error).into_response())
147 }
148}
149
150pub struct AxumMultipartInput<Input>(PhantomData<Input>);
152
153impl<Input> AxumInputAlg<Input> for AxumMultipartInput<Input>
154where
155 Input: FromPartsAlg<AxumParts> + Send,
156 Input::Error: Display,
157{
158 async fn extract(parts: &mut Parts, body: &mut Option<Body>) -> Result<Input, Response> {
159 let request = taken(parts, body);
160 let read = Multipart::from_request(request, &()).await.map_err(IntoResponse::into_response)?;
161
162 Input::from_parts(AxumParts(read))
163 .await
164 .map_err(|error| (StatusCode::BAD_REQUEST, error.to_string()).into_response())
165 }
166}
167
168pub(crate) trait AxumInputsAlg<Outputs> {
170 fn extract(parts: &mut Parts, body: &mut Option<Body>) -> impl Future<Output = Result<Outputs, Response>> + Send;
171}
172
173impl AxumInputsAlg<()> for () {
174 async fn extract(_parts: &mut Parts, _body: &mut Option<Body>) -> Result<(), Response> {
175 Ok(())
176 }
177}
178
179macro_rules! axum_inputs {
180 ($($input:ident => $output:ident),+ $(,)?) => {
181 impl<$($input, $output),+> AxumInputsAlg<($($output,)+)> for ($($input,)+)
182 where
183 $($input: AxumInputAlg<$output>, $output: Send,)+
184 {
185 async fn extract(parts: &mut Parts, body: &mut Option<Body>) -> Result<($($output,)+), Response> {
186 Ok(($($input::extract(parts, body).await?,)+))
187 }
188 }
189 };
190}
191
192axum_inputs!(I1 => O1);
193axum_inputs!(I1 => O1, I2 => O2);
194axum_inputs!(I1 => O1, I2 => O2, I3 => O3);
195axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4);
196axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5);
197axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6);
198axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7);
199axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8);
200axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9);
201axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10);
202axum_inputs!(I1 => O1, I2 => O2, I3 => O3, I4 => O4, I5 => O5, I6 => O6, I7 => O7, I8 => O8, I9 => O9, I10 => O10, I11 => O11);
203axum_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);
204axum_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);
205axum_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);
206axum_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);
207axum_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);