use serde::de::DeserializeOwned;
use topcoat_core::context::Cx;
use crate::request::uri;
pub trait QueryParams {
type Output<'cx>;
#[doc(hidden)]
fn query_params(cx: &Cx, _: QueryParamsSealed) -> Self::Output<'_>;
}
#[inline]
#[must_use]
pub fn query_params<T: QueryParams>(cx: &Cx) -> T::Output<'_> {
T::query_params(cx, QueryParamsSealed::new())
}
pub type QueryParamsError = serde_path_to_error::Error<serde::de::value::Error>;
pub fn parse_query_params<T: DeserializeOwned>(cx: &Cx) -> Result<T, QueryParamsError> {
let query = uri(cx).query().unwrap_or("");
let deserializer =
crate::urlencoded::Deserializer::new(form_urlencoded::parse(query.as_bytes()));
serde_path_to_error::deserialize(deserializer)
}
#[doc(hidden)]
#[derive(Debug)]
pub struct QueryParamsSealed(());
impl QueryParamsSealed {
pub(crate) fn new() -> Self {
Self(())
}
}
#[cfg(test)]
mod tests {
use http::Request;
use topcoat_core::context::CxTestBuilder;
use super::*;
#[derive(Debug, serde::Deserialize)]
struct Paging {
page: Option<f64>,
}
fn cx(uri: &str) -> Cx {
let (parts, ()) = Request::builder()
.uri(uri)
.body(())
.expect("request should build")
.into_parts();
CxTestBuilder::new().request_context(parts).build()
}
#[test]
fn parse_query_params_reads_empty_optional_value_as_none() {
let paging: Paging =
parse_query_params(&cx("/items?page=")).expect("an empty optional value");
assert_eq!(paging.page, None);
}
#[test]
fn parse_query_params_reads_present_optional_value() {
let paging: Paging =
parse_query_params(&cx("/items?page=2")).expect("a valid optional value");
assert_eq!(paging.page, Some(2.0));
}
}