aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Barnard2017-05-03 13:20:02 +0100
committerEdward Barnard2017-05-03 13:20:02 +0100
commit29248dcf3e8e1798b09967583fffed95a8856380 (patch)
tree09bcdf8a9154e1457f83ace3b3c24703bb40b7e8
parent9ac3cd9e720ed9e412361c51c5611f24b1518402 (diff)
downloadrust-plist-29248dcf3e8e1798b09967583fffed95a8856380.tar.bz2
Implement serialisation and deserialisation for the plist Date type.v0.2.0
-rw-r--r--src/date.rs59
-rw-r--r--src/serde/ser.rs22
-rw-r--r--tests/serde_tests/mod.rs32
-rw-r--r--tests/tests.rs1
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")]