summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHendrik Sollich <hoodie@users.noreply.github.com>2019-03-08 14:20:05 +0100
committerGitHub <noreply@github.com>2019-03-08 14:20:05 +0100
commitc024d5000ebe375836c8c73cf5e05b2ed9df6b10 (patch)
tree5ac9372007163d8f4a6def66ce031bfbaa129bae
parent95df4e92cccc4c7cc52a03cf0ef81824251b9c05 (diff)
parent3387ec4bd0cab2b6d888b663fbd88c5cdc00ba95 (diff)
Merge pull request #8 from strohel/feature/deterministic-to-string
Make Component serialization deterministic, add integration tests
-rw-r--r--src/components.rs21
-rw-r--r--tests/calendar.rs41
2 files changed, 53 insertions, 9 deletions
diff --git a/src/components.rs b/src/components.rs
index 7139e4b..b4efb91 100644
--- a/src/components.rs
+++ b/src/components.rs
@@ -4,7 +4,7 @@ use uuid::Uuid;
// use std::io;
use std::fmt;
use std::mem;
-use std::collections::HashMap;
+use std::collections::BTreeMap;
use crate::properties::*;
@@ -18,7 +18,7 @@ pub struct Todo { inner: InnerComponent }
#[derive(Debug, Default)]
struct InnerComponent{
- properties: HashMap<String,Property>,
+ properties: BTreeMap<String, Property>,
multi_properties: Vec<Property>
}
@@ -27,7 +27,7 @@ impl InnerComponent {
/// copies over everything
pub fn done(&mut self) -> Self {
InnerComponent{
- properties: mem::replace(&mut self.properties, HashMap::new()),
+ properties: mem::replace(&mut self.properties, BTreeMap::new()),
multi_properties: mem::replace(&mut self.multi_properties, Vec::new()),
}
}
@@ -116,8 +116,8 @@ pub trait Component {
/// These are used in the `BEGIN` and `END` line of the component.
fn component_kind() -> &'static str;
- /// Allows access to the inner properties HashMap.
- fn properties(&self) -> &HashMap<String,Property>;
+ /// Allows access to the inner properties map.
+ fn properties(&self) -> &BTreeMap<String,Property>;
/// Read-only access to `multi_properties`
fn multi_properties(&self) -> &Vec<Property> ;
@@ -127,14 +127,17 @@ pub trait Component {
fn fmt_write<W: fmt::Write>(&self, out: &mut W) -> Result<(), fmt::Error> {
write_crlf!(out, "BEGIN:{}", Self::component_kind())?;
- let now = Local::now().format("%Y%m%dT%H%M%S");
- write_crlf!(out, "DTSTAMP:{}", now)?;
+
+ if !self.properties().contains_key("DTSTAMP") {
+ let now = Local::now().format("%Y%m%dT%H%M%S");
+ write_crlf!(out, "DTSTAMP:{}", now)?;
+ }
for property in self.properties().values() {
property.fmt_write(out)?;
}
- if !self.properties().keys().any(|key| key == "UID") {
+ if !self.properties().contains_key("UID") {
write_crlf!(out, "UID:{}", Uuid::new_v4())?;
}
@@ -287,7 +290,7 @@ macro_rules! component_impl {
fn component_kind() -> &'static str { $kind }
/// Read-only access to `properties`
- fn properties(&self) -> &HashMap<String, Property> {
+ fn properties(&self) -> &BTreeMap<String, Property> {
&self.inner.properties
}
diff --git a/tests/calendar.rs b/tests/calendar.rs
new file mode 100644
index 0000000..785bec5
--- /dev/null
+++ b/tests/calendar.rs
@@ -0,0 +1,41 @@
+use chrono::prelude::*;
+use icalendar::{Calendar, Class, Component, Event, EventStatus};
+
+const EXPECTED_CAL_CONTENT: &str = "\
+BEGIN:VCALENDAR\r
+VERSION:2.0\r
+PRODID:ICALENDAR-RS\r
+CALSCALE:GREGORIAN\r
+BEGIN:VEVENT\r
+CLASS:CONFIDENTIAL\r
+DESCRIPTION:Description\r
+DTEND:20140709T091011\r
+DTSTAMP:20190307T181159\r
+DTSTART:20140708T091011\r
+LOCATION:Somewhere\r
+PRIORITY:10\r
+STATUS:TENTATIVE\r
+SUMMARY:summary\r
+UID:euid\r
+END:VEVENT\r
+END:VCALENDAR\r
+";
+
+#[test]
+fn test_calendar_to_string() {
+ let mut calendar = Calendar::new();
+ let event = Event::new()
+ .status(EventStatus::Tentative)
+ .starts(Local.ymd(2014, 7, 8).and_hms(9, 10, 11))
+ .ends(Local.ymd(2014, 7, 9).and_hms(9, 10, 11))
+ .priority(11) // converted to 10
+ .summary("summary")
+ .description("Description")
+ .location("Somewhere")
+ .uid("euid")
+ .class(Class::Confidential)
+ .add_property("DTSTAMP", "20190307T181159")
+ .done();
+ calendar.push(event);
+ assert_eq!(calendar.to_string(), EXPECTED_CAL_CONTENT);
+}