diff options
author | Matthias Beyer <mail@beyermatthias.de> | 2016-04-03 21:57:48 +0200 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2016-04-03 21:57:48 +0200 |
commit | 338e62ab4094fb203092119b0da93c65d0e152ae (patch) | |
tree | 16abd5db43bda59727f5f33796c022bc2e411725 | |
parent | da172798101e63b13c51fd1f83a28a8edfeefa05 (diff) | |
parent | a1395c090b41c6714577dcfdd5b1c0a691adcd34 (diff) |
Merge branch 'deserialize'
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/date.rs | 13 | ||||
-rw-r--r-- | src/status.rs | 3 | ||||
-rw-r--r-- | src/task.rs | 317 |
4 files changed, 324 insertions, 11 deletions
@@ -8,7 +8,7 @@ chrono = "0.2.20" log = "0.3.5" serde = "0.7.0" serde_json = "0.7.0" -uuid = { version = "0.1.18", features = ["serde"] } +uuid = { version = "0.2.0", features = ["serde"] } [dev-dependencies] env_logger = "0.3.2" diff --git a/src/date.rs b/src/date.rs index 970e89c..ec3bef5 100644 --- a/src/date.rs +++ b/src/date.rs @@ -8,9 +8,17 @@ use serde::de::Visitor; use serde::de::Error as SerdeError; use chrono::naive::datetime::NaiveDateTime; -#[derive(Clone, Debug, Hash)] +#[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Date(NaiveDateTime); +impl From<NaiveDateTime> for Date { + + fn from(ndt: NaiveDateTime) -> Date { + Date(ndt) + } + +} + pub static TASKWARRIOR_DATETIME_TEMPLATE : &'static str = "%Y%m%dT%H%M%SZ"; impl Serialize for Date { @@ -18,7 +26,8 @@ impl Serialize for Date { fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer { - serializer.serialize_str(&format!("{}", self.0)) + let formatted = self.0.format(TASKWARRIOR_DATETIME_TEMPLATE); + serializer.serialize_str(&format!("{}", formatted)) } } diff --git a/src/status.rs b/src/status.rs index 258c923..8333c66 100644 --- a/src/status.rs +++ b/src/status.rs @@ -5,9 +5,6 @@ use serde::Deserializer; use serde::de::Error; use serde::de::Visitor; -use std::convert::Into; -use std::convert::From; - #[derive(Clone, Debug, PartialEq, Eq)] pub enum TaskStatus { Pending, diff --git a/src/task.rs b/src/task.rs index 14541c6..9c8c5b2 100644 --- a/src/task.rs +++ b/src/task.rs @@ -1,19 +1,16 @@ -use std::collections::BTreeMap; use std::result::Result as RResult; -use serde_json::value::Value; use serde::Serialize; use serde::Serializer; use serde::Deserialize; use serde::Deserializer; +use serde::de::Visitor; use serde::de::Error as SerdeError; use serde::ser::MapVisitor; +use serde::de::MapVisitor as DeserializeMapVisitor; use uuid::Uuid; -use error::TaskError; -use error::TaskErrorKind; use priority::TaskPriority; -use result::Result; use status::TaskStatus; use project::Project; use tag::Tag; @@ -292,3 +289,313 @@ impl<'a> MapVisitor for TaskVisitor<'a> { } } + +impl Deserialize for Task { + + fn deserialize<D>(deserializer: &mut D) -> RResult<Task, D::Error> + where D: Deserializer + { + static FIELDS: &'static [&'static str] = &[ + "status", + "uuid", + "entry", + "description", + + "annotation", + "depends", + "due", + "end", + "imask", + "mask", + "modified", + "parent", + "priority", + "project", + "recur", + "scheduled", + "start", + "tags", + "until", + "wait" + ]; + deserializer.deserialize_struct("Task", FIELDS, TaskDeserializeVisitor) + } + +} + +struct TaskDeserializeVisitor; + +impl Visitor for TaskDeserializeVisitor { + type Value = Task; + + fn visit_map<V>(&mut self, mut visitor: V) -> RResult<Task, V::Error> + where V: DeserializeMapVisitor + { + let mut status = None; + let mut uuid = None; + let mut entry = None; + let mut description = None; + + let mut annotation = None; + let mut depends = None; + let mut due = None; + let mut end = None; + let mut imask = None; + let mut mask = None; + let mut modified = None; + let mut parent = None; + let mut priority = None; + let mut project = None; + let mut recur = None; + let mut scheduled = None; + let mut start = None; + let mut tags = None; + let mut until = None; + let mut wait = None; + + loop { + let key : Option<String> = try!(visitor.visit_key()); + if key.is_none() { + break; + } + let key = key.unwrap(); + + match &key[..] { + "status" => { + status = Some(try!(visitor.visit_value())); + }, + "uuid" => { + uuid = Some(try!(visitor.visit_value())); + }, + "entry" => { + entry = Some(try!(visitor.visit_value())); + }, + "description" => { + description = Some(try!(visitor.visit_value())); + }, + + "annotation" => { + annotation = Some(try!(visitor.visit_value())); + }, + "depends" => { + depends = Some(try!(visitor.visit_value())); + }, + "due" => { + due = Some(try!(visitor.visit_value())); + }, + "end" => { + end = Some(try!(visitor.visit_value())); + }, + "imask" => { + imask = Some(try!(visitor.visit_value())); + }, + "mask" => { + mask = Some(try!(visitor.visit_value())); + }, + "modified" => { + modified = Some(try!(visitor.visit_value())); + }, + "parent" => { + parent = Some(try!(visitor.visit_value())); + }, + "priority" => { + priority = Some(try!(visitor.visit_value())); + }, + "project" => { + project = Some(try!(visitor.visit_value())); + }, + "recur" => { + recur = Some(try!(visitor.visit_value())); + }, + "scheduled" => { + scheduled = Some(try!(visitor.visit_value())); + }, + "start" => { + start = Some(try!(visitor.visit_value())); + }, + "tags" => { + tags = Some(try!(visitor.visit_value())); + }, + "until" => { + until = Some(try!(visitor.visit_value())); + }, + "wait" => { + wait = Some(try!(visitor.visit_value())); + }, + + field => { + use serde::de::impls::IgnoredAny; + + debug!("field '{}' ignored", field); + let _: IgnoredAny = try!(visitor.visit_value()); + } + } + } + + let status = match status { + Some(status) => status, + None => try!(visitor.missing_field("status")), + }; + + let uuid = match uuid { + Some(uuid) => uuid, + None => try!(visitor.missing_field("uuid")), + }; + + let entry = match entry { + Some(entry) => entry, + None => try!(visitor.missing_field("entry")), + }; + + let description = match description { + Some(description) => description, + None => try!(visitor.missing_field("description")), + }; + + try!(visitor.end()); + + let task = Task::new( + status, + uuid, + entry, + description, + + annotation, + depends, + due, + end, + imask, + mask, + modified, + parent, + priority, + project, + recur, + scheduled, + start, + tags, + until, + wait + ); + + Ok(task) + } +} + +#[cfg(test)] +mod test { + use date::Date; + use date::TASKWARRIOR_DATETIME_TEMPLATE; + use status::TaskStatus; + use task::Task; + + use uuid::Uuid; + use chrono::naive::datetime::NaiveDateTime; + use serde_json; + + fn mkdate(s: &str) -> Date { + let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE); + Date::from(n.unwrap()) + } + + #[test] + fn test_deser() { + let s = +r#"{ +"id": 1, +"description": "test", +"entry": "20150619T165438Z", +"status": "waiting", +"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0" +}"#; + + println!("{}", s); + + let task = serde_json::from_str(s); + println!("{:?}", task); + assert!(task.is_ok()); + let task : Task = task.unwrap(); + + assert!(task.status().clone() == TaskStatus::Waiting); + assert!(task.description() == "test"); + assert!(task.entry().clone() == mkdate("20150619T165438Z")); + assert!(task.uuid().clone() == Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap()); + + let back = serde_json::to_string(&task).unwrap(); + + assert!(back.contains("description")); + assert!(back.contains("test")); + assert!(back.contains("entry")); + assert!(back.contains("20150619T165438Z")); + assert!(back.contains("status")); + assert!(back.contains("waiting")); + assert!(back.contains("uuid")); + assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0")); + } + + #[test] + fn test_deser_more() { + let s = +r#"{ +"id": 1, +"description": "some description", +"entry": "20150619T165438Z", +"modified": "20160327T164007Z", +"project": "someproject", +"status": "waiting", +"tags": ["some", "tags", "are", "here"], +"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0", +"wait": "20160508T164007Z", +"urgency": 0.583562 +}"#; + + println!("{}", s); + + let task = serde_json::from_str(s); + println!("{:?}", task); + assert!(task.is_ok()); + let task : Task = task.unwrap(); + + assert!(task.status().clone() == TaskStatus::Waiting); + assert!(task.description() == "some description"); + assert!(task.entry().clone() == mkdate("20150619T165438Z")); + assert!(task.uuid().clone() == Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap()); + + assert!(task.modified() == Some(&mkdate("20160327T164007Z"))); + assert!(task.project() == Some(&String::from("someproject"))); + + if let Some(tags) = task.tags() { + for tag in tags { + let any_tag = [ "some", "tags", "are", "here", ] + .into_iter().any(|t| tag == *t); + assert!(any_tag, "Tag {} missing", tag); + } + } else { + assert!(false, "Tags completely missing"); + } + + assert!(task.wait() == Some(&mkdate("20160508T164007Z"))); + // assert!(task.urgency().clone() == 0.583562); + + let back = serde_json::to_string(&task).unwrap(); + + assert!(back.contains("description")); + assert!(back.contains("some description")); + assert!(back.contains("entry")); + assert!(back.contains("20150619T165438Z")); + assert!(back.contains("project")); + assert!(back.contains("someproject")); + assert!(back.contains("status")); + assert!(back.contains("waiting")); + assert!(back.contains("tags")); + assert!(back.contains("some")); + assert!(back.contains("tags")); + assert!(back.contains("are")); + assert!(back.contains("here")); + assert!(back.contains("uuid")); + assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0")); + } + + +} + |