diff options
| -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")] |
