diff options
| author | Edward Barnard | 2017-05-03 13:20:02 +0100 | 
|---|---|---|
| committer | Edward Barnard | 2017-05-03 13:20:02 +0100 | 
| commit | 29248dcf3e8e1798b09967583fffed95a8856380 (patch) | |
| tree | 09bcdf8a9154e1457f83ace3b3c24703bb40b7e8 | |
| parent | 9ac3cd9e720ed9e412361c51c5611f24b1518402 (diff) | |
| download | rust-plist-29248dcf3e8e1798b09967583fffed95a8856380.tar.bz2 | |
Implement serialisation and deserialisation for the plist Date type.v0.2.0
| -rw-r--r-- | src/date.rs | 59 | ||||
| -rw-r--r-- | src/serde/ser.rs | 22 | ||||
| -rw-r--r-- | tests/serde_tests/mod.rs | 32 | ||||
| -rw-r--r-- | tests/tests.rs | 1 | 
4 files changed, 107 insertions, 7 deletions
| diff --git a/src/date.rs b/src/date.rs index 2bcf9da..1aa0e72 100644 --- a/src/date.rs +++ b/src/date.rs @@ -65,3 +65,62 @@ impl FromStr for Date {          })      }  } + +#[cfg(feature = "serde")] +mod serde_impls { +    use serde_base::de::{Deserialize, Deserializer, Error, Visitor, Unexpected}; +    use serde_base::ser::{Serialize, Serializer}; +    use std::fmt; +    use std::str::FromStr; + +    use Date; + +    impl Serialize for Date { +        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> +            where S: Serializer, +        { +            let date_str = self.to_string(); +            serializer.serialize_newtype_struct("PLIST-DATE", &date_str) +        } +    } + +    struct DateNewtypeVisitor; + +    impl<'de> Visitor<'de> for DateNewtypeVisitor { +        type Value = Date; + +        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +            formatter.write_str("a plist date newtype") +        } + +        fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error> +            where D: Deserializer<'de>, +        { +            deserializer.deserialize_str(DateStrVisitor) +        } +    } + +    struct DateStrVisitor; + +    impl<'de> Visitor<'de> for DateStrVisitor { +        type Value = Date; + +        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +            formatter.write_str("a plist date string") +        } + +        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> +            where E: Error, +        { +            Date::from_str(v).map_err(|_| E::invalid_value(Unexpected::Str(v), &self)) +        } +    } + +    impl<'de> Deserialize<'de> for Date { +        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> +            where D: Deserializer<'de>, +        { +            deserializer.deserialize_newtype_struct("PLIST-DATE", DateNewtypeVisitor) +        } +    } +} diff --git a/src/serde/ser.rs b/src/serde/ser.rs index b5eca6a..a77dc23 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -3,8 +3,9 @@  use serde_base::ser;  use std::fmt::Display; +use std::str::FromStr; -use {Error, EventWriter, PlistEvent}; +use {Date, Error, EventWriter, PlistEvent};  impl ser::Error for Error {      fn custom<T: Display>(msg: T) -> Self { @@ -17,14 +18,16 @@ pub struct Serializer<W: EventWriter> {      // We don't want to serialize None if the Option is in a struct field as this is how null      // fields are represented in plists. This is fragile but results in minimal code duplication.      // TODO: This is fragile. Use distinct types instead. -    maybe_option_field_name: Option<&'static str> +    maybe_option_field_name: Option<&'static str>, +    expecting_date: bool  }  impl<W: EventWriter> Serializer<W> {      pub fn new(writer: W) -> Serializer<W> {          Serializer {              writer: writer, -            maybe_option_field_name: None +            maybe_option_field_name: None, +            expecting_date: false          }      } @@ -34,6 +37,9 @@ impl<W: EventWriter> Serializer<W> {          if let Some(field_name) = self.maybe_option_field_name.take() {              self.emit(PlistEvent::StringValue(field_name.to_owned()))?;          } +        if self.expecting_date { +            panic!("Expecting date"); +        }          Ok(self.writer.write(&event)?)      } @@ -115,7 +121,12 @@ impl<'a, W: EventWriter> ser::Serializer for &'a mut Serializer<W> {      }      fn serialize_str(self, value: &str) -> Result<(), Self::Error> { -        self.emit(PlistEvent::StringValue(value.to_owned())) +        if self.expecting_date { +            self.expecting_date = false; +            self.emit(PlistEvent::DateValue(Date::from_str(value).expect("Invalid date format"))) +        } else { +            self.emit(PlistEvent::StringValue(value.to_owned())) +        }      }      fn serialize_bytes(self, value: &[u8]) -> Result<(), Self::Error> { @@ -171,6 +182,9 @@ impl<'a, W: EventWriter> ser::Serializer for &'a mut Serializer<W> {                                                              _name: &'static str,                                                              value: &T)                                                              -> Result<(), Self::Error> { +        if _name == "PLIST-DATE" { +            self.expecting_date = true; +        }          value.serialize(self)      } diff --git a/tests/serde_tests/mod.rs b/tests/serde_tests/mod.rs index e490a2c..ad5140a 100644 --- a/tests/serde_tests/mod.rs +++ b/tests/serde_tests/mod.rs @@ -1,4 +1,5 @@ -use plist::{EventWriter, PlistEvent, Result as PlistResult}; +use chrono::{TimeZone, UTC}; +use plist::{Date, EventWriter, PlistEvent, Result as PlistResult};  use plist::serde::{Serializer, Deserializer};  use plist::PlistEvent::*;  use serde::de::DeserializeOwned; @@ -219,7 +220,7 @@ fn type_with_options() {          c: None      }; -    let ty = TypeWithOptions { +    let obj = TypeWithOptions {          a: Some("hello".to_owned()),          b: None,          c: Some(Box::new(inner)) @@ -235,5 +236,30 @@ fn type_with_options() {                         EndDictionary,                         EndDictionary]; -    assert_roundtrip(ty, Some(comparison)); +    assert_roundtrip(obj, Some(comparison)); +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +struct TypeWithDate { +    a: Option<i32>, +    b: Option<Date> +} + +#[test] +fn type_with_date() { +    let date: Date = UTC.ymd(1981, 05, 16).and_hms(11, 32, 06).into(); + +    let obj = TypeWithDate { +        a: Some(28), +        b: Some(date.clone()), +    }; + +    let comparison = &[StartDictionary(None), +                       StringValue("a".to_owned()), +                       IntegerValue(28), +                       StringValue("b".to_owned()), +                       DateValue(date), +                       EndDictionary]; + +    assert_roundtrip(obj, Some(comparison));  } diff --git a/tests/tests.rs b/tests/tests.rs index 8754b09..41bbfd5 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,3 +1,4 @@ +extern crate chrono;  extern crate plist;  #[cfg(feature = "serde")] | 
