use std::{fmt, ops};
use serde::de;
use crate::http::Payload;
use crate::web::error::{ErrorRenderer, QueryPayloadError};
use crate::web::{FromRequest, HttpRequest};
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Query<T>(pub T);
impl<T> Query<T> {
pub fn into_inner(self) -> T {
self.0
}
pub fn from_query(query_str: &str) -> Result<Self, QueryPayloadError>
where
T: de::DeserializeOwned,
{
serde_urlencoded::from_str::<T>(query_str).map_or_else(
|e| Err(QueryPayloadError::Deserialize(e)),
|val| Ok(Query(val)),
)
}
}
impl<T> ops::Deref for Query<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> ops::DerefMut for Query<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: fmt::Debug> fmt::Debug for Query<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for Query<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T, Err> FromRequest<Err> for Query<T>
where
T: de::DeserializeOwned,
Err: ErrorRenderer,
{
type Error = QueryPayloadError;
#[inline]
async fn from_request(req: &HttpRequest, _: &mut Payload) -> Result<Self, Self::Error> {
serde_urlencoded::from_str::<T>(req.query_string()).map_or_else(
move |e| {
let e = QueryPayloadError::Deserialize(e);
log::debug!(
"Failed during Query extractor deserialization. \
Request path: {:?}",
req.path()
);
Err(e)
},
|val| Ok(Query(val)),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::web::test::{TestRequest, from_request};
#[derive(serde::Deserialize, Debug, thiserror::Error)]
#[error("Id({id})")]
struct Id {
id: String,
}
#[crate::rt_test]
async fn test_service_request_extract() {
let req = TestRequest::with_uri("/name/user1/").to_srv_request();
assert!(Query::<Id>::from_query(req.query_string()).is_err());
let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
let mut s = Query::<Id>::from_query(req.query_string()).unwrap();
assert_eq!(s.id, "test");
assert_eq!(format!("{s}, {s:?}"), "Id(test), Id { id: \"test\" }");
s.id = "test1".to_string();
let s = s.into_inner();
assert_eq!(s.id, "test1");
}
#[crate::rt_test]
async fn test_request_extract() {
let req = TestRequest::with_uri("/name/user1/").to_srv_request();
let (req, mut pl) = req.into_parts();
let res = from_request::<Query<Id>>(&req, &mut pl).await;
assert!(res.is_err());
let req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
let (req, mut pl) = req.into_parts();
let mut s = from_request::<Query<Id>>(&req, &mut pl).await.unwrap();
assert_eq!(s.id, "test");
assert_eq!(format!("{s}, {s:?}"), "Id(test), Id { id: \"test\" }");
s.id = "test1".to_string();
let s = s.into_inner();
assert_eq!(s.id, "test1");
}
}