1#![warn(missing_docs)]
2use std::convert::Infallible;
34
35use axum::{
36 async_trait,
37 body::HttpBody,
38 extract::{rejection::BytesRejection, FromRequest},
39 http::Request,
40 response::IntoResponse,
41 BoxError,
42};
43use serde::de::DeserializeOwned;
44use thiserror::Error;
45
46mod bind;
47
48#[derive(Debug, Error)]
50#[non_exhaustive]
51pub enum BindError {
52 #[error("invalid mime type")]
54 InvalidMimeType,
55 #[error("body read error: {0}")]
57 BodyReadError(BytesRejection),
58 #[cfg(feature = "json")]
60 #[error("json error: {0}")]
61 JsonError(serde_json::Error),
62 #[cfg(feature = "urlencoded")]
64 #[error("urlencoded error: {0}")]
65 UrlEncodedError(serde_urlencoded::de::Error),
66 #[cfg(feature = "xml")]
68 #[error("xml error: {0}")]
69 XmlError(serde_xml_rs::Error),
70}
71
72pub type BindResult<T> = Result<T, BindError>;
74
75pub struct TryBindForm<T: DeserializeOwned>(pub BindResult<T>);
77
78#[async_trait]
79impl<S, B, T> FromRequest<S, B> for TryBindForm<T>
80where
81 T: DeserializeOwned,
82 B: HttpBody + Send + 'static,
83 B::Data: Send,
84 B::Error: Into<BoxError>,
85 S: Send + Sync,
86{
87 type Rejection = Infallible;
88
89 async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
90 let deserialized = bind::bind_request(req, state).await;
91
92 Ok(TryBindForm(deserialized))
93 }
94}
95
96pub struct BindForm<T: DeserializeOwned>(pub T);
98
99pub struct BindFormRejection(BindError);
101
102impl IntoResponse for BindFormRejection {
103 fn into_response(self) -> axum::response::Response {
104 let body = format!("{}", self.0);
105 (
106 axum::http::StatusCode::BAD_REQUEST,
107 [(axum::http::header::CONTENT_TYPE, "text/plain")],
108 body,
109 )
110 .into_response()
111 }
112}
113
114#[async_trait]
115impl<S, B, T> FromRequest<S, B> for BindForm<T>
116where
117 T: DeserializeOwned,
118 B: HttpBody + Send + 'static,
119 B::Data: Send,
120 B::Error: Into<BoxError>,
121 S: Send + Sync,
122{
123 type Rejection = BindFormRejection;
124
125 async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
126 let deserialized = bind::bind_request(req, state).await;
127
128 match deserialized {
129 Ok(deserialized) => Ok(BindForm(deserialized)),
130 Err(err) => Err(BindFormRejection(err)),
131 }
132 }
133}