use std::collections::BTreeMap;
use crate::component::parse_component;
use crate::component::Component;
use crate::error::*;
use crate::property::Property;
#[cfg(feature = "timeconversions")]
use chrono::NaiveDate;
#[cfg(feature = "timeconversions")]
use chrono::NaiveDateTime;
#[cfg(feature = "timeconversions")]
use crate::util::DATE_FMT;
#[cfg(feature = "timeconversions")]
use crate::util::DATE_TIME_FMT;
#[derive(Debug)]
pub struct ICalendar(Component);
impl ICalendar {
pub fn build(s: &str) -> VObjectResult<ICalendar> {
let c = parse_component(s)?;
Self::from_component(c).map_err(|_| VObjectError::NotAnICalendar(s.to_owned()))
}
pub fn empty() -> ICalendar {
let c = Component::new("VCALENDAR");
ICalendar(c)
}
pub fn add_event(&mut self, builder: EventBuilder) {
self.0.subcomponents.push(builder.into_component())
}
pub fn add_journal(&mut self, builder: JournalBuilder) {
self.0.subcomponents.push(builder.into_component())
}
pub fn with_event(mut self, builder: EventBuilder) -> Self {
self.0.subcomponents.push(builder.into_component());
self
}
pub fn with_journal(mut self, builder: JournalBuilder) -> Self {
self.0.subcomponents.push(builder.into_component());
self
}
pub fn from_component(c: Component) -> Result<ICalendar, Component> {
if c.name == "VCALENDAR" {
Ok(ICalendar(c))
} else {
Err(c)
}
}
pub fn events(&self) -> EventIterator<'_> {
EventIterator::new(self.0.subcomponents.iter())
}
pub fn journals(&self) -> JournalIterator<'_> {
JournalIterator::new(self.0.subcomponents.iter())
}
make_getter_function_for_optional!(version, "VERSION", Version);
make_getter_function_for_optional!(prodid, "PRODID", Prodid);
}
create_data_type!(Version);
create_data_type!(Prodid);
pub struct EventIterator<'a>(::std::slice::Iter<'a, Component>);
impl<'a> EventIterator<'a> {
fn new(i: ::std::slice::Iter<'a, Component>) -> EventIterator<'a> {
EventIterator(i)
}
}
impl<'a> Iterator for EventIterator<'a> {
type Item = Result<Event<'a>, &'a Component>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(Event::from_component)
}
}
#[derive(Debug, Clone)]
pub struct Event<'a>(&'a Component);
impl<'a> Event<'a> {
fn from_component(c: &'a Component) -> Result<Event<'a>, &'a Component> {
if c.name == "VEVENT" {
Ok(Event(c))
} else {
Err(c)
}
}
make_getter_function_for_optional!(dtend, "DTEND", Dtend);
make_getter_function_for_optional!(dtstart, "DTSTART", Dtstart);
make_getter_function_for_optional!(dtstamp, "DTSTAMP", Dtstamp);
make_getter_function_for_optional!(uid, "UID", Uid);
make_getter_function_for_optional!(description, "DESCRIPTION", Description);
make_getter_function_for_optional!(summary, "SUMMARY", Summary);
make_getter_function_for_optional!(url, "URL", Url);
make_getter_function_for_optional!(location, "LOCATION", Location);
make_getter_function_for_optional!(class, "CLASS", Class);
make_getter_function_for_optional!(categories, "CATEGORIES", Categories);
make_getter_function_for_optional!(transp, "TRANSP", Transp);
make_getter_function_for_optional!(rrule, "RRULE", Rrule);
pub fn build() -> EventBuilder {
EventBuilder(Component::new(String::from("VEVENT")))
}
}
pub struct JournalIterator<'a>(::std::slice::Iter<'a, Component>);
impl<'a> JournalIterator<'a> {
fn new(i: ::std::slice::Iter<'a, Component>) -> JournalIterator<'a> {
JournalIterator(i)
}
}
impl<'a> Iterator for JournalIterator<'a> {
type Item = Result<Journal<'a>, &'a Component>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(Journal::from_component)
}
}
#[derive(Debug, Clone)]
pub struct Journal<'a>(&'a Component);
impl<'a> Journal<'a> {
fn from_component(c: &'a Component) -> Result<Journal<'a>, &'a Component> {
if c.name == "VJOURNAL" {
Ok(Journal(c))
} else {
Err(c)
}
}
make_getter_function_for_optional!(dtstamp, "DTSTAMP", Dtstamp);
make_getter_function_for_optional!(uid, "UID", Uid);
make_getter_function_for_optional!(class, "CLASS", Class);
make_getter_function_for_optional!(created, "CREATED", Created);
make_getter_function_for_optional!(dtstart, "DTSTART", Dtstart);
make_getter_function_for_optional!(last_modified, "LAST-MODIFIED", LastModified);
make_getter_function_for_optional!(organizer, "ORGANIZER", Organizer);
make_getter_function_for_optional!(recurrence_id, "RECURRENCE-ID", RecurrenceId);
make_getter_function_for_optional!(sequence, "SEQUENCE", Sequence);
make_getter_function_for_optional!(status, "STATUS", Status);
make_getter_function_for_optional!(summary, "SUMMARY", Summary);
make_getter_function_for_optional!(url, "URL", Url);
make_getter_function_for_optional!(color, "COLOR", Color);
make_getter_function_for_values!(rrule, "RRULE", Rrule);
make_getter_function_for_values!(attach, "ATTACH", Attach);
make_getter_function_for_values!(attendee, "ATTENDEE", Attendee);
make_getter_function_for_values!(categories, "CATEGORIES", Categories);
make_getter_function_for_values!(comment, "COMMENT", Comment);
make_getter_function_for_values!(contact, "CONTACT", Contact);
make_getter_function_for_values!(description, "DESCRIPTION", Description);
make_getter_function_for_values!(exdate, "EXDATE", Exdate);
make_getter_function_for_values!(related, "RELATED", Related);
make_getter_function_for_values!(rdate, "RDATE", Rdate);
make_getter_function_for_values!(request_status, "REQUEST-STATUS", RequestStatus);
make_getter_function_for_values!(image, "IMAGE", Image);
pub fn build() -> JournalBuilder {
JournalBuilder(Component::new(String::from("VJOURNAL")))
}
}
create_data_type!(Dtend);
create_data_type!(Dtstart);
create_data_type!(Dtstamp);
create_data_type!(Uid);
create_data_type!(Description);
create_data_type!(Summary);
create_data_type!(Url);
create_data_type!(Location);
create_data_type!(Class);
create_data_type!(Categories);
create_data_type!(Transp);
create_data_type!(Rrule);
create_data_type!(Created);
create_data_type!(LastModified);
create_data_type!(Organizer);
create_data_type!(RecurrenceId);
create_data_type!(Sequence);
create_data_type!(Status);
create_data_type!(Color);
create_data_type!(Attach);
create_data_type!(Attendee);
create_data_type!(Comment);
create_data_type!(Contact);
create_data_type!(Exdate);
create_data_type!(Related);
create_data_type!(Rdate);
create_data_type!(RequestStatus);
create_data_type!(Image);
#[cfg(feature = "timeconversions")]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum Time {
Date(NaiveDate),
DateTime(NaiveDateTime),
}
#[cfg(feature = "timeconversions")]
pub trait AsDateTime {
fn as_datetime(&self) -> VObjectResult<Time>;
}
#[cfg(feature = "timeconversions")]
impl AsDateTime for Dtend {
fn as_datetime(&self) -> VObjectResult<Time> {
Ok(
match NaiveDateTime::parse_from_str(&self.0, DATE_TIME_FMT) {
Ok(dt) => Time::DateTime(dt),
Err(_) => NaiveDate::parse_from_str(&self.0, DATE_FMT).map(Time::Date)?,
},
)
}
}
#[cfg(feature = "timeconversions")]
impl AsDateTime for Dtstart {
fn as_datetime(&self) -> VObjectResult<Time> {
Ok(
match NaiveDateTime::parse_from_str(&self.0, DATE_TIME_FMT) {
Ok(dt) => Time::DateTime(dt),
Err(_) => NaiveDate::parse_from_str(&self.0, DATE_FMT).map(Time::Date)?,
},
)
}
}
#[cfg(feature = "timeconversions")]
impl AsDateTime for Dtstamp {
fn as_datetime(&self) -> VObjectResult<Time> {
Ok(
match NaiveDateTime::parse_from_str(&self.0, DATE_TIME_FMT) {
Ok(dt) => Time::DateTime(dt),
Err(_) => NaiveDate::parse_from_str(&self.0, DATE_FMT).map(Time::Date)?,
},
)
}
}
#[cfg(feature = "timeconversions")]
impl AsDateTime for LastModified {
fn as_datetime(&self) -> VObjectResult<Time> {
Ok(
match NaiveDateTime::parse_from_str(&self.0, DATE_TIME_FMT) {
Ok(dt) => Time::DateTime(dt),
Err(_) => NaiveDate::parse_from_str(&self.0, DATE_FMT).map(Time::Date)?,
},
)
}
}
#[derive(Clone, Debug)]
pub struct EventBuilder(Component);
macro_rules! make_setter_function_for {
($fnname:ident, $name:expr, $type:ty, $tostring:expr) => {
#[doc = concat!("Setter for \"", $name, "\" property")]
#[doc = ""]
#[doc = "# Notice"]
#[doc = ""]
#[doc = "Internally, the property is overridden. Old values are dropped silently."]
pub fn $fnname(&mut self, value: $type, params: Option<BTreeMap<String, String>>) {
let property = Property {
name: String::from($name),
params: params.unwrap_or_else(BTreeMap::new),
raw_value: $tostring(value),
prop_group: None,
};
self.0.set(property);
}
};
}
macro_rules! make_function_for {
($fnname:ident, $name:expr, $type:ty, $tostring:expr) => {
#[doc = concat!("Chainable setter for \"", $name, "\" property.")]
#[doc = ""]
#[doc = "# Notice"]
#[doc = ""]
#[doc = "Internally, the property is added, not overridden."]
pub fn $fnname(mut self, value: $type, params: Option<BTreeMap<String, String>>) -> Self {
let property = Property {
name: String::from($name),
params: params.unwrap_or_else(BTreeMap::new),
raw_value: $tostring(value),
prop_group: None,
};
self.0.push(property);
self
}
};
}
impl EventBuilder {
fn into_component(self) -> Component {
self.0
}
make_setter_function_for!(set_dtend, "DTEND", Dtend, Dtend::into_raw);
make_setter_function_for!(set_dtstart, "DTSTART", Dtstart, Dtstart::into_raw);
make_setter_function_for!(set_dtstamp, "DTSTAMP", Dtstamp, Dtstamp::into_raw);
make_setter_function_for!(set_uid, "UID", Uid, Uid::into_raw);
make_setter_function_for!(
set_description,
"DESCRIPTION",
Description,
Description::into_raw
);
make_setter_function_for!(set_summary, "SUMMARY", Summary, Summary::into_raw);
make_setter_function_for!(set_url, "URL", Url, Url::into_raw);
make_setter_function_for!(set_location, "LOCATION", Location, Location::into_raw);
make_setter_function_for!(set_class, "CLASS", Class, Class::into_raw);
make_setter_function_for!(
set_categories,
"CATEGORIES",
Categories,
Categories::into_raw
);
make_setter_function_for!(set_transp, "TRANSP", Transp, Transp::into_raw);
make_setter_function_for!(set_rrule, "RRULE", Rrule, Rrule::into_raw);
make_function_for!(with_dtend, "DTEND", Dtend, Dtend::into_raw);
make_function_for!(with_dtstart, "DTSTART", Dtstart, Dtstart::into_raw);
make_function_for!(with_dtstamp, "DTSTAMP", Dtstamp, Dtstamp::into_raw);
make_function_for!(with_uid, "UID", Uid, Uid::into_raw);
make_function_for!(
with_description,
"DESCRIPTION",
Description,
Description::into_raw
);
make_function_for!(with_summary, "SUMMARY", Summary, Summary::into_raw);
make_function_for!(with_url, "URL", Url, Url::into_raw);
make_function_for!(with_location, "LOCATION", Location, Location::into_raw);
make_function_for!(with_class, "CLASS", Class, Class::into_raw);
make_function_for!(
with_categories,
"CATEGORIES",
Categories,
Categories::into_raw
);
make_function_for!(with_transp, "TRANSP", Transp, Transp::into_raw);
make_function_for!(with_rrule, "RRULE", Rrule, Rrule::into_raw);
}
#[derive(Clone, Debug)]
pub struct JournalBuilder(Component);
impl JournalBuilder {
fn into_component(self) -> Component {
self.0
}
make_setter_function_for!(set_dtstamp, "DTSTAMP", Dtstamp, Dtstamp::into_raw);
make_setter_function_for!(set_uid, "UID", Uid, Uid::into_raw);
make_setter_function_for!(set_class, "CLASS", Class, Class::into_raw);
make_setter_function_for!(set_created, "CREATED", Created, Created::into_raw);
make_setter_function_for!(set_dtstart, "DTSTART", Dtstart, Dtstart::into_raw);
make_setter_function_for!(
set_last_modified,
"LAST-MODIFIED",
LastModified,
LastModified::into_raw
);
make_setter_function_for!(set_organizer, "ORGANIZER", Organizer, Organizer::into_raw);
make_setter_function_for!(
set_recurrence_id,
"RECURRENCE-ID",
RecurrenceId,
RecurrenceId::into_raw
);
make_setter_function_for!(set_sequence, "SEQUENCE", Sequence, Sequence::into_raw);
make_setter_function_for!(set_status, "STATUS", Status, Status::into_raw);
make_setter_function_for!(set_summary, "SUMMARY", Summary, Summary::into_raw);
make_setter_function_for!(set_url, "URL", Url, Url::into_raw);
make_setter_function_for!(set_color, "COLOR", Color, Color::into_raw);
make_setter_function_for!(set_rrule, "RRULE", Rrule, Rrule::into_raw);
make_setter_function_for!(set_attach, "ATTACH", Attach, Attach::into_raw);
make_setter_function_for!(set_attendee, "ATTENDEE", Attendee, Attendee::into_raw);
make_setter_function_for!(
set_categories,
"CATEGORIES",
Categories,
Categories::into_raw
);
make_setter_function_for!(set_comment, "COMMENT", Comment, Comment::into_raw);
make_setter_function_for!(set_contact, "CONTACT", Contact, Contact::into_raw);
make_setter_function_for!(
set_description,
"DESCRIPTION",
Description,
Description::into_raw
);
make_setter_function_for!(set_exdate, "EXDATE", Exdate, Exdate::into_raw);
make_setter_function_for!(set_related, "RELATED", Related, Related::into_raw);
make_setter_function_for!(set_rdate, "RDATE", Rdate, Rdate::into_raw);
make_setter_function_for!(
set_request_status,
"REQUEST-STATUS",
RequestStatus,
RequestStatus::into_raw
);
make_setter_function_for!(set_image, "IMAGE", Image, Image::into_raw);
make_function_for!(with_rrule, "RRULE", Rrule, Rrule::into_raw);
make_function_for!(with_attach, "ATTACH", Attach, Attach::into_raw);
make_function_for!(with_attendee, "ATTENDEE", Attendee, Attendee::into_raw);
make_function_for!(
with_categories,
"CATEGORIES",
Categories,
Categories::into_raw
);
make_function_for!(with_comment, "COMMENT", Comment, Comment::into_raw);
make_function_for!(with_contact, "CONTACT", Contact, Contact::into_raw);
make_function_for!(
with_description,
"DESCRIPTION",
Description,
Description::into_raw
);
make_function_for!(with_exdate, "EXDATE", Exdate, Exdate::into_raw);
make_function_for!(with_related, "RELATED", Related, Related::into_raw);
make_function_for!(with_rdate, "RDATE", Rdate, Rdate::into_raw);
make_function_for!(
with_request_status,
"REQUEST-STATUS",
RequestStatus,
RequestStatus::into_raw
);
make_function_for!(with_image, "IMAGE", Image, Image::into_raw);
}
#[cfg(all(test, feature = "timeconversions"))]
mod tests {
use super::ICalendar;
use crate::util::*;
use chrono::NaiveDate;
use chrono::NaiveDateTime;
use super::*;
const TEST_ENTRY: &str = "BEGIN:VCALENDAR\n\
VERSION:2.0\n\
PRODID:http://www.example.com/calendarapplication/\n\
METHOD:PUBLISH\n\
BEGIN:VEVENT\n\
UID:461092315540@example.com\n\
ORGANIZER;CN=\"Alice Balder, Example Inc.\":MAILTO:alice@example.com\n\
LOCATION:Somewhere\n\
SUMMARY:Eine Kurzinfo\n\
DESCRIPTION:Beschreibung des Termines\n\
CLASS:PUBLIC\n\
DTSTART:20060910T220000Z\n\
DTEND:20060919T215900Z\n\
DTSTAMP:20060812T125900Z\n\
END:VEVENT\n\
END:VCALENDAR\n";
const TEST_JOURNAL: &str = "BEGIN:VCALENDAR\n\
VERSION:2.0\n\
PRODID:http://www.example.com/calendarapplication/\n\
METHOD:PUBLISH\n\
BEGIN:VJOURNAL\n\
DTSTAMP:20060812T125900Z\n\
UID:461092315640@example.com\n\
SUMMARY:Journal entry\n\
DESCRIPTION:A long journal entry \n \
which wraps lines.\n\
STATUS:IN_PROGRESS\n\
CATEGORIES:test\n\
END:VJOURNAL\n\
END:VCALENDAR\n";
const TEST_ENTRY_OC: &str = "BEGIN:VCALENDAR\n\
VERSION:2.0\n\
PRODID:ownCloud Calendar\n\
CALSCALE:GREGORIAN\n\
BEGIN:VEVENT\n\
UID:ff411055a5\n\
DTSTAMP:20160128T223013Z\n\
CREATED:20160128T223013Z\n\
LAST-MODIFIED:20160128T223013Z\n\
SUMMARY:Amon Amarth - Jomsviking\n\
DTSTART;VALUE=DATE:20160325\n\
DTEND;VALUE=DATE:20160326\n\
LOCATION:\n\
DESCRIPTION:\n\
CATEGORIES:\n\
END:VEVENT\n\
END:VCALENDAR\n\
";
#[test]
fn test_parse() {
let cal = ICalendar::build(TEST_ENTRY);
assert!(cal.is_ok(), "Not okay: {:?}\n in '{}'", cal, TEST_ENTRY);
}
#[test]
fn test_journal_parse() {
let cal = ICalendar::build(TEST_JOURNAL);
assert!(cal.is_ok(), "Not okay: {:?}\n in '{}'", cal, TEST_JOURNAL);
}
#[test]
fn test_iter() {
let ical = ICalendar::build(TEST_ENTRY).unwrap();
assert_eq!(ical.events().filter(|r| r.is_ok()).count(), 1);
assert_eq!(ical.journals().filter(|r| r.is_ok()).count(), 0);
}
#[test]
fn test_journal_iter() {
let ical = ICalendar::build(TEST_JOURNAL).unwrap();
assert_eq!(ical.journals().filter(|r| r.is_ok()).count(), 1);
assert_eq!(ical.events().filter(|r| r.is_ok()).count(), 0);
}
#[test]
fn test_icalendar_attributes() {
let ical = ICalendar::build(TEST_ENTRY).unwrap();
assert_eq!(ical.version().unwrap().raw(), "2.0");
assert_eq!(
ical.prodid().unwrap().raw(),
"http://www.example.com/calendarapplication/"
);
}
#[test]
fn test_event_attributes() {
let ical = ICalendar::build(TEST_ENTRY).unwrap();
let ev = ical.events().next().unwrap().unwrap();
assert_eq!(
ev.dtend().map(|e| e.raw().clone()),
Some("20060919T215900Z".to_owned())
);
assert_eq!(
ev.dtstart().map(|e| e.raw().clone()),
Some("20060910T220000Z".to_owned())
);
assert_eq!(
ev.dtstamp().map(|e| e.raw().clone()),
Some("20060812T125900Z".to_owned())
);
assert_eq!(
ev.uid().map(|e| e.raw().clone()),
Some("461092315540@example.com".to_owned())
);
assert_eq!(
ev.description().map(|e| e.raw().clone()),
Some("Beschreibung des Termines".to_owned())
);
assert_eq!(
ev.summary().map(|e| e.raw().clone()),
Some("Eine Kurzinfo".to_owned())
);
assert_eq!(ev.url(), None);
assert_eq!(
ev.location().map(|e| e.raw().clone()),
Some("Somewhere".to_owned())
);
assert_eq!(
ev.class().map(|e| e.raw().clone()),
Some("PUBLIC".to_owned())
);
assert_eq!(ev.categories(), None);
assert_eq!(ev.transp(), None);
assert_eq!(ev.rrule(), None);
}
#[test]
fn test_journal_attributes() {
let ical = ICalendar::build(TEST_JOURNAL).unwrap();
let j = ical.journals().next().unwrap().unwrap();
assert_eq!(
j.dtstamp().map(|j| j.raw().clone()),
Some("20060812T125900Z".to_owned())
);
assert_eq!(
j.uid().map(|j| j.raw().clone()),
Some("461092315640@example.com".to_owned())
);
assert_eq!(
j.description()
.into_iter()
.map(|j| j.raw().clone())
.collect::<Vec<_>>(),
vec!["A long journal entry which wraps lines.".to_owned()]
);
assert_eq!(
j.summary().map(|j| j.raw().clone()),
Some("Journal entry".to_owned())
);
assert_eq!(j.url(), None);
assert_eq!(j.class().map(|j| j.raw().clone()), None,);
assert_eq!(
j.status().map(|j| j.raw().clone()),
Some("IN_PROGRESS".to_owned())
);
assert_eq!(
j.categories()
.into_iter()
.map(|j| j.raw().clone())
.collect::<Vec<_>>(),
vec!["test".to_owned()]
);
assert_eq!(j.rrule(), vec![]);
}
#[test]
fn test_event_attributes_oc() {
let ical = ICalendar::build(TEST_ENTRY_OC).unwrap();
assert_eq!(ical.version().unwrap().raw(), "2.0");
assert_eq!(ical.prodid().unwrap().raw(), "ownCloud Calendar");
let ev = ical.events().next().unwrap().unwrap();
assert_eq!(
ev.dtend().map(|e| e.raw().clone()),
Some("20160326".to_owned())
);
assert_eq!(
ev.dtstart().map(|e| e.raw().clone()),
Some("20160325".to_owned())
);
assert_eq!(
ev.dtstamp().map(|e| e.raw().clone()),
Some("20160128T223013Z".to_owned())
);
assert_eq!(
ev.uid().map(|e| e.raw().clone()),
Some("ff411055a5".to_owned())
);
assert_eq!(
ev.description().map(|e| e.raw().clone()),
Some("".to_owned())
);
assert_eq!(
ev.summary().map(|e| e.raw().clone()),
Some("Amon Amarth - Jomsviking".to_owned())
);
assert_eq!(ev.url(), None);
assert_eq!(ev.location().map(|e| e.raw().clone()), Some("".to_owned()));
assert_eq!(ev.class().map(|e| e.raw().clone()), None);
assert_eq!(
ev.categories().map(|e| e.raw().clone()),
Some("".to_owned())
);
assert_eq!(ev.transp(), None);
assert_eq!(ev.rrule(), None);
}
#[cfg(feature = "timeconversions")]
#[test]
fn test_event_attributes_with_conversions() {
let ical = ICalendar::build(TEST_ENTRY).unwrap();
let ev = ical.events().next().unwrap().unwrap();
assert_eq!(
ev.dtend().map(|e| e.as_datetime().unwrap()).unwrap(),
Time::DateTime(
NaiveDateTime::parse_from_str("20060919T215900Z", DATE_TIME_FMT).unwrap()
)
);
assert_eq!(
ev.dtstart().map(|e| e.as_datetime().unwrap()).unwrap(),
Time::DateTime(
NaiveDateTime::parse_from_str("20060910T220000Z", DATE_TIME_FMT).unwrap()
)
);
assert_eq!(
ev.dtstamp().map(|e| e.as_datetime().unwrap()).unwrap(),
Time::DateTime(
NaiveDateTime::parse_from_str("20060812T125900Z", DATE_TIME_FMT).unwrap()
)
);
}
#[cfg(feature = "timeconversions")]
#[test]
fn test_event_attributes_oc_with_conversions() {
let ical = ICalendar::build(TEST_ENTRY_OC).unwrap();
assert_eq!(ical.version().unwrap().raw(), "2.0");
assert_eq!(ical.prodid().unwrap().raw(), "ownCloud Calendar");
let ev = ical.events().next().unwrap().unwrap();
assert_eq!(
ev.dtend().map(|e| e.as_datetime().unwrap()).unwrap(),
Time::Date(NaiveDate::parse_from_str("20160326", DATE_FMT).unwrap())
);
assert_eq!(
ev.dtstart().map(|e| e.as_datetime().unwrap()).unwrap(),
Time::Date(NaiveDate::parse_from_str("20160325", DATE_FMT).unwrap())
);
assert_eq!(
ev.dtstamp().map(|e| e.as_datetime().unwrap()).unwrap(),
Time::DateTime(
NaiveDateTime::parse_from_str("20160128T223013Z", DATE_TIME_FMT).unwrap()
)
);
}
#[test]
fn test_build_event() {
let mut ical = ICalendar::empty();
let mut builder = Event::build();
let desc = Description::new(String::from("test"), BTreeMap::new());
builder.set_description(desc, None);
let uid = Uid::new(String::from("testuid"), BTreeMap::new());
builder.set_uid(uid, None);
let summary = Summary::new(String::from("summary"), BTreeMap::new());
builder.set_summary(summary, None);
ical.add_event(builder);
let ev = ical.events().next().unwrap().unwrap();
assert_eq!(
ev.uid().map(|e| e.raw().clone()),
Some("testuid".to_owned())
);
assert_eq!(
ev.description().map(|e| e.raw().clone()),
Some("test".to_owned())
);
assert_eq!(
ev.summary().map(|e| e.raw().clone()),
Some("summary".to_owned())
);
}
#[test]
fn test_build_journal() {
let mut ical = ICalendar::empty();
let mut builder = Journal::build();
let desc = Description::new(String::from("test"), BTreeMap::new());
builder.set_description(desc, None);
let uid = Uid::new(String::from("testuid"), BTreeMap::new());
builder.set_uid(uid, None);
let summary = Summary::new(String::from("summary"), BTreeMap::new());
builder.set_summary(summary, None);
ical.add_journal(builder);
let j = ical.journals().next().unwrap().unwrap();
assert_eq!(j.uid().map(|j| j.raw().clone()), Some("testuid".to_owned()));
assert_eq!(
j.description()
.into_iter()
.map(|j| j.raw().clone())
.collect::<Vec<_>>(),
vec!["test".to_owned()]
);
assert_eq!(
j.summary().map(|j| j.raw().clone()),
Some("summary".to_owned())
);
}
}