Repository: tokio-rs/axum
File: axum-extra/src/extract/query.rs (Rust)

Code before:
        }
    }
}

#[allow(deprecated)]
impl<T> std::ops::Deref for OptionalQuery<T> {
    type Target = Option<T>;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[allow(deprecated)]
impl<T> std::ops::DerefMut for OptionalQuery<T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

/// Rejection used for [`OptionalQuery`].
///
/// Contains one variant for each way the [`OptionalQuery`] extractor can fail.
#[derive(Debug)]
#[non_exhaustive]
#[cfg(feature = "query")]
pub enum OptionalQueryRejection {
    #[allow(missing_docs)]
    FailedToDeserializeQueryString(Error),
}

impl IntoResponse for OptionalQueryRejection {
    fn into_response(self) -> Response {
        match self {
            Self::FailedToDeserializeQueryString(inner) => (
                StatusCode::BAD_REQUEST,
                format!("Failed to deserialize query string: {inner}"),
            )
                .into_response(),
        }
    }
}

impl fmt::Display for OptionalQueryRejection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::FailedToDeserializeQueryString(inner) => inner.fmt(f),
        }
    }

Review comment (refactor):
`OptionalPath` became this:
```
        parts
            .extract::<Option<Path<T>>>()
            .await
            .map(|opt| Self(opt.map(|Path(x)| x)))
```

I think that a similar thing can be done here.

Code after:
/// // empty query string into `None`
/// async fn list_things(OptionalQuery(pagination): OptionalQuery<Pagination>) {
///     match pagination {
///         Some(Pagination{ page, per_page }) => { /* return specified page */ },
///         None => { /* return fist page */ }
///     }
///     // ...
/// }
///
/// let app = Router::new().route("/list_things", get(list_things));
/// # let _: Router = app;
/// ```
///
/// If the query string cannot be parsed it will reject the request with a `400
/// Bad Request` response.
///
/// For handling values being empty vs missing see the [query-params-with-empty-strings][example]
/// example.
///
/// [example]: https://github.com/tokio-rs/axum/blob/main/examples/query-params-with-empty-strings/src/main.rs
#[cfg_attr(docsrs, doc(cfg(feature = "query")))]
#[deprecated = "Use Option<Query<_>> instead"]
#[derive(Debug, Clone, Copy, Default)]
pub struct OptionalQuery<T>(pub Option<T>);

#[allow(deprecated)]
impl<T, S> FromRequestParts<S> for OptionalQuery<T>
where
    T: DeserializeOwned,
    S: Send + Sync,
{
    type Rejection = OptionalQueryRejection;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        if let Some(query) = parts.uri.query() {
            let value = serde_html_form::from_str(query).map_err(|err| {
                OptionalQueryRejection::FailedToDeserializeQueryString(Error::new(err))
            })?;
            Ok(OptionalQuery(Some(value)))
        } else {
            Ok(OptionalQuery(None))
        }
    }
}

#[allow(deprecated)]
impl<T> std::ops::Deref for OptionalQuery<T> {
    type Target = Option<T>;

    #[inline]
    fn deref(&self) -> &Self::Target {