diff --git a/.appveyor.yml b/.appveyor.yml index 523f526..ee313e2 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,5 +15,5 @@ install: - cargo -vV build: false test_script: - - cargo test --features blocking,gzip -- --test-threads=1 + - cargo test --features blocking,gzip,json -- --test-threads=1 skip_branch_with_pr: true diff --git a/.travis.yml b/.travis.yml index 8af4be3..f5e6803 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,6 +42,11 @@ matrix: - rust: nightly env: FEATURES="--features gzip" + # optional json + #- rust: stable + - rust: nightly + env: FEATURES="--features json" + # socks #- rust: stable #- rust: nightly diff --git a/Cargo.toml b/Cargo.toml index 7e5b939..4cfa10f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,8 @@ cookies = ["cookie_crate", "cookie_store"] gzip = ["async-compression"] +json = ["serde_json"] + #trust-dns = ["trust-dns-resolver"] [dependencies] @@ -59,7 +61,6 @@ time = "0.1.42" # TODO: candidates for optional features serde = "1.0" -serde_json = "1.0" serde_urlencoded = "0.6.1" # Optional deps... @@ -85,6 +86,9 @@ cookie_store = { version = "0.9", optional = true } ## gzip async-compression = { version = "0.1.0-alpha.4", default-features = false, features = ["gzip", "stream"], optional = true } +## json +serde_json = { version = "1.0", optional = true } + ## socks #socks = { version = "0.3.2", optional = true } @@ -123,6 +127,16 @@ name = "blocking" path = "examples/blocking.rs" required-features = ["blocking"] +[[example]] +name = "json_dynamic" +path = "examples/json_dynamic.rs" +required-features = ["json"] + +[[example]] +name = "json_typed" +path = "examples/json_typed.rs" +required-features = ["json"] + [[test]] name = "blocking" path = "tests/blocking.rs" diff --git a/src/async_impl/request.rs b/src/async_impl/request.rs index b8366e4..138288a 100644 --- a/src/async_impl/request.rs +++ b/src/async_impl/request.rs @@ -3,6 +3,7 @@ use std::future::Future; use base64::encode; use serde::Serialize; +#[cfg(feature = "json")] use serde_json; use serde_urlencoded; @@ -287,10 +288,15 @@ impl RequestBuilder { /// Send a JSON body. /// + /// # Optional + /// + /// This requires the optional `json` feature enabled. + /// /// # Errors /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. + #[cfg(feature = "json")] pub fn json(mut self, json: &T) -> RequestBuilder { let mut error = None; if let Ok(ref mut req) = self.request { diff --git a/src/async_impl/response.rs b/src/async_impl/response.rs index 9b51f0c..c82a764 100644 --- a/src/async_impl/response.rs +++ b/src/async_impl/response.rs @@ -11,7 +11,9 @@ use hyper::header::CONTENT_LENGTH; use hyper::{HeaderMap, StatusCode, Version}; use log::debug; use mime::Mime; +#[cfg(feature = "json")] use serde::de::DeserializeOwned; +#[cfg(feature = "json")] use serde_json; use tokio::timer::Delay; use url::Url; @@ -197,6 +199,10 @@ impl Response { /// Try to deserialize the response body as JSON. /// + /// # Optional + /// + /// This requires the optional `json` feature enabled. + /// /// # Examples /// /// ``` @@ -229,7 +235,9 @@ impl Response { /// This method fails whenever the response body is not in JSON format /// or it cannot be properly deserialized to target type `T`. For more /// details please see [`serde_json::from_reader`]. + /// /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html + #[cfg(feature = "json")] pub async fn json(self) -> crate::Result { let full = self.bytes().await?; diff --git a/src/blocking/request.rs b/src/blocking/request.rs index 6d236f6..b8545d1 100644 --- a/src/blocking/request.rs +++ b/src/blocking/request.rs @@ -3,6 +3,7 @@ use std::fmt; use base64::encode; use http::HttpTryFrom; use serde::Serialize; +#[cfg(feature = "json")] use serde_json; use serde_urlencoded; @@ -388,6 +389,12 @@ impl RequestBuilder { /// Sets the body to the JSON serialization of the passed value, and /// also sets the `Content-Type: application/json` header. /// + /// # Optional + /// + /// This requires the optional `json` feature enabled. + /// + /// # Examples + /// /// ```rust /// # use reqwest::Error; /// # use std::collections::HashMap; @@ -408,6 +415,7 @@ impl RequestBuilder { /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. + #[cfg(feature = "json")] pub fn json(mut self, json: &T) -> RequestBuilder { let mut error = None; if let Ok(ref mut req) = self.request { @@ -552,6 +560,7 @@ mod tests { use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST}; use crate::Method; use serde::Serialize; + #[cfg(feature = "json")] use serde_json; use serde_urlencoded; use std::collections::{BTreeMap, HashMap}; @@ -776,6 +785,7 @@ mod tests { } #[test] + #[cfg(feature = "json")] fn add_json() { let client = Client::new(); let some_url = "https://google.com/"; @@ -796,6 +806,7 @@ mod tests { } #[test] + #[cfg(feature = "json")] fn add_json_fail() { use serde::ser::Error as _; use serde::{Serialize, Serializer}; diff --git a/src/blocking/response.rs b/src/blocking/response.rs index 92b9e1b..6123f82 100644 --- a/src/blocking/response.rs +++ b/src/blocking/response.rs @@ -7,6 +7,7 @@ use std::time::Duration; use http; use hyper::header::HeaderMap; +#[cfg(feature = "json")] use serde::de::DeserializeOwned; use super::client::KeepCoreThreadAlive; @@ -50,6 +51,7 @@ impl Response { /// Checking for general status class: /// /// ```rust + /// # #[cfg(feature = "json")] /// # fn run() -> Result<(), Box> { /// let resp = reqwest::blocking::get("http://httpbin.org/get")?; /// if resp.status().is_success() { @@ -181,6 +183,10 @@ impl Response { /// Try and deserialize the response body as JSON using `serde`. /// + /// # Optional + /// + /// This requires the optional `json` feature enabled. + /// /// # Examples /// /// ```rust @@ -208,7 +214,9 @@ impl Response { /// This method fails whenever the response body is not in JSON format /// or it cannot be properly deserialized to target type `T`. For more /// details please see [`serde_json::from_reader`]. + /// /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html + #[cfg(feature = "json")] pub fn json(self) -> crate::Result { wait::timeout(self.inner.json(), self.timeout).map_err(|e| match e { wait::Waited::TimedOut(e) => crate::error::decode(e), diff --git a/src/lib.rs b/src/lib.rs index bdc87c7..f6d36fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,12 +95,13 @@ //! //! There is also a `json` method helper on the [`RequestBuilder`][builder] that works in //! a similar fashion the `form` method. It can take any value that can be -//! serialized into JSON. +//! serialized into JSON. The feature `json` is required. //! //! ```rust //! # use reqwest::Error; //! # use std::collections::HashMap; //! # +//! # #[cfg(feature = "json")] //! # async fn run() -> Result<(), Error> { //! // This will POST a body of `{"lang":"rust","body":"json"}` //! let mut map = HashMap::new(); @@ -159,6 +160,7 @@ //! - **blocking**: Provides the [blocking][] client API. //! - **cookies**: Provides cookie session support. //! - **gzip**: Provides response body gzip decompression. +//! - **json**: Provides serialization and deserialization for JSON bodies. //! //! //! [hyper]: http://hyper.rs diff --git a/tests/blocking.rs b/tests/blocking.rs index c6003d8..4519094 100644 --- a/tests/blocking.rs +++ b/tests/blocking.rs @@ -38,6 +38,7 @@ fn test_response_non_utf_8_text() { } #[test] +#[cfg(feature = "json")] fn test_response_json() { let server = server::http(move |_req| async { http::Response::new("\"Hello\"".into()) }); diff --git a/tests/client.rs b/tests/client.rs index 08b76f8..1bf26b9 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -45,6 +45,7 @@ async fn response_text() { } #[tokio::test] +#[cfg(feature = "json")] async fn response_json() { let _ = env_logger::try_init();