diff --git a/src/token/bearer.rs b/src/token/bearer.rs index 781162f..f65a498 100644 --- a/src/token/bearer.rs +++ b/src/token/bearer.rs @@ -1,5 +1,9 @@ +use std::marker::PhantomData; + use hyper::header; use rustc_serialize::json::Json; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; +use serde::{ser, de}; use super::{Token, Lifetime}; use client::response::{FromResponse, ParseError, JsonHelper}; @@ -58,6 +62,97 @@ impl FromResponse for Bearer { } } +impl Serialize for Bearer { + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> { + serializer.visit_struct("Bearer", SerVisitor(self, 0)) + } +} + +struct SerVisitor<'a, L: Lifetime + Serialize + 'a>(&'a Bearer, u8); +impl<'a, L: Lifetime + Serialize + 'a> ser::MapVisitor for SerVisitor<'a, L> { + fn visit(&mut self, serializer: &mut S) -> Result, S::Error> { + self.1 += 1; + match self.1 { + 1 => serializer.visit_struct_elt("access_token", &self.0.access_token).map(Some), + 2 => serializer.visit_struct_elt("scope", &self.0.scope).map(Some), + 3 => serializer.visit_struct_elt("lifetime", &self.0.lifetime).map(Some), + _ => Ok(None), + } + } + + fn len(&self) -> Option { Some(3) } +} + +impl Deserialize for Bearer { + fn deserialize(deserializer: &mut D) -> Result { + static FIELDS: &'static [&'static str] = &["access_token", "scope", "lifetime"]; + deserializer.visit_struct("Bearer", FIELDS, DeVisitor(PhantomData)) + } +} + +struct DeVisitor(PhantomData); +impl de::Visitor for DeVisitor { + type Value = Bearer; + + fn visit_map(&mut self, mut visitor: V) -> Result, 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(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 { + "access_token" => Ok(Field::AccessToken), + "scope" => Ok(Field::Scope), + "lifetime" => Ok(Field::Lifetime), + _ => Err(de::Error::syntax("expected access_token, scope or lifetime")), + } + } +} + #[cfg(test)] mod tests { use chrono::{UTC, Duration};