diff --git a/src/token/expiring.rs b/src/token/expiring.rs index 0ad8a1c..b56caad 100644 --- a/src/token/expiring.rs +++ b/src/token/expiring.rs @@ -1,6 +1,8 @@ use chrono::{DateTime, UTC, Duration, TimeZone}; use rustc_serialize::json::Json; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; +use serde::{ser, de}; use super::Lifetime; use client::response::{FromResponse, ParseError, JsonHelper}; @@ -91,6 +93,91 @@ impl Decodable for Expiring { } } +impl Serialize for Expiring { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> { + serializer.visit_struct("Expiring", SerVisitor(self, 0)) + } +} + +struct SerVisitor<'a>(&'a Expiring, u8); +impl<'a> ser::MapVisitor for SerVisitor<'a> { + fn visit(&mut self, serializer: &mut S) -> Result, S::Error> { + self.1 += 1; + match self.1 { + 1 => serializer.visit_struct_elt("refresh_token", &self.0.refresh_token).map(Some), + 2 => serializer.visit_struct_elt("expires", &self.0.expires.timestamp()).map(Some), + _ => Ok(None), + } + } + + fn len(&self) -> Option { Some(2) } +} + +impl Deserialize for Expiring { + fn deserialize(deserializer: &mut D) -> Result { + static FIELDS: &'static [&'static str] = &["refresh_token", "expires"]; + deserializer.visit_struct("Expiring", FIELDS, DeVisitor) + } +} + +struct DeVisitor; +impl de::Visitor for DeVisitor { + type Value = Expiring; + + fn visit_map(&mut self, mut visitor: V) -> Result { + 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(Expiring { + refresh_token: refresh_token, + expires: expires, + }) + } +} + +enum Field { + RefreshToken, + Expires, +} + +impl Deserialize for Field { + fn deserialize(deserializer: &mut D) -> Result { + deserializer.visit(FieldVisitor) + } +} + +struct FieldVisitor; +impl de::Visitor for FieldVisitor { + type Value = Field; + + fn visit_str(&mut self, value: &str) -> Result { + match value { + "refresh_token" => Ok(Field::RefreshToken), + "expires" => Ok(Field::Expires), + _ => Err(de::Error::syntax("expected refresh_token or expires")), + } + } +} + #[cfg(test)] mod tests { use chrono::{UTC, Duration, Timelike};