use http::StatusCode;
use http::request::Parts;
use serde::de::DeserializeOwned;
use crate::extractors::FromRequest;
use crate::extractors::FromRequestParts;
use crate::responder::Responder;
use crate::types::Request;
#[doc(alias = "query")]
pub struct Query<T>(pub T);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QueryError {
MissingQueryString,
ParseError(String),
DeserializationError(String),
}
impl std::fmt::Display for QueryError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingQueryString => write!(f, "no query string found in request URI"),
Self::ParseError(err) => write!(f, "failed to parse query parameters: {err}"),
Self::DeserializationError(err) => {
write!(f, "failed to deserialize query parameters: {err}")
}
}
}
}
impl std::error::Error for QueryError {}
impl Responder for QueryError {
fn into_response(self) -> crate::types::Response {
match self {
QueryError::MissingQueryString => (
StatusCode::BAD_REQUEST,
"No query string found in request URI",
)
.into_response(),
QueryError::ParseError(err) => (
StatusCode::BAD_REQUEST,
format!("Failed to parse query parameters: {err}"),
)
.into_response(),
QueryError::DeserializationError(err) => (
StatusCode::BAD_REQUEST,
format!("Failed to deserialize query parameters: {err}"),
)
.into_response(),
}
}
}
impl<T> Query<T>
where
T: DeserializeOwned,
{
fn extract_from_query_string(query_string: Option<&str>) -> Result<Query<T>, QueryError> {
let query = query_string.unwrap_or_default();
let query_data = serde_urlencoded::from_str::<T>(query)
.map_err(|e| QueryError::DeserializationError(e.to_string()))?;
Ok(Query(query_data))
}
}
impl<'a, T> FromRequest<'a> for Query<T>
where
T: DeserializeOwned + Send + 'a,
{
type Error = QueryError;
fn from_request(
req: &'a mut Request,
) -> impl core::future::Future<Output = core::result::Result<Self, Self::Error>> + Send + 'a {
futures_util::future::ready(Self::extract_from_query_string(req.uri().query()))
}
}
impl<'a, T> FromRequestParts<'a> for Query<T>
where
T: DeserializeOwned + Send + 'a,
{
type Error = QueryError;
fn from_request_parts(
parts: &'a mut Parts,
) -> impl core::future::Future<Output = core::result::Result<Self, Self::Error>> + Send + 'a {
futures_util::future::ready(Self::extract_from_query_string(parts.uri.query()))
}
}