1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use hyper::{body::HttpBody, Body, Response};
use serde::{Deserialize, Deserializer};

use crate::router::response::extender::StaticResponseExtender;
use crate::state::{State, StateData};

/// Defines a binding for storing the query parameters from the `Request` URI in `State`. On
/// failure the `StaticResponseExtender` implementation extends the `Response` to indicate why the
/// extraction process failed.
///
/// This trait is automatically implemented when the struct implements the `Deserialize`,
/// `StateData` and `StaticResponseExtender` traits. These traits can be derived, or implemented
/// manually for greater control.
///
/// The default behaviour given by deriving all three traits will use the automatically derived
/// behaviour from Serde, and result in a `400 Bad Request` HTTP response if the query string is
/// not able to be deserialized.
///
/// # Examples
///
/// ```rust
/// # extern crate gotham;
/// # #[macro_use]
/// # extern crate gotham_derive;
/// # extern crate hyper;
/// # extern crate mime;
/// # extern crate serde;
/// # #[macro_use]
/// # extern crate serde_derive;
/// #
/// # use hyper::{Body, Response, StatusCode};
/// # use gotham::state::{FromState, State};
/// # use gotham::helpers::http::response::create_response;
/// # use gotham::router::Router;
/// # use gotham::router::builder::*;
/// # use gotham::test::TestServer;
/// #
/// #[derive(Deserialize, StateData, StaticResponseExtender)]
/// struct MyQueryParams {
///     x: i32,
///     y: MyEnum,
/// }
///
/// #[derive(Deserialize, Clone, Copy, Debug)]
/// #[serde(rename_all = "kebab-case")]
/// enum MyEnum {
///     A,
///     B,
///     C,
/// }
///
/// fn handler(state: State) -> (State, Response<Body>) {
///     let &MyQueryParams { x, y } = MyQueryParams::borrow_from(&state);
///     let body = format!("x = {}, y = {:?}", x, y);
///
///     let response = create_response(
///         &state,
///         StatusCode::OK,
///         mime::TEXT_PLAIN,
///         body,
///     );
///
///     (state, response)
/// }
///
/// fn router() -> Router {
///     build_simple_router(|route| {
///         route
///             .get("/test")
///             .with_query_string_extractor::<MyQueryParams>()
///             .to(handler);
///     })
/// }
/// #
/// # fn main() {
/// #   let test_server = TestServer::new(router()).unwrap();
/// #   let response = test_server
/// #       .client()
/// #       .get("http://example.com/test?x=15&y=b")
/// #       .perform()
/// #       .unwrap();
/// #   assert_eq!(response.status(), StatusCode::OK);
/// #   let body = response.read_utf8_body().unwrap();
/// #   assert_eq!(body, "x = 15, y = B");
/// # }
pub trait QueryStringExtractor<B>:
    for<'de> Deserialize<'de> + StaticResponseExtender<ResBody = B> + StateData
where
    B: HttpBody,
{
}

impl<T, B> QueryStringExtractor<B> for T
where
    B: HttpBody,
    for<'de> T: Deserialize<'de> + StaticResponseExtender<ResBody = B> + StateData,
{
}

/// A `QueryStringExtractor` that does not extract/store any data.
///
/// This is the default `QueryStringExtractor` which is applied to a route when no other
/// `QueryStringExtractor` is provided. It ignores any query parameters, and always succeeds during
/// deserialization.
#[derive(Debug)]
pub struct NoopQueryStringExtractor;

// This doesn't get derived correctly if we just `#[derive(Deserialize)]` above, because the
// Deserializer expects to _ignore_ a value, not just do nothing. By filling in the impl ourselves,
// we can explicitly do nothing.
impl<'de> Deserialize<'de> for NoopQueryStringExtractor {
    fn deserialize<D>(_de: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(NoopQueryStringExtractor)
    }
}

impl StateData for NoopQueryStringExtractor {}

impl StaticResponseExtender for NoopQueryStringExtractor {
    type ResBody = Body;
    fn extend(_state: &mut State, _res: &mut Response<Body>) {}
}