rama_http/service/web/endpoint/extract/
query.rs

1//! Module in function of the [`Query`] extractor.
2
3use super::{FromRequestContextRefPair, OptionalFromRequestContextRefPair};
4use crate::dep::http::request::Parts;
5use crate::utils::macros::define_http_rejection;
6use rama_core::Context;
7use serde::de::DeserializeOwned;
8
9/// Extractor that deserializes query strings into some type.
10///
11/// `T` is expected to implement [`serde::Deserialize`].
12pub struct Query<T>(pub T);
13
14define_http_rejection! {
15    #[status = BAD_REQUEST]
16    #[body = "Failed to deserialize query string"]
17    /// Rejection type used if the [`Query`] extractor is unable to
18    /// deserialize the query string into the target type.
19    pub struct FailedToDeserializeQueryString(Error);
20}
21
22impl<T: std::fmt::Debug> std::fmt::Debug for Query<T> {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        f.debug_tuple("Query").field(&self.0).finish()
25    }
26}
27
28impl<T: Clone> Clone for Query<T> {
29    fn clone(&self) -> Self {
30        Self(self.0.clone())
31    }
32}
33
34impl<T> Query<T>
35where
36    T: DeserializeOwned + Send + Sync + 'static,
37{
38    /// Create a `Query<T>` directly from the query str,
39    /// can be useful to combine this method as part of another extractor
40    /// or otherwise impossible combination.
41    pub fn parse_query_str(query: &str) -> Result<Self, FailedToDeserializeQueryString> {
42        let params =
43            serde_html_form::from_str(query).map_err(FailedToDeserializeQueryString::from_err)?;
44        Ok(Query(params))
45    }
46}
47
48impl<T, S> FromRequestContextRefPair<S> for Query<T>
49where
50    T: DeserializeOwned + Send + Sync + 'static,
51    S: Clone + Send + Sync + 'static,
52{
53    type Rejection = FailedToDeserializeQueryString;
54
55    async fn from_request_context_ref_pair(
56        _ctx: &Context<S>,
57        parts: &Parts,
58    ) -> Result<Self, Self::Rejection> {
59        let query = parts.uri.query().unwrap_or_default();
60        Query::parse_query_str(query)
61    }
62}
63
64impl<T, S> OptionalFromRequestContextRefPair<S> for Query<T>
65where
66    T: DeserializeOwned + Send + Sync + 'static,
67    S: Clone + Send + Sync + 'static,
68{
69    type Rejection = FailedToDeserializeQueryString;
70
71    async fn from_request_context_ref_pair(
72        _ctx: &Context<S>,
73        parts: &Parts,
74    ) -> Result<Option<Self>, Self::Rejection> {
75        match parts.uri.query() {
76            Some(query) => {
77                let params = serde_html_form::from_str(query)
78                    .map_err(FailedToDeserializeQueryString::from_err)?;
79                Ok(Some(Query(params)))
80            }
81            None => Ok(None),
82        }
83    }
84}