Replace rustc_serialize with serde

This commit is contained in:
Curtis McEnroe 2017-06-04 19:53:04 -04:00
parent fea3ff6086
commit 62e8d11cb6
No known key found for this signature in database
GPG Key ID: CEA2F97ADCFCD77C
12 changed files with 186 additions and 636 deletions

91
Cargo.lock generated
View File

@ -5,9 +5,9 @@ dependencies = [
"chrono 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper-native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"yup-hyper-mock 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -50,6 +50,7 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -79,6 +80,11 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "dtoa"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "foreign-types"
version = "0.2.0"
@ -144,7 +150,7 @@ dependencies = [
[[package]]
name = "itoa"
version = "0.1.1"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -270,6 +276,11 @@ name = "pkg-config"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "quote"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.3.15"
@ -283,11 +294,6 @@ name = "redox_syscall"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.1.7"
@ -346,17 +352,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "0.7.15"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_json"
version = "0.7.4"
name = "serde_derive"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive_internals"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synom"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -409,6 +453,11 @@ name = "unicode-normalization"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "1.4.1"
@ -461,6 +510,7 @@ dependencies = [
"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec"
"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
"checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d"
"checksum gcc 0.3.50 (registry+https://github.com/rust-lang/crates.io-index)" = "5f837c392f2ea61cb1576eac188653df828c861b7137d74ea4a5caa89621f9e6"
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
@ -468,7 +518,7 @@ dependencies = [
"checksum hyper 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cb7031283266d12f2d4bf30b624bc2b2fd21bbcc00863c9928e87dc5e1699d2e"
"checksum hyper-native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "48fecce9e67dff46707980abb41f10eaa49cf0eded8dd0c26ae94b3ae5c3f705"
"checksum idna 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2233d4940b1f19f0418c158509cd7396b8d70a5db5705ce410914dc8fa603b37"
"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
@ -485,17 +535,21 @@ dependencies = [
"checksum openssl 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b34cd77cf91301fff3123fbd46b065c3b728b17a392835de34c397315dce5586"
"checksum openssl-sys 0.9.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e035022a50faa380bd7ccdbd184d946ce539ebdb0a358780de92a995882af97a"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
"checksum redox_syscall 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "3041aeb6000db123d2c9c751433f526e1f404b23213bd733167ab770c3989b4d"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
"checksum schannel 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4e45ac5e9e4698c1c138d2972bedcd90b81fe1efeba805449d2bdd54512de5f9"
"checksum secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f412dfa83308d893101dd59c10d6fda8283465976c28c287c5c855bf8d216bc"
"checksum security-framework 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "42ddf098d78d0b64564b23ee6345d07573e7d10e52ad86875d89ddf5f8378a02"
"checksum security-framework-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "5bacdada57ea62022500c457c8571c17dfb5e6240b7c8eac5916ffa8c7138a55"
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
"checksum serde 0.7.15 (registry+https://github.com/rust-lang/crates.io-index)" = "1b0e0732aa8ec4267f61815a396a942ba3525062e3bd5520aa8419927cfc0a92"
"checksum serde_json 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b22e8a0554f31cb0f501e027de07b253553b308124f61c57598b9678dba35c0b"
"checksum serde 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f530d36fb84ec48fb7146936881f026cdbf4892028835fd9398475f82c1bb4"
"checksum serde_derive 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "10552fad5500771f3902d0c5ba187c5881942b811b7ba0d8fbbfbf84d80806d3"
"checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"
"checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b"
"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
"checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3"
"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
@ -503,6 +557,7 @@ dependencies = [
"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
"checksum unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6a2c4e3710edd365cd7e78383153ed739fa31af19f9172f72d3575060f5a43a"
"checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff"
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
"checksum url 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2ba3456fbe5c0098cb877cf08b92b76c3e18e0be9e47c35b487220d377d24e"
"checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47"
"checksum version_check 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2bb3950bf29e36796dea723df1747619dd331881aefef75b7cf1c58fdd738afe"

View File

@ -12,20 +12,14 @@ documentation = "https://cmcenroe.me/inth-oauth2/inth_oauth2"
repository = "https://github.com/programble/inth-oauth2"
readme = "README.md"
[features]
default = ["serde"]
[dependencies]
chrono = "0.3"
chrono = { version = "0.3", features = ["serde"] }
hyper = "0.10"
rustc-serialize = "0.3.16"
serde = "1.0.8"
serde_derive = "1.0.5"
serde_json = "1.0.2"
url = "1.1.0"
[dependencies.serde]
version = "0.7.0"
optional = true
[dev-dependencies]
hyper-native-tls = "0.2"
serde_json = "0.7.0"
yup-hyper-mock = "2.0"

View File

@ -2,7 +2,7 @@ use std::error::Error;
use std::{fmt, io};
use hyper;
use rustc_serialize::json;
use serde_json;
use url;
use client::response::ParseError;
@ -18,7 +18,7 @@ pub enum ClientError {
/// Hyper error.
Hyper(hyper::Error),
/// JSON error.
Json(json::ParserError),
Json(serde_json::Error),
/// Response parse error.
Parse(ParseError),
/// OAuth 2.0 error.
@ -75,6 +75,6 @@ macro_rules! impl_from {
impl_from!(ClientError::Io, io::Error);
impl_from!(ClientError::Url, url::ParseError);
impl_from!(ClientError::Hyper, hyper::Error);
impl_from!(ClientError::Json, json::ParserError);
impl_from!(ClientError::Json, serde_json::Error);
impl_from!(ClientError::Parse, ParseError);
impl_from!(ClientError::OAuth2, OAuth2Error);

View File

@ -3,7 +3,7 @@
use std::marker::PhantomData;
use hyper::{self, header, mime};
use rustc_serialize::json::Json;
use serde_json::{self, Value};
use url::Url;
use url::form_urlencoded::Serializer;
@ -105,7 +105,7 @@ impl<P: Provider> Client<P> {
&'a self,
http_client: &hyper::Client,
mut body: Serializer<String>
) -> Result<Json, ClientError> {
) -> Result<Value, ClientError> {
if P::credentials_in_body() {
body.append_pair("client_id", &self.client_id);
body.append_pair("client_secret", &self.client_secret);
@ -129,7 +129,7 @@ impl<P: Provider> Client<P> {
.body(&body);
let mut response = try!(request.send());
let json = try!(Json::from_reader(&mut response));
let json = serde_json::from_reader(&mut response)?;
let error = OAuth2Error::from_response(&json);

View File

@ -3,19 +3,19 @@
use std::error::Error;
use std::fmt;
use rustc_serialize::json::{self, Json};
use serde_json::Value;
/// Response parsing.
pub trait FromResponse: Sized {
/// Parse a JSON response.
fn from_response(json: &Json) -> Result<Self, ParseError>;
fn from_response(json: &Value) -> Result<Self, ParseError>;
/// Parse a JSON response, inheriting missing values from the previous instance.
///
/// Necessary for parsing refresh token responses where the absence of a new refresh token
/// implies that the previous refresh token is still valid.
#[allow(unused_variables)]
fn from_response_inherit(json: &Json, prev: &Self) -> Result<Self, ParseError> {
fn from_response_inherit(json: &Value, prev: &Self) -> Result<Self, ParseError> {
FromResponse::from_response(json)
}
}
@ -54,42 +54,3 @@ impl fmt::Display for ParseError {
impl Error for ParseError {
fn description(&self) -> &str { "response parse error" }
}
/// JSON helper for response parsing.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct JsonHelper<'a>(pub &'a Json);
impl<'a> JsonHelper<'a> {
/// Returns self as a `JsonObjectHelper` or fails with `ParseError::ExpectedType`.
pub fn as_object(&self) -> Result<JsonObjectHelper<'a>, ParseError>{
self.0.as_object()
.ok_or_else(|| ParseError::ExpectedType("object"))
.map(|o| JsonObjectHelper(o))
}
}
/// JSON object helper for response parsing.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct JsonObjectHelper<'a>(pub &'a json::Object);
impl<'a> JsonObjectHelper<'a> {
/// Gets a field as a string or returns `None`.
pub fn get_string_option(&self, key: &'static str) -> Option<&'a str> {
self.0.get(key).and_then(Json::as_string)
}
/// Gets a field as a string or fails with `ParseError::ExpectedFieldType`.
pub fn get_string(&self, key: &'static str) -> Result<&'a str, ParseError> {
self.get_string_option(key).ok_or_else(|| ParseError::ExpectedFieldType(key, "string"))
}
/// Gets a field as an i64 or returns `None`.
pub fn get_i64_option(&self, key: &'static str) -> Option<i64> {
self.0.get(key).and_then(Json::as_i64)
}
/// Gets a field as an i64 or fails with `ParseError::ExpectedFieldType`.
pub fn get_i64(&self, key: &'static str) -> Result<i64, ParseError> {
self.get_i64_option(key).ok_or_else(|| ParseError::ExpectedFieldType(key, "i64"))
}
}

View File

@ -3,9 +3,9 @@
use std::error::Error;
use std::fmt;
use rustc_serialize::json::Json;
use serde_json::Value;
use client::response::{FromResponse, ParseError, JsonHelper};
use client::response::{FromResponse, ParseError};
/// OAuth 2.0 error codes.
///
@ -87,12 +87,14 @@ impl Error for OAuth2Error {
}
impl FromResponse for OAuth2Error {
fn from_response(json: &Json) -> Result<Self, ParseError> {
let obj = try!(JsonHelper(json).as_object());
fn from_response(json: &Value) -> Result<Self, ParseError> {
let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
let code = try!(obj.get_string("error"));
let description = obj.get_string_option("error_description");
let uri = obj.get_string_option("error_uri");
let code = obj.get("error")
.and_then(Value::as_str)
.ok_or(ParseError::ExpectedFieldType("error", "string"))?;
let description = obj.get("error_description").and_then(Value::as_str);
let uri = obj.get("error_uri").and_then(Value::as_str);
Ok(OAuth2Error {
code: code.into(),
@ -104,14 +106,12 @@ impl FromResponse for OAuth2Error {
#[cfg(test)]
mod tests {
use rustc_serialize::json::Json;
use client::response::{FromResponse, ParseError};
use super::{OAuth2Error, OAuth2ErrorCode};
#[test]
fn from_response_empty() {
let json = Json::from_str("{}").unwrap();
let json = "{}".parse().unwrap();
assert_eq!(
ParseError::ExpectedFieldType("error", "string"),
OAuth2Error::from_response(&json).unwrap_err()
@ -120,7 +120,7 @@ mod tests {
#[test]
fn from_response() {
let json = Json::from_str(r#"{"error":"invalid_request"}"#).unwrap();
let json = r#"{"error":"invalid_request"}"#.parse().unwrap();
assert_eq!(
OAuth2Error {
code: OAuth2ErrorCode::InvalidRequest,
@ -133,7 +133,8 @@ mod tests {
#[test]
fn from_response_with_description() {
let json = Json::from_str(r#"{"error":"invalid_request","error_description":"foo"}"#)
let json = r#"{"error":"invalid_request","error_description":"foo"}"#
.parse()
.unwrap();
assert_eq!(
OAuth2Error {
@ -147,9 +148,9 @@ mod tests {
#[test]
fn from_response_with_uri() {
let json = Json::from_str(
r#"{"error":"invalid_request","error_uri":"http://example.com"}"#
).unwrap();
let json = r#"{"error":"invalid_request","error_uri":"http://example.com"}"#
.parse()
.unwrap();
assert_eq!(
OAuth2Error {
code: OAuth2ErrorCode::InvalidRequest,

View File

@ -115,37 +115,19 @@
//!
//! ### Persisting tokens
//!
//! All token types implement `Encodable` / `Decodable` from `rustc_serialize` and `Serialize` /
//! `Deserialize` from `serde` (with the default `serde` feature).
//!
//! ```no_run
//! # extern crate inth_oauth2;
//! # extern crate rustc_serialize;
//! # use inth_oauth2::Client;
//! # use inth_oauth2::provider::google::Installed;
//! use rustc_serialize::json;
//! # fn main() {
//! # let http_client = Default::default();
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
//! # let token = client.request_token(&http_client, "").unwrap();
//! let json = json::encode(&token).unwrap();
//! # }
//! ```
//! All token types implement `Serialize` and `Deserialize` from `serde`.
//!
//! ```no_run
//! # extern crate inth_oauth2;
//! extern crate serde_json;
//! # use inth_oauth2::Client;
//! # use inth_oauth2::provider::google::Installed;
//! # #[cfg(feature = "serde")]
//! # fn main() {
//! # let http_client = Default::default();
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
//! # let token = client.request_token(&http_client, "").unwrap();
//! let json = serde_json::to_string(&token).unwrap();
//! # }
//! # #[cfg(not(feature = "serde"))]
//! # fn main() { }
//! ```
#![warn(
@ -160,21 +142,18 @@
variant_size_differences
)]
#[macro_use]
extern crate serde_derive;
extern crate chrono;
extern crate hyper;
extern crate rustc_serialize;
extern crate serde_json;
extern crate url;
#[cfg(feature = "serde")]
extern crate serde;
pub use token::{Token, Lifetime};
pub use client::{Client, ClientError};
pub mod token;
pub mod provider;
pub mod error;
pub mod client;
#[cfg(all(test, feature = "serde"))]
extern crate serde_json;
pub use token::{Token, Lifetime};
pub use client::{Client, ClientError};

View File

@ -1,13 +1,13 @@
use hyper::header;
use rustc_serialize::json::Json;
use serde_json::Value;
use super::{Token, Lifetime};
use client::response::{FromResponse, ParseError, JsonHelper};
use client::response::{FromResponse, ParseError};
/// The bearer token type.
///
/// See [RFC 6750](http://tools.ietf.org/html/rfc6750).
#[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Bearer<L: Lifetime> {
access_token: String,
scope: Option<String>,
@ -27,16 +27,20 @@ impl<'a, L: Lifetime> Into<header::Authorization<header::Bearer>> for &'a Bearer
}
impl<L: Lifetime> Bearer<L> {
fn from_response_and_lifetime(json: &Json, lifetime: L) -> Result<Self, ParseError> {
let obj = try!(JsonHelper(json).as_object());
fn from_response_and_lifetime(json: &Value, lifetime: L) -> Result<Self, ParseError> {
let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
let token_type = try!(obj.get_string("token_type"));
let token_type = obj.get("token_type")
.and_then(Value::as_str)
.ok_or(ParseError::ExpectedFieldType("token_type", "string"))?;
if token_type != "Bearer" && token_type != "bearer" {
return Err(ParseError::ExpectedFieldValue("token_type", "Bearer"));
}
let access_token = try!(obj.get_string("access_token"));
let scope = obj.get_string_option("scope");
let access_token = obj.get("access_token")
.and_then(Value::as_str)
.ok_or(ParseError::ExpectedFieldType("access_token", "string"))?;
let scope = obj.get("scope").and_then(Value::as_str);
Ok(Bearer {
access_token: access_token.into(),
@ -47,123 +51,20 @@ impl<L: Lifetime> Bearer<L> {
}
impl<L: Lifetime> FromResponse for Bearer<L> {
fn from_response(json: &Json) -> Result<Self, ParseError> {
let lifetime = try!(FromResponse::from_response(json));
fn from_response(json: &Value) -> Result<Self, ParseError> {
let lifetime = FromResponse::from_response(json)?;
Bearer::from_response_and_lifetime(json, lifetime)
}
fn from_response_inherit(json: &Json, prev: &Self) -> Result<Self, ParseError> {
let lifetime = try!(FromResponse::from_response_inherit(json, &prev.lifetime));
fn from_response_inherit(json: &Value, prev: &Self) -> Result<Self, ParseError> {
let lifetime = FromResponse::from_response_inherit(json, &prev.lifetime)?;
Bearer::from_response_and_lifetime(json, lifetime)
}
}
#[cfg(feature = "serde")]
mod serde {
use std::marker::PhantomData;
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::{ser, de};
use token::Lifetime;
use super::Bearer;
impl<L: Lifetime + Serialize> Serialize for Bearer<L> {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.serialize_struct("Bearer", SerVisitor(self, 0))
}
}
struct SerVisitor<'a, L: Lifetime + Serialize + 'a>(&'a Bearer<L>, u8);
impl<'a, L: Lifetime + Serialize + 'a> ser::MapVisitor for SerVisitor<'a, L> {
fn visit<S: Serializer>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error> {
self.1 += 1;
match self.1 {
1 => serializer.serialize_struct_elt("access_token", &self.0.access_token).map(Some),
2 => serializer.serialize_struct_elt("scope", &self.0.scope).map(Some),
3 => serializer.serialize_struct_elt("lifetime", &self.0.lifetime).map(Some),
_ => Ok(None),
}
}
fn len(&self) -> Option<usize> { Some(3) }
}
impl<L: Lifetime + Deserialize> Deserialize for Bearer<L> {
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
static FIELDS: &'static [&'static str] = &["access_token", "scope", "lifetime"];
deserializer.deserialize_struct("Bearer", FIELDS, DeVisitor(PhantomData))
}
}
struct DeVisitor<L: Lifetime + Deserialize>(PhantomData<L>);
impl<L: Lifetime + Deserialize> de::Visitor for DeVisitor<L> {
type Value = Bearer<L>;
fn visit_map<V: de::MapVisitor>(&mut self, mut visitor: V) -> Result<Bearer<L>, V::Error> {
let mut access_token = None;
let mut scope = None;
let mut lifetime = None;
loop {
match try!(visitor.visit_key()) {
Some(Field::AccessToken) => access_token = Some(try!(visitor.visit_value())),
Some(Field::Scope) => scope = Some(try!(visitor.visit_value())),
Some(Field::Lifetime) => lifetime = Some(try!(visitor.visit_value())),
None => break,
}
}
let access_token = match access_token {
Some(s) => s,
None => return visitor.missing_field("access_token"),
};
let lifetime = match lifetime {
Some(l) => l,
None => return visitor.missing_field("lifetime"),
};
try!(visitor.end());
Ok(Bearer {
access_token: access_token,
scope: scope,
lifetime: lifetime,
})
}
}
enum Field {
AccessToken,
Scope,
Lifetime,
}
impl Deserialize for Field {
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer.deserialize(FieldVisitor)
}
}
struct FieldVisitor;
impl de::Visitor for FieldVisitor {
type Value = Field;
fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Field, E> {
match value {
"access_token" => Ok(Field::AccessToken),
"scope" => Ok(Field::Scope),
"lifetime" => Ok(Field::Lifetime),
_ => Err(de::Error::custom("expected access_token, scope or lifetime")),
}
}
}
}
#[cfg(test)]
mod tests {
use chrono::{UTC, Duration};
use rustc_serialize::json::Json;
use client::response::{FromResponse, ParseError};
use token::{Static, Refresh};
@ -171,7 +72,7 @@ mod tests {
#[test]
fn from_response_with_invalid_token_type() {
let json = Json::from_str(r#"{"token_type":"MAC","access_token":"aaaaaaaa"}"#).unwrap();
let json = r#"{"token_type":"MAC","access_token":"aaaaaaaa"}"#.parse().unwrap();
assert_eq!(
ParseError::ExpectedFieldValue("token_type", "Bearer"),
Bearer::<Static>::from_response(&json).unwrap_err()
@ -180,7 +81,7 @@ mod tests {
#[test]
fn from_response_capital_b() {
let json = Json::from_str(r#"{"token_type":"Bearer","access_token":"aaaaaaaa"}"#).unwrap();
let json = r#"{"token_type":"Bearer","access_token":"aaaaaaaa"}"#.parse().unwrap();
assert_eq!(
Bearer {
access_token: String::from("aaaaaaaa"),
@ -193,7 +94,7 @@ mod tests {
#[test]
fn from_response_little_b() {
let json = Json::from_str(r#"{"token_type":"bearer","access_token":"aaaaaaaa"}"#).unwrap();
let json = r#"{"token_type":"bearer","access_token":"aaaaaaaa"}"#.parse().unwrap();
assert_eq!(
Bearer {
access_token: String::from("aaaaaaaa"),
@ -206,9 +107,9 @@ mod tests {
#[test]
fn from_response_with_scope() {
let json = Json::from_str(
r#"{"token_type":"Bearer","access_token":"aaaaaaaa","scope":"foo"}"#
).unwrap();
let json = r#"{"token_type":"Bearer","access_token":"aaaaaaaa","scope":"foo"}"#
.parse()
.unwrap();
assert_eq!(
Bearer {
access_token: String::from("aaaaaaaa"),
@ -221,14 +122,14 @@ mod tests {
#[test]
fn from_response_refresh() {
let json = Json::from_str(r#"
let json = r#"
{
"token_type":"Bearer",
"access_token":"aaaaaaaa",
"expires_in":3600,
"refresh_token":"bbbbbbbb"
}
"#).unwrap();
"#.parse().unwrap();
let bearer = Bearer::<Refresh>::from_response(&json).unwrap();
assert_eq!("aaaaaaaa", bearer.access_token);
assert_eq!(None, bearer.scope);
@ -240,23 +141,23 @@ mod tests {
#[test]
fn from_response_inherit_refresh() {
let json = Json::from_str(r#"
let json = r#"
{
"token_type":"Bearer",
"access_token":"aaaaaaaa",
"expires_in":3600,
"refresh_token":"bbbbbbbb"
}
"#).unwrap();
"#.parse().unwrap();
let prev = Bearer::<Refresh>::from_response(&json).unwrap();
let json = Json::from_str(r#"
let json = r#"
{
"token_type":"Bearer",
"access_token":"cccccccc",
"expires_in":3600
}
"#).unwrap();
"#.parse().unwrap();
let bearer = Bearer::<Refresh>::from_response_inherit(&json, &prev).unwrap();
assert_eq!("cccccccc", bearer.access_token);
assert_eq!(None, bearer.scope);
@ -265,19 +166,4 @@ mod tests {
assert!(refresh.expires() > &UTC::now());
assert!(refresh.expires() <= &(UTC::now() + Duration::seconds(3600)));
}
#[cfg(feature = "serde")]
#[test]
fn serialize_deserialize() {
use serde_json;
let original = Bearer {
access_token: String::from("foo"),
scope: Some(String::from("bar")),
lifetime: Static,
};
let serialized = serde_json::to_value(&original);
let deserialized = serde_json::from_value(serialized).unwrap();
assert_eq!(original, deserialized);
}
}

View File

@ -1,12 +1,11 @@
use chrono::{DateTime, UTC, Duration, TimeZone};
use rustc_serialize::json::Json;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use chrono::{DateTime, UTC, Duration};
use serde_json::Value;
use super::Lifetime;
use client::response::{FromResponse, ParseError, JsonHelper};
use client::response::{FromResponse, ParseError};
/// An expiring token.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Expiring {
expires: DateTime<UTC>,
}
@ -21,14 +20,16 @@ impl Lifetime for Expiring {
}
impl FromResponse for Expiring {
fn from_response(json: &Json) -> Result<Self, ParseError> {
let obj = try!(JsonHelper(json).as_object());
fn from_response(json: &Value) -> Result<Self, ParseError> {
let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
if obj.0.contains_key("refresh_token") {
if obj.contains_key("refresh_token") {
return Err(ParseError::UnexpectedField("refresh_token"));
}
let expires_in = try!(obj.get_i64("expires_in"));
let expires_in = obj.get("expires_in")
.and_then(Value::as_i64)
.ok_or(ParseError::ExpectedFieldType("expires_in", "i64"))?;
Ok(Expiring {
expires: UTC::now() + Duration::seconds(expires_in),
@ -36,159 +37,18 @@ impl FromResponse for Expiring {
}
}
#[derive(RustcEncodable, RustcDecodable)]
struct Serializable {
expires: i64,
}
impl<'a> From<&'a Expiring> for Serializable {
fn from(expiring: &Expiring) -> Self {
Serializable {
expires: expiring.expires.timestamp(),
}
}
}
impl Into<Expiring> for Serializable {
fn into(self) -> Expiring {
Expiring {
expires: UTC.timestamp(self.expires, 0),
}
}
}
impl Encodable for Expiring {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
Serializable::from(self).encode(s)
}
}
impl Decodable for Expiring {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
Serializable::decode(d).map(Into::into)
}
}
#[cfg(feature = "serde")]
mod serde {
use chrono::{UTC, TimeZone};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::{ser, de};
use super::Expiring;
impl Serialize for Expiring {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.serialize_struct("Expiring", SerVisitor(self, 0))
}
}
struct SerVisitor<'a>(&'a Expiring, u8);
impl<'a> ser::MapVisitor for SerVisitor<'a> {
fn visit<S: Serializer>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error> {
self.1 += 1;
match self.1 {
1 => serializer.serialize_struct_elt("expires", &self.0.expires.timestamp()).map(Some),
_ => Ok(None),
}
}
fn len(&self) -> Option<usize> { Some(1) }
}
impl Deserialize for Expiring {
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
static FIELDS: &'static [&'static str] = &["expires"];
deserializer.deserialize_struct("Expiring", FIELDS, DeVisitor)
}
}
struct DeVisitor;
impl de::Visitor for DeVisitor {
type Value = Expiring;
fn visit_map<V: de::MapVisitor>(&mut self, mut visitor: V) -> Result<Expiring, V::Error> {
let mut expires = None;
loop {
match try!(visitor.visit_key()) {
Some(Field::Expires) => expires = Some(try!(visitor.visit_value())),
None => break,
}
}
let expires = match expires {
Some(i) => UTC.timestamp(i, 0),
None => return visitor.missing_field("expires"),
};
try!(visitor.end());
Ok(Expiring {
expires: expires,
})
}
}
enum Field {
Expires,
}
impl Deserialize for Field {
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer.deserialize(FieldVisitor)
}
}
struct FieldVisitor;
impl de::Visitor for FieldVisitor {
type Value = Field;
fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Field, E> {
match value {
"expires" => Ok(Field::Expires),
_ => Err(de::Error::custom("expected expires")),
}
}
}
}
#[cfg(test)]
mod tests {
use chrono::{UTC, Duration, Timelike};
use rustc_serialize::json::{self, Json};
use chrono::{UTC, Duration};
use client::response::FromResponse;
use super::Expiring;
#[test]
fn from_response() {
let json = Json::from_str(r#"{"expires_in":3600}"#).unwrap();
let json = r#"{"expires_in":3600}"#.parse().unwrap();
let expiring = Expiring::from_response(&json).unwrap();
assert!(expiring.expires > UTC::now());
assert!(expiring.expires <= UTC::now() + Duration::seconds(3600));
}
#[test]
fn encode_decode() {
let expiring = Expiring {
expires: UTC::now().with_nanosecond(0).unwrap(),
};
let json = json::encode(&expiring).unwrap();
let decoded = json::decode(&json).unwrap();
assert_eq!(expiring, decoded);
}
#[cfg(feature = "serde")]
#[test]
fn serialize_deserialize() {
use serde_json;
let original = Expiring {
expires: UTC::now().with_nanosecond(0).unwrap(),
};
let serialized = serde_json::to_value(&original);
let deserialized = serde_json::from_value(serialized).unwrap();
assert_eq!(original, deserialized);
}
}

View File

@ -29,14 +29,12 @@ pub trait Lifetime: FromResponse {
fn expired(&self) -> bool;
}
pub use self::bearer::Bearer;
mod bearer;
pub use self::statik::Static;
mod expiring;
mod refresh;
mod statik;
pub use self::bearer::Bearer;
pub use self::expiring::Expiring;
mod expiring;
pub use self::refresh::Refresh;
mod refresh;
pub use self::statik::Static;

View File

@ -1,12 +1,11 @@
use chrono::{DateTime, UTC, Duration, TimeZone};
use rustc_serialize::json::Json;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use chrono::{DateTime, UTC, Duration};
use serde_json::Value;
use super::Lifetime;
use client::response::{FromResponse, ParseError, JsonHelper};
use client::response::{FromResponse, ParseError};
/// An expiring token which can be refreshed.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Refresh {
refresh_token: String,
expires: DateTime<UTC>,
@ -27,11 +26,15 @@ impl Lifetime for Refresh {
}
impl FromResponse for Refresh {
fn from_response(json: &Json) -> Result<Self, ParseError> {
let obj = try!(JsonHelper(json).as_object());
fn from_response(json: &Value) -> Result<Self, ParseError> {
let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
let refresh_token = try!(obj.get_string("refresh_token"));
let expires_in = try!(obj.get_i64("expires_in"));
let refresh_token = obj.get("refresh_token")
.and_then(Value::as_str)
.ok_or(ParseError::ExpectedFieldType("refresh_token", "string"))?;
let expires_in = obj.get("expires_in")
.and_then(Value::as_i64)
.ok_or(ParseError::ExpectedFieldType("expires_in", "i64"))?;
Ok(Refresh {
refresh_token: refresh_token.into(),
@ -39,14 +42,17 @@ impl FromResponse for Refresh {
})
}
fn from_response_inherit(json: &Json, prev: &Self) -> Result<Self, ParseError> {
let obj = try!(JsonHelper(json).as_object());
fn from_response_inherit(json: &Value, prev: &Self) -> Result<Self, ParseError> {
let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
let refresh_token = try! {
obj.get_string("refresh_token")
.or(Ok(&prev.refresh_token))
};
let expires_in = try!(obj.get_i64("expires_in"));
let refresh_token = obj.get("refresh_token")
.and_then(Value::as_str)
.or(Some(&prev.refresh_token))
.ok_or(ParseError::ExpectedFieldType("refresh_token", "string"))?;
let expires_in = obj.get("expires_in")
.and_then(Value::as_i64)
.ok_or(ParseError::ExpectedFieldType("expires_in", "i64"))?;
Ok(Refresh {
refresh_token: refresh_token.into(),
@ -55,147 +61,16 @@ impl FromResponse for Refresh {
}
}
#[derive(RustcEncodable, RustcDecodable)]
struct Serializable {
refresh_token: String,
expires: i64,
}
impl<'a> From<&'a Refresh> for Serializable {
fn from(refresh: &Refresh) -> Self {
Serializable {
refresh_token: refresh.refresh_token.clone(),
expires: refresh.expires.timestamp(),
}
}
}
impl Into<Refresh> for Serializable {
fn into(self) -> Refresh {
Refresh {
refresh_token: self.refresh_token,
expires: UTC.timestamp(self.expires, 0),
}
}
}
impl Encodable for Refresh {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
Serializable::from(self).encode(s)
}
}
impl Decodable for Refresh {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
Serializable::decode(d).map(Into::into)
}
}
#[cfg(feature = "serde")]
mod serde {
use chrono::{UTC, TimeZone};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::{ser, de};
use super::Refresh;
impl Serialize for Refresh {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.serialize_struct("Refresh", SerVisitor(self, 0))
}
}
struct SerVisitor<'a>(&'a Refresh, u8);
impl<'a> ser::MapVisitor for SerVisitor<'a> {
fn visit<S: Serializer>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error> {
self.1 += 1;
match self.1 {
1 => serializer.serialize_struct_elt("refresh_token", &self.0.refresh_token).map(Some),
2 => serializer.serialize_struct_elt("expires", &self.0.expires.timestamp()).map(Some),
_ => Ok(None),
}
}
fn len(&self) -> Option<usize> { Some(2) }
}
impl Deserialize for Refresh {
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
static FIELDS: &'static [&'static str] = &["refresh_token", "expires"];
deserializer.deserialize_struct("Refresh", FIELDS, DeVisitor)
}
}
struct DeVisitor;
impl de::Visitor for DeVisitor {
type Value = Refresh;
fn visit_map<V: de::MapVisitor>(&mut self, mut visitor: V) -> Result<Refresh, V::Error> {
let mut refresh_token = None;
let mut expires = None;
loop {
match try!(visitor.visit_key()) {
Some(Field::RefreshToken) => refresh_token = Some(try!(visitor.visit_value())),
Some(Field::Expires) => expires = Some(try!(visitor.visit_value())),
None => break,
}
}
let refresh_token = match refresh_token {
Some(s) => s,
None => return visitor.missing_field("refresh_token"),
};
let expires = match expires {
Some(i) => UTC.timestamp(i, 0),
None => return visitor.missing_field("expires"),
};
try!(visitor.end());
Ok(Refresh {
refresh_token: refresh_token,
expires: expires,
})
}
}
enum Field {
RefreshToken,
Expires,
}
impl Deserialize for Field {
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer.deserialize(FieldVisitor)
}
}
struct FieldVisitor;
impl de::Visitor for FieldVisitor {
type Value = Field;
fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Field, E> {
match value {
"refresh_token" => Ok(Field::RefreshToken),
"expires" => Ok(Field::Expires),
_ => Err(de::Error::custom("expected refresh_token or expires")),
}
}
}
}
#[cfg(test)]
mod tests {
use chrono::{UTC, Duration, Timelike};
use rustc_serialize::json::{self, Json};
use chrono::{UTC, Duration};
use client::response::FromResponse;
use super::Refresh;
#[test]
fn from_response() {
let json = Json::from_str(r#"{"refresh_token":"aaaaaaaa","expires_in":3600}"#).unwrap();
let json = r#"{"refresh_token":"aaaaaaaa","expires_in":3600}"#.parse().unwrap();
let refresh = Refresh::from_response(&json).unwrap();
assert_eq!("aaaaaaaa", refresh.refresh_token);
assert!(refresh.expires > UTC::now());
@ -204,7 +79,7 @@ mod tests {
#[test]
fn from_response_inherit() {
let json = Json::from_str(r#"{"expires_in":3600}"#).unwrap();
let json = r#"{"expires_in":3600}"#.parse().unwrap();
let prev = Refresh {
refresh_token: String::from("aaaaaaaa"),
expires: UTC::now(),
@ -214,29 +89,4 @@ mod tests {
assert!(refresh.expires > UTC::now());
assert!(refresh.expires <= UTC::now() + Duration::seconds(3600));
}
#[test]
fn encode_decode() {
let refresh = Refresh {
refresh_token: String::from("foo"),
expires: UTC::now().with_nanosecond(0).unwrap(),
};
let json = json::encode(&refresh).unwrap();
let decoded = json::decode(&json).unwrap();
assert_eq!(refresh, decoded);
}
#[cfg(feature = "serde")]
#[test]
fn serialize_deserialize() {
use serde_json;
let original = Refresh {
refresh_token: String::from("foo"),
expires: UTC::now().with_nanosecond(0).unwrap(),
};
let serialized = serde_json::to_value(&original);
let deserialized = serde_json::from_value(serialized).unwrap();
assert_eq!(original, deserialized);
}
}

View File

@ -1,10 +1,10 @@
use rustc_serialize::json::Json;
use serde_json::Value;
use super::Lifetime;
use client::response::{FromResponse, ParseError, JsonHelper};
use client::response::{FromResponse, ParseError};
/// A static, non-expiring token.
#[derive(Debug, Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Static;
impl Lifetime for Static {
@ -12,66 +12,32 @@ impl Lifetime for Static {
}
impl FromResponse for Static {
fn from_response(json: &Json) -> Result<Self, ParseError> {
let obj = try!(JsonHelper(json).as_object());
if obj.0.contains_key("expires_in") {
fn from_response(json: &Value) -> Result<Self, ParseError> {
let obj = json.as_object().ok_or(ParseError::ExpectedType("object"))?;
if obj.contains_key("expires_in") {
return Err(ParseError::UnexpectedField("expires_in"));
}
Ok(Static)
}
}
#[cfg(feature = "serde")]
mod serde {
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::impls::UnitVisitor;
use super::Static;
impl Serialize for Static {
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
serializer.serialize_unit_struct("Static")
}
}
impl Deserialize for Static {
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
deserializer.deserialize_unit_struct("Static", UnitVisitor)
.and(Ok(Static))
}
}
}
#[cfg(test)]
mod tests {
use rustc_serialize::json::Json;
use client::response::{FromResponse, ParseError};
use super::Static;
#[test]
fn from_response() {
let json = Json::from_str("{}").unwrap();
let json = "{}".parse().unwrap();
assert_eq!(Static, Static::from_response(&json).unwrap());
}
#[test]
fn from_response_with_expires_in() {
let json = Json::from_str(r#"{"expires_in":3600}"#).unwrap();
let json = r#"{"expires_in":3600}"#.parse().unwrap();
assert_eq!(
ParseError::UnexpectedField("expires_in"),
Static::from_response(&json).unwrap_err()
);
}
#[cfg(feature = "serde")]
#[test]
fn serialize_deserialize() {
use serde_json;
let original = Static;
let serialized = serde_json::to_value(&original);
let deserialized = serde_json::from_value(serialized).unwrap();
assert_eq!(original, deserialized);
}
}