use super::{FromPartsStateRefPair, OptionalFromPartsStateRefPair};
use crate::request::Parts;
use crate::utils::macros::define_http_rejection;
use rama_net::uri::Uri;
use serde::de::DeserializeOwned;
#[derive(Debug, Clone)]
pub struct Query<T>(pub T);
define_http_rejection! {
#[status = BAD_REQUEST]
#[body = "Failed to deserialize query string"]
pub struct FailedToDeserializeQueryString(Error);
}
impl<T> Query<T>
where
T: DeserializeOwned + Send + Sync + 'static,
{
#[inline(always)]
pub fn try_from_uri(value: &Uri) -> Result<Self, FailedToDeserializeQueryString> {
value
.query_params::<T>()
.map(Self)
.map_err(FailedToDeserializeQueryString::from_err)
}
pub fn parse_query_str(query: &str) -> Result<Self, FailedToDeserializeQueryString> {
let params =
serde_html_form::from_str(query).map_err(FailedToDeserializeQueryString::from_err)?;
Ok(Self(params))
}
}
impl<T, State> FromPartsStateRefPair<State> for Query<T>
where
T: DeserializeOwned + Send + Sync + 'static,
State: Send + Sync,
{
type Rejection = FailedToDeserializeQueryString;
async fn from_parts_state_ref_pair(
parts: &Parts,
_state: &State,
) -> Result<Self, Self::Rejection> {
parts
.uri
.query_params::<T>()
.map(Self)
.map_err(FailedToDeserializeQueryString::from_err)
}
}
impl<T, State> OptionalFromPartsStateRefPair<State> for Query<T>
where
T: DeserializeOwned + Send + Sync + 'static,
State: Send + Sync,
{
type Rejection = FailedToDeserializeQueryString;
async fn from_parts_state_ref_pair(
parts: &Parts,
_state: &State,
) -> Result<Option<Self>, Self::Rejection> {
match parts.uri.query() {
Some(_) => parts
.uri
.query_params::<T>()
.map(|params| Some(Self(params)))
.map_err(FailedToDeserializeQueryString::from_err),
None => Ok(None),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[test]
fn test_try_from_uri() {
#[derive(Deserialize)]
struct TestQueryParams {
foo: Vec<String>,
bar: u32,
}
let uri: Uri = "http://example.com/path?foo=hello&bar=42&foo=goodbye"
.parse()
.unwrap();
let Query(TestQueryParams { foo, bar }) = Query::try_from_uri(&uri).unwrap();
assert_eq!(foo, [String::from("hello"), String::from("goodbye")]);
assert_eq!(bar, 42);
}
#[test]
fn test_try_from_uri_with_invalid_query() {
#[derive(Deserialize)]
struct TestQueryParams {
_foo: String,
_bar: u32,
}
let uri: Uri = "http://example.com/path?foo=hello&bar=invalid"
.parse()
.unwrap();
let result: Result<Query<TestQueryParams>, _> = Query::try_from_uri(&uri);
assert!(result.is_err());
}
}