From 715913a6b3fd66f5675b3b576db3439f1e4267a8 Mon Sep 17 00:00:00 2001 From: Yong Wen Chua Date: Tue, 5 Sep 2017 14:11:28 +0800 Subject: [PATCH] Gate serde behind a feature flag (#25) * Gate serde behind a feature flag Fixes #16 * Fix travis build configuration --- .gitignore | 1 + .travis.yml | 19 ++++---- Cargo.toml | 23 +++++++-- examples/json.rs | 2 + src/headers.rs | 15 ++++-- src/lib.rs | 122 ++++++++++++++++++++++++++++++----------------- 6 files changed, 121 insertions(+), 61 deletions(-) diff --git a/.gitignore b/.gitignore index 4308d82..8b9a2c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target/ **/*.rs.bk Cargo.lock +**/*.rs.fmt diff --git a/.travis.yml b/.travis.yml index 2b2f93b..10018e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,13 @@ sudo: false language: rust rust: - nightly-2017-09-05 +env: + global: + - TRAVIS_CARGO_NIGHTLY_FEATURE="" + - secure: dig1qv7Qc4IrAKoszmetgU1qyMnd5bPZC+jpgKaiimDX5mGiJJB3qHWql6+jeR5JDEcFGKE8/oFMkCtg78E6Gv6FcJ6qwB3Ks4MRfZlLmTPcwPj0pf6789M9FRnTsPMp3HHmeoVsfM4ujZYgk3Az+bDU4En6/8U4RCdl0CAmRGwMt9QtKzSygiHivCs/BpqkQ+Coq0HezaYDzBrNF3Q0gsxgy+5DALwSaQqAsKatb73w7i4EuyzrgfPW8OvkUj3i2Zc2zuY8FCElZY1H7igQK4P/EFyKyiRbyTzVsvwbqBwPPQuVzT8wwmOqcCmQACnuLD9EiF0iP2sAtWTJ0H2HosptVqxQbqmsvGHLzAza1jb1buLBgeIvPwDRP1sQmxVRgD1JkzRiZkt8EwN6MYzqtmfPA32Aukoji47n1zmyombJlAKkC8gKSXc6XTByOXcFNcYQ0BbyT9Tngn9K+YZUmSG/Wf33GKOCQc80I07FnOOsfYUpU43Vjv7xuFdyJn9vS9nxZzhZURQzrfJg4mgcqOTRGHIFGHdvrnVzDJX60gWQMoRadLjLoknGH86BUScmEJ0dWwhHS8dBeYboTNJuiW2EQtcnvDawQ8F79bhyNLVhM2ICoaf9Rok/lMjLUEN+6HzOGzw/rQYtJaJ6trF5CFwZIf3q2kHbW6wybwU/KTg= + matrix: + - CARGO_FLAGS="--all-features" + - CARGO_FLAGS="--no-default-features" addons: apt: packages: @@ -14,12 +21,8 @@ before_script: export PATH=$HOME/.local/bin:$PATH script: - | - travis-cargo build -- --all-features --all && - travis-cargo test -- --all-features --all && - travis-cargo --only nightly-2017-09-05 doc -- --no-deps --all-features --all + travis-cargo build -- "${CARGO_FLAGS}" && + travis-cargo test -- "${CARGO_FLAGS}" && + travis-cargo --only nightly-2017-09-05 doc -- --no-deps "${CARGO_FLAGS}" after_success: -- travis-cargo --only nightly-2017-09-05 doc-upload -env: - global: - - TRAVIS_CARGO_NIGHTLY_FEATURE="" - - secure: dig1qv7Qc4IrAKoszmetgU1qyMnd5bPZC+jpgKaiimDX5mGiJJB3qHWql6+jeR5JDEcFGKE8/oFMkCtg78E6Gv6FcJ6qwB3Ks4MRfZlLmTPcwPj0pf6789M9FRnTsPMp3HHmeoVsfM4ujZYgk3Az+bDU4En6/8U4RCdl0CAmRGwMt9QtKzSygiHivCs/BpqkQ+Coq0HezaYDzBrNF3Q0gsxgy+5DALwSaQqAsKatb73w7i4EuyzrgfPW8OvkUj3i2Zc2zuY8FCElZY1H7igQK4P/EFyKyiRbyTzVsvwbqBwPPQuVzT8wwmOqcCmQACnuLD9EiF0iP2sAtWTJ0H2HosptVqxQbqmsvGHLzAza1jb1buLBgeIvPwDRP1sQmxVRgD1JkzRiZkt8EwN6MYzqtmfPA32Aukoji47n1zmyombJlAKkC8gKSXc6XTByOXcFNcYQ0BbyT9Tngn9K+YZUmSG/Wf33GKOCQc80I07FnOOsfYUpU43Vjv7xuFdyJn9vS9nxZzhZURQzrfJg4mgcqOTRGHIFGHdvrnVzDJX60gWQMoRadLjLoknGH86BUScmEJ0dWwhHS8dBeYboTNJuiW2EQtcnvDawQ8F79bhyNLVhM2ICoaf9Rok/lMjLUEN+6HzOGzw/rQYtJaJ6trF5CFwZIf3q2kHbW6wybwU/KTg= +- test $CARGO_FLAGS = "--all-features" && travis-cargo --only nightly-2017-09-05 doc-upload diff --git a/Cargo.toml b/Cargo.toml index e2b53cc..bd57c2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,15 +14,23 @@ categories = ["web-programming"] [badges] travis-ci = { repository = "lawliet89/rocket_cors" } +[features] +default = ["serialization"] + +# Serialization and deserialization support for settings +serialization = ["serde", "serde_derive", "unicase_serde", "url_serde"] + [dependencies] log = "0.3" rocket = "0.3" -serde = "1.0" -serde_derive = "1.0" unicase = "2.0" -unicase_serde = "0.1.0" url = "1.5.1" -url_serde = "0.2.0" + +# Optional dependencies that are activated by the various features +serde = { version = "1.0", optional = true } +serde_derive = { version = "1.0", optional = true } +unicase_serde = { version = "0.1.0", optional = true } +url_serde = { version = "0.2.0", optional = true } [build-dependencies] ansi_term = "0.9" @@ -33,3 +41,10 @@ hyper = "0.10" rocket_codegen = "0.3" serde_json = "1.0" serde_test = "1.0" + +[[example]] +name = "json" +required-features = ["serialization"] + +[package.metadata.docs.rs] +all-features = true diff --git a/examples/json.rs b/examples/json.rs index b4086cb..989638e 100644 --- a/examples/json.rs +++ b/examples/json.rs @@ -1,4 +1,6 @@ //! This example is to demonstrate the JSON serialization and deserialization of the Cors settings +//! +//! Note: This requires the `serialization` feature which is enabled by default. extern crate rocket; extern crate rocket_cors as cors; extern crate serde_json; diff --git a/src/headers.rs b/src/headers.rs index 71dfee7..48060f0 100644 --- a/src/headers.rs +++ b/src/headers.rs @@ -9,14 +9,18 @@ use rocket::{self, Outcome}; use rocket::http::Status; use rocket::request::{self, FromRequest}; use unicase::UniCase; -use unicase_serde; use url; + +#[cfg(feature = "serialization")] +use unicase_serde; +#[cfg(feature = "serialization")] use url_serde; /// A case insensitive header name -#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug, Hash)] +#[derive(Eq, PartialEq, Clone, Debug, Hash)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct HeaderFieldName( - #[serde(with = "unicase_serde::unicase")] + #[cfg_attr(feature = "serialization", serde(with = "unicase_serde::unicase"))] UniCase ); @@ -58,9 +62,10 @@ impl FromStr for HeaderFieldName { pub type HeaderFieldNamesSet = HashSet; /// A wrapped `url::Url` to allow for deserialization -#[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Clone, Hash, Debug)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct Url( - #[serde(with = "url_serde")] + #[cfg_attr(feature = "serialization", serde(with = "url_serde"))] url::Url ); diff --git a/src/lib.rs b/src/lib.rs index 060b331..0596e1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,16 @@ //! rocket_cors = { git = "https://github.com/lawliet89/rocket_cors", branch = "master" } //! ``` //! +//! ## Features +//! +//! By default, a `serialization` feature is enabled in this crate that allows you to (de)serialize +//! the `Cors` struct that is described below. If you would like to disable this, simply change +//! your `Cargo.toml` to: +//! +//! ```toml +//! rocket_cors = { version = "0.2.0", default-features = false } +//! ``` +//! //! ## Usage //! //! Before you can add CORS responses to your application, you need to create a `Cors` struct that @@ -52,7 +62,8 @@ //! The [`Cors` struct](struct.Cors.html) contains the settings for CORS requests to be validated //! and for responses to be generated. Defaults are defined for every field in the struct, and //! are documented on the [`Cors` struct](struct.Cors.html) page. You can also deserialize -//! the struct from some format like JSON, YAML or TOML. +//! the struct from some format like JSON, YAML or TOML when the default `serialization` feature +//! is enabled. //! //! ### Three modes of operation //! @@ -437,18 +448,25 @@ extern crate log; #[macro_use] extern crate rocket; +extern crate unicase; +extern crate url; + +#[cfg(feature = "serialization")] extern crate serde; +#[cfg(feature = "serialization")] #[macro_use] extern crate serde_derive; -extern crate unicase; +#[cfg(feature = "serialization")] extern crate unicase_serde; -extern crate url; +#[cfg(feature = "serialization")] extern crate url_serde; #[cfg(test)] extern crate hyper; +#[cfg(feature = "serialization")] #[cfg(test)] extern crate serde_test; +#[cfg(feature = "serialization")] #[cfg(test)] extern crate serde_json; @@ -471,7 +489,6 @@ use rocket::{Outcome, State}; use rocket::http::{self, Status}; use rocket::request::{Request, FromRequest}; use rocket::response; -use serde::{Serialize, Deserialize}; use headers::{HeaderFieldName, HeaderFieldNamesSet, Origin, AccessControlRequestHeaders, AccessControlRequestMethod, Url}; @@ -599,7 +616,8 @@ impl<'r> response::Responder<'r> for Error { /// /// This enum is serialized and deserialized /// ["Externally tagged"](https://serde.rs/enum-representations.html) -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub enum AllOrSome { /// Everything is allowed. Usually equivalent to the "*" value. All, @@ -672,42 +690,52 @@ impl fmt::Display for Method { } } -impl Serialize for Method { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(self.as_str()) +#[cfg(feature = "serialization")] +mod method_serde { + use std::fmt; + use std::str::FromStr; + + use serde::{self, Serialize, Deserialize}; + + use Method; + + impl Serialize for Method { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.as_str()) + } } -} -impl<'de> Deserialize<'de> for Method { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - use serde::de::{self, Visitor}; + impl<'de> Deserialize<'de> for Method { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::{self, Visitor}; - struct MethodVisitor; - impl<'de> Visitor<'de> for MethodVisitor { - type Value = Method; + struct MethodVisitor; + impl<'de> Visitor<'de> for MethodVisitor { + type Value = Method; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string containing a HTTP Verb") - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string containing a HTTP Verb") + } - fn visit_str(self, s: &str) -> Result - where - E: de::Error, - { - match Self::Value::from_str(s) { - Ok(value) => Ok(value), - Err(e) => Err(de::Error::custom(format!("{:?}", e))), + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + match Self::Value::from_str(s) { + Ok(value) => Ok(value), + Err(e) => Err(de::Error::custom(format!("{:?}", e))), + } } } - } - deserializer.deserialize_string(MethodVisitor) + deserializer.deserialize_string(MethodVisitor) + } } } @@ -796,7 +824,8 @@ impl AllowedHeaders { /// documentation at the [crate root](index.html) for usage information. /// /// You create a new copy of this struct by defining the configurations in the fields below. -/// This struct can also be deserialized by serde. +/// This struct can also be deserialized by serde with the `serialization` feature which is +/// enabled by default. /// /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) is implemented for this /// struct. The default for each field is described in the docuementation for the field. @@ -864,7 +893,8 @@ impl AllowedHeaders { /// } /// /// ``` -#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)] +#[derive(Eq, PartialEq, Clone, Debug)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub struct Cors { /// Origins that are allowed to make requests. /// Will be verified against the `Origin` request header. @@ -882,7 +912,7 @@ pub struct Cors { /// Defaults to `All`. /// /// ``` - #[serde(default)] + #[cfg_attr(feature = "serialization", serde(default))] pub allowed_origins: AllowedOrigins, /// The list of methods which the allowed origins are allowed to access for /// non-simple requests. @@ -891,7 +921,7 @@ pub struct Cors { /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). /// /// Defaults to `[GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE]` - #[serde(default = "Cors::default_allowed_methods")] + #[cfg_attr(feature = "serialization", serde(default = "Cors::default_allowed_methods"))] pub allowed_methods: AllowedMethods, /// The list of header field names which can be used when this resource is accessed by allowed /// origins. @@ -903,7 +933,7 @@ pub struct Cors { /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). /// /// Defaults to `All`. - #[serde(default)] + #[cfg_attr(feature = "serialization", serde(default))] pub allowed_headers: AllOrSome>, /// Allows users to make authenticated requests. /// If true, injects the `Access-Control-Allow-Credentials` header in responses. @@ -914,7 +944,7 @@ pub struct Cors { /// in an `Error::CredentialsWithWildcardOrigin` error during Rocket launch or runtime. /// /// Defaults to `false`. - #[serde(default)] + #[cfg_attr(feature = "serialization", serde(default))] pub allow_credentials: bool, /// The list of headers which are safe to expose to the API of a CORS API specification. /// This corresponds to the `Access-Control-Expose-Headers` responde header. @@ -923,13 +953,13 @@ pub struct Cors { /// [Resource Processing Model](https://www.w3.org/TR/cors/#resource-processing-model). /// /// This defaults to an empty set. - #[serde(default)] + #[cfg_attr(feature = "serialization", serde(default))] pub expose_headers: HashSet, /// The maximum time for which this CORS request maybe cached. This value is set as the /// `Access-Control-Max-Age` header. /// /// This defaults to `None` (unset). - #[serde(default)] + #[cfg_attr(feature = "serialization", serde(default))] pub max_age: Option, /// If true, and the `allowed_origins` parameter is `All`, a wildcard /// `Access-Control-Allow-Origin` response header is sent, rather than the request’s @@ -943,14 +973,14 @@ pub struct Cors { /// in an `Error::CredentialsWithWildcardOrigin` error during Rocket launch or runtime. /// /// Defaults to `false`. - #[serde(default)] + #[cfg_attr(feature = "serialization", serde(default))] pub send_wildcard: bool, /// When used as Fairing, Cors will need to redirect failed CORS checks to a custom route to /// be mounted by the fairing. Specify the base the route so that it doesn't clash with any /// of your existing routes. /// /// Defaults to "/cors" - #[serde(default = "Cors::default_fairing_route_base")] + #[cfg_attr(feature = "serialization", serde(default = "Cors::default_fairing_route_base"))] pub fairing_route_base: String, } @@ -1753,6 +1783,7 @@ mod tests { use rocket::local::Client; use rocket::http::Header; + #[cfg(feature = "serialization")] use serde_json; use super::*; @@ -1808,6 +1839,7 @@ mod tests { } /// Check that the the default deserialization matches the one returned by `Default::default` + #[cfg(feature = "serialization")] #[test] fn cors_default_deserialization_is_correct() { let deserialized: Cors = serde_json::from_str("{}").expect("To not fail"); @@ -2039,11 +2071,13 @@ mod tests { } - #[derive(Debug, PartialEq, Serialize, Deserialize)] + #[derive(Debug, PartialEq)] + #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] struct MethodTest { method: ::Method, } + #[cfg(feature = "serialization")] #[test] fn method_serde_roundtrip() { use serde_test::{Token, assert_tokens};