Skip to content

Commit 5f82540

Browse files
authored
axum-extra/query: Use rejection macros for QueryRejection (#3122)
1 parent 28d8d9b commit 5f82540

File tree

1 file changed

+26
-95
lines changed

1 file changed

+26
-95
lines changed

axum-extra/src/extract/query.rs

+26-95
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
use axum::{
2-
extract::FromRequestParts,
3-
response::{IntoResponse, Response},
4-
Error,
5-
};
6-
use http::{request::Parts, StatusCode};
1+
use axum::extract::FromRequestParts;
2+
use axum_core::__composite_rejection as composite_rejection;
3+
use axum_core::__define_rejection as define_rejection;
4+
use http::request::Parts;
75
use serde::de::DeserializeOwned;
8-
use std::fmt;
96

107
/// Extractor that deserializes query strings into some type.
118
///
@@ -93,63 +90,27 @@ where
9390
let deserializer =
9491
serde_html_form::Deserializer::new(form_urlencoded::parse(query.as_bytes()));
9592
let value = serde_path_to_error::deserialize(deserializer)
96-
.map_err(|err| QueryRejection::FailedToDeserializeQueryString(Error::new(err)))?;
93+
.map_err(FailedToDeserializeQueryString::from_err)?;
9794
Ok(Query(value))
9895
}
9996
}
10097

10198
axum_core::__impl_deref!(Query);
10299

103-
/// Rejection used for [`Query`].
104-
///
105-
/// Contains one variant for each way the [`Query`] extractor can fail.
106-
#[derive(Debug)]
107-
#[non_exhaustive]
108-
#[cfg(feature = "query")]
109-
pub enum QueryRejection {
110-
#[allow(missing_docs)]
111-
FailedToDeserializeQueryString(Error),
112-
}
113-
114-
impl QueryRejection {
115-
/// Get the status code used for this rejection.
116-
pub fn status(&self) -> StatusCode {
117-
match self {
118-
Self::FailedToDeserializeQueryString(_) => StatusCode::BAD_REQUEST,
119-
}
120-
}
121-
}
122-
123-
impl IntoResponse for QueryRejection {
124-
fn into_response(self) -> Response {
125-
let status = self.status();
126-
match self {
127-
Self::FailedToDeserializeQueryString(inner) => {
128-
let body = format!("Failed to deserialize query string: {inner}");
129-
axum_core::__log_rejection!(
130-
rejection_type = Self,
131-
body_text = body,
132-
status = status,
133-
);
134-
(status, body).into_response()
135-
}
136-
}
137-
}
138-
}
139-
140-
impl fmt::Display for QueryRejection {
141-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142-
match self {
143-
Self::FailedToDeserializeQueryString(inner) => inner.fmt(f),
144-
}
145-
}
100+
define_rejection! {
101+
#[status = BAD_REQUEST]
102+
#[body = "Failed to deserialize query string"]
103+
/// Rejection type used if the [`Query`] extractor is unable to
104+
/// deserialize the query string into the target type.
105+
pub struct FailedToDeserializeQueryString(Error);
146106
}
147107

148-
impl std::error::Error for QueryRejection {
149-
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
150-
match self {
151-
Self::FailedToDeserializeQueryString(inner) => Some(inner),
152-
}
108+
composite_rejection! {
109+
/// Rejection used for [`Query`].
110+
///
111+
/// Contains one variant for each way the [`Query`] extractor can fail.
112+
pub enum QueryRejection {
113+
FailedToDeserializeQueryString,
153114
}
154115
}
155116

@@ -207,9 +168,8 @@ where
207168
if let Some(query) = parts.uri.query() {
208169
let deserializer =
209170
serde_html_form::Deserializer::new(form_urlencoded::parse(query.as_bytes()));
210-
let value = serde_path_to_error::deserialize(deserializer).map_err(|err| {
211-
OptionalQueryRejection::FailedToDeserializeQueryString(Error::new(err))
212-
})?;
171+
let value = serde_path_to_error::deserialize(deserializer)
172+
.map_err(FailedToDeserializeQueryString::from_err)?;
213173
Ok(OptionalQuery(Some(value)))
214174
} else {
215175
Ok(OptionalQuery(None))
@@ -233,42 +193,12 @@ impl<T> std::ops::DerefMut for OptionalQuery<T> {
233193
}
234194
}
235195

236-
/// Rejection used for [`OptionalQuery`].
237-
///
238-
/// Contains one variant for each way the [`OptionalQuery`] extractor can fail.
239-
#[derive(Debug)]
240-
#[non_exhaustive]
241-
#[cfg(feature = "query")]
242-
pub enum OptionalQueryRejection {
243-
#[allow(missing_docs)]
244-
FailedToDeserializeQueryString(Error),
245-
}
246-
247-
impl IntoResponse for OptionalQueryRejection {
248-
fn into_response(self) -> Response {
249-
match self {
250-
Self::FailedToDeserializeQueryString(inner) => (
251-
StatusCode::BAD_REQUEST,
252-
format!("Failed to deserialize query string: {inner}"),
253-
)
254-
.into_response(),
255-
}
256-
}
257-
}
258-
259-
impl fmt::Display for OptionalQueryRejection {
260-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261-
match self {
262-
Self::FailedToDeserializeQueryString(inner) => inner.fmt(f),
263-
}
264-
}
265-
}
266-
267-
impl std::error::Error for OptionalQueryRejection {
268-
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
269-
match self {
270-
Self::FailedToDeserializeQueryString(inner) => Some(inner),
271-
}
196+
composite_rejection! {
197+
/// Rejection used for [`OptionalQuery`].
198+
///
199+
/// Contains one variant for each way the [`OptionalQuery`] extractor can fail.
200+
pub enum OptionalQueryRejection {
201+
FailedToDeserializeQueryString,
272202
}
273203
}
274204

@@ -279,6 +209,7 @@ mod tests {
279209
use axum::routing::{get, post};
280210
use axum::Router;
281211
use http::header::CONTENT_TYPE;
212+
use http::StatusCode;
282213
use serde::Deserialize;
283214

284215
#[tokio::test]

0 commit comments

Comments
 (0)