diff --git a/core/http/src/method.rs b/core/http/src/method.rs index ecb06123..7fdf8b29 100644 --- a/core/http/src/method.rs +++ b/core/http/src/method.rs @@ -3,9 +3,30 @@ use std::str::FromStr; use self::Method::*; -// TODO: Support non-standard methods, here and in codegen. +// TODO: Support non-standard methods, here and in codegen? /// Representation of HTTP methods. +/// +/// # (De)serialization +/// +/// `Method` is both `Serialize` and `Deserialize`, represented as an +/// [uncased](crate::uncased) string. For example, [`Method::Get`] serializes to +/// `"GET"` and deserializes from any casing of `"GET"` including `"get"`, +/// `"GeT"`, and `"GET"`. +/// +/// ```rust +/// # #[cfg(feature = "serde")] mod serde { +/// # use serde_ as serde; +/// use serde::{Serialize, Deserialize}; +/// use rocket::http::Method; +/// +/// #[derive(Deserialize, Serialize)] +/// # #[serde(crate = "serde_")] +/// struct Foo { +/// method: Method, +/// } +/// # } +/// ``` #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum Method { /// The `GET` variant. @@ -127,3 +148,38 @@ impl fmt::Display for Method { self.as_str().fmt(f) } } + +#[cfg(feature = "serde")] +mod serde { + use std::fmt; + use super::*; + + use serde_::ser::{Serialize, Serializer}; + use serde_::de::{Deserialize, Deserializer, Error, Visitor, Unexpected}; + + impl<'a> Serialize for Method { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(self.as_str()) + } + } + + struct DeVisitor; + + impl<'de> Visitor<'de> for DeVisitor { + type Value = Method; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "valid HTTP method string") + } + + fn visit_str(self, v: &str) -> Result { + Method::from_str(v).map_err(|_| E::invalid_value(Unexpected::Str(v), &self)) + } + } + + impl<'de> Deserialize<'de> for Method { + fn deserialize>(deserializer: D) -> Result { + deserializer.deserialize_str(DeVisitor) + } + } +} diff --git a/core/lib/tests/http_uri_serde.rs b/core/lib/tests/http_serde.rs similarity index 81% rename from core/lib/tests/http_uri_serde.rs rename to core/lib/tests/http_serde.rs index 35e22405..7215ec83 100644 --- a/core/lib/tests/http_uri_serde.rs +++ b/core/lib/tests/http_serde.rs @@ -4,6 +4,7 @@ use pretty_assertions::assert_eq; use rocket::{Config, uri}; use rocket::http::uri::{Absolute, Asterisk, Authority, Origin, Reference}; +use rocket::http::Method; #[derive(PartialEq, Debug, Serialize, Deserialize)] struct UriContainer<'a> { @@ -23,6 +24,13 @@ struct UriContainerOwned { reference: Reference<'static>, } +#[derive(PartialEq, Debug, Serialize, Deserialize)] +struct MethodContainer { + mget: Method, + mput: Method, + mpost: Method, +} + #[test] fn uri_serde() { figment::Jail::expect_with(|jail| { @@ -111,3 +119,32 @@ fn uri_serde_round_trip() { reference: uri!("https://rocket.rs:8000/index.html").into(), }); } + +#[test] +fn method_serde() { + figment::Jail::expect_with(|jail| { + jail.create_file("Rocket.toml", r#" + [default] + mget = "GET" + mput = "PuT" + mpost = "post" + "#)?; + + let methods: MethodContainer = Config::figment().extract()?; + assert_eq!(methods, MethodContainer { + mget: Method::Get, + mput: Method::Put, + mpost: Method::Post + }); + + let tmp = Figment::from(Serialized::defaults(methods)); + let methods: MethodContainer = tmp.extract()?; + assert_eq!(methods, MethodContainer { + mget: Method::Get, + mput: Method::Put, + mpost: Method::Post + }); + + Ok(()) + }); +}