summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNora <nora.widdecke@tu-bs.de>2019-01-05 14:37:15 +0100
committerNora <nora.widdecke@tu-bs.de>2019-01-05 14:37:25 +0100
commit1316149036b931aa951ac1c7ca3ca8c07b27bd1a (patch)
tree2ed8320818e135a6f1b57ba9e48da89d0c5708b1 /src
parentd5397b37b1e49436f6c1e40a13cbc89a23634bfc (diff)
refactor icalwrap.rs into module icalwrap
Diffstat (limited to 'src')
-rw-r--r--src/icalwrap/icalcomponent.rs42
-rw-r--r--src/icalwrap/icalproperty.rs60
-rw-r--r--src/icalwrap/icalvcalendar.rs (renamed from src/icalwrap.rs)444
-rw-r--r--src/icalwrap/icalvevent.rs309
-rw-r--r--src/icalwrap/mod.rs10
5 files changed, 449 insertions, 416 deletions
diff --git a/src/icalwrap/icalcomponent.rs b/src/icalwrap/icalcomponent.rs
new file mode 100644
index 0000000..4b9076b
--- /dev/null
+++ b/src/icalwrap/icalcomponent.rs
@@ -0,0 +1,42 @@
+use std::ffi::CString;
+
+use super::IcalProperty;
+use ical;
+
+pub trait IcalComponent {
+ fn get_ptr(&self) -> *mut ical::icalcomponent;
+ fn as_component(&self) -> &dyn IcalComponent;
+
+ fn get_property(&self, property_kind: ical::icalproperty_kind) -> IcalProperty<'_> {
+ unsafe {
+ let property = ical::icalcomponent_get_first_property(self.get_ptr(), property_kind);
+ IcalProperty::from_ptr(property, self.as_component())
+ }
+ }
+
+ fn get_properties(self: &Self, property_kind: ical::icalproperty_kind) -> Vec<IcalProperty<'_>> {
+ let mut properties = Vec::new();
+ unsafe {
+ let mut property_ptr = ical::icalcomponent_get_first_property(self.get_ptr(), property_kind);
+ while !property_ptr.is_null() {
+ let property = IcalProperty::from_ptr(property_ptr, self.as_component());
+ properties.push(property);
+ property_ptr = ical::icalcomponent_get_next_property(self.get_ptr(), property_kind);
+ }
+ }
+ properties
+ }
+
+ fn get_properties_all(&self) -> Vec<IcalProperty<'_>> {
+ self.get_properties(ical::icalproperty_kind_ICAL_ANY_PROPERTY)
+ }
+
+ fn get_properties_by_name(&self, property_name: &str) -> Vec<IcalProperty> {
+ let property_kind = unsafe {
+ let c_str = CString::new(property_name).unwrap();
+ ical::icalproperty_string_to_kind(c_str.as_ptr())
+ };
+ self.get_properties(property_kind)
+ }
+}
+
diff --git a/src/icalwrap/icalproperty.rs b/src/icalwrap/icalproperty.rs
new file mode 100644
index 0000000..15b07e0
--- /dev/null
+++ b/src/icalwrap/icalproperty.rs
@@ -0,0 +1,60 @@
+use chrono::NaiveDate;
+use std::ffi::CStr;
+use std::fmt;
+
+use super::icalcomponent::IcalComponent;
+use ical;
+
+pub struct IcalProperty<'a> {
+ ptr: *mut ical::icalproperty,
+ _parent: &'a dyn IcalComponent,
+}
+
+impl<'a> Drop for IcalProperty<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ ical::icalproperty_free(self.ptr);
+ }
+ }
+}
+
+impl<'a> fmt::Debug for IcalProperty<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.as_ical_string())
+ }
+}
+
+impl<'a> IcalProperty<'a> {
+ pub fn from_ptr(ptr: *mut ical::icalproperty, parent: &'a dyn IcalComponent) -> Self {
+ IcalProperty { ptr, _parent: parent }
+ }
+
+ pub fn get_name(&self) -> String {
+ unsafe {
+ let cstr = CStr::from_ptr(ical::icalproperty_get_property_name(self.ptr));
+ cstr.to_string_lossy().into_owned()
+ }
+ }
+
+ pub fn get_value(&self) -> String {
+ unsafe {
+ let cstr = CStr::from_ptr(ical::icalproperty_get_value_as_string(self.ptr));
+ cstr.to_string_lossy().into_owned()
+ }
+ }
+
+ pub fn as_ical_string(&self) -> String {
+ unsafe {
+ let cstr = CStr::from_ptr(ical::icalproperty_as_ical_string(self.ptr));
+ cstr.to_string_lossy().trim().to_owned()
+ }
+ }
+
+ pub fn get_value_as_date(&self) -> Option<NaiveDate> {
+ unsafe {
+ let date = ical::icaltime_from_string(ical::icalproperty_get_value_as_string(self.ptr));
+ NaiveDate::from_ymd_opt(date.year, date.month as u32, date.day as u32)
+ }
+ }
+}
+
diff --git a/src/icalwrap.rs b/src/icalwrap/icalvcalendar.rs
index ecd7a4e..ed91721 100644
--- a/src/icalwrap.rs
+++ b/src/icalwrap/icalvcalendar.rs
@@ -1,149 +1,24 @@
-use chrono::{NaiveDate, Duration, DateTime, Date, Utc, TimeZone, Local};
+use chrono::{DateTime, Utc};
use std::ffi::{CStr, CString};
-use std::fmt;
use std::ops::Deref;
use std::path::PathBuf;
use std::rc::Rc;
+use super::IcalVEvent;
+use super::IcalComponent;
use ical;
-pub trait IcalComponent {
- fn get_ptr(&self) -> *mut ical::icalcomponent;
- fn as_component(&self) -> &dyn IcalComponent;
-
- fn get_property(&self, property_kind: ical::icalproperty_kind) -> IcalProperty<'_> {
- unsafe {
- let property = ical::icalcomponent_get_first_property(self.get_ptr(), property_kind);
- IcalProperty::from_ptr(property, self.as_component())
- }
- }
-
- fn get_properties(self: &Self, property_kind: ical::icalproperty_kind) -> Vec<IcalProperty<'_>> {
- let mut properties = Vec::new();
- unsafe {
- let mut property_ptr = ical::icalcomponent_get_first_property(self.get_ptr(), property_kind);
- while !property_ptr.is_null() {
- let property = IcalProperty::from_ptr(property_ptr, self.as_component());
- properties.push(property);
- property_ptr = ical::icalcomponent_get_next_property(self.get_ptr(), property_kind);
- }
- }
- properties
- }
-
- fn get_properties_all(&self) -> Vec<IcalProperty<'_>> {
- self.get_properties(ical::icalproperty_kind_ICAL_ANY_PROPERTY)
- }
-
- fn get_properties_by_name(&self, property_name: &str) -> Vec<IcalProperty> {
- let property_kind = unsafe {
- let c_str = CString::new(property_name).unwrap();
- ical::icalproperty_string_to_kind(c_str.as_ptr())
- };
- self.get_properties(property_kind)
- }
-}
-
-struct IcalComponentOwner {
- ptr: *mut ical::icalcomponent
-}
-
-impl Deref for IcalComponentOwner {
- type Target = *mut ical::icalcomponent;
-
- fn deref(&self) -> &Self::Target {
- &self.ptr
- }
-}
-
pub struct IcalVCalendar {
comp: Rc<IcalComponentOwner>,
path: Option<PathBuf>,
instance_timestamp: Option<DateTime<Utc>>,
}
-pub struct IcalVEvent {
- ptr: *mut ical::icalcomponent,
- parent: Option<IcalVCalendar>,
- instance_timestamp: Option<DateTime<Utc>>,
-}
-
-pub struct IcalProperty<'a> {
- ptr: *mut ical::icalproperty,
- _parent: &'a dyn IcalComponent,
-}
-
pub struct IcalEventIter<'a> {
iter: ical::icalcompiter,
parent: &'a IcalVCalendar,
}
-impl Drop for IcalComponentOwner {
- fn drop(&mut self) {
- unsafe {
- // println!("free");
- ical::icalcomponent_free(self.ptr);
- }
- }
-}
-
-impl Drop for IcalVEvent {
- fn drop(&mut self) {
- unsafe {
- // println!("free");
- ical::icalcomponent_free(self.ptr);
- }
- }
-}
-
-impl<'a> Drop for IcalProperty<'a> {
- fn drop(&mut self) {
- unsafe {
- ical::icalproperty_free(self.ptr);
- }
- }
-}
-
-impl<'a> IcalProperty<'a> {
- fn from_ptr(ptr: *mut ical::icalproperty, parent: &'a dyn IcalComponent) -> Self {
- IcalProperty { ptr, _parent: parent }
- }
-
- pub fn get_name(&self) -> String {
- unsafe {
- let cstr = CStr::from_ptr(ical::icalproperty_get_property_name(self.ptr));
- cstr.to_string_lossy().into_owned()
- }
- }
-
- pub fn get_value(&self) -> String {
- unsafe {
- let cstr = CStr::from_ptr(ical::icalproperty_get_value_as_string(self.ptr));
- cstr.to_string_lossy().into_owned()
- }
- }
-
- pub fn as_ical_string(&self) -> String {
- unsafe {
- let cstr = CStr::from_ptr(ical::icalproperty_as_ical_string(self.ptr));
- cstr.to_string_lossy().trim().to_owned()
- }
- }
-
- pub fn get_value_as_date(&self) -> Option<NaiveDate> {
- unsafe {
- let date = ical::icaltime_from_string(ical::icalproperty_get_value_as_string(self.ptr));
- NaiveDate::from_ymd_opt(date.year, date.month as u32, date.day as u32)
- }
- }
-}
-
-impl<'a> fmt::Debug for IcalProperty<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.as_ical_string())
- }
-}
-
impl IcalComponent for IcalVCalendar {
fn get_ptr(&self) -> *mut ical::icalcomponent {
self.comp.ptr
@@ -154,16 +29,6 @@ impl IcalComponent for IcalVCalendar {
}
}
-impl IcalComponent for IcalVEvent {
- fn get_ptr (&self) -> *mut ical::icalcomponent {
- self.ptr
- }
-
- fn as_component(&self) -> &dyn IcalComponent {
- self
- }
-}
-
impl Clone for IcalVCalendar {
fn clone (&self) -> Self {
let new_comp_ptr = unsafe {
@@ -185,7 +50,7 @@ impl IcalVCalendar {
}
}
- fn shallow_copy(&self) -> Self {
+ pub fn shallow_copy(&self) -> Self {
IcalVCalendar {
comp: self.comp.clone(),
path: self.path.clone(),
@@ -367,164 +232,6 @@ impl IcalVCalendar {
}
output
}
-
-}
-
-impl IcalVEvent {
- fn from_ptr_with_parent(
- ptr: *mut ical::icalcomponent,
- parent: &IcalVCalendar,
- ) -> IcalVEvent {
- IcalVEvent {
- ptr,
- parent: Some(parent.shallow_copy()),
- instance_timestamp: None,
- }
- }
-
- pub fn get_dtend_unix(&self) -> Option<i64> {
- match self.instance_timestamp {
- Some(timestamp) => unsafe {
- let icalduration = ical::icalcomponent_get_duration(self.ptr);
- let duration = Duration::seconds(i64::from(ical::icaldurationtype_as_int(icalduration)));
- Some(timestamp.checked_add_signed(duration)?.timestamp())
- },
- None =>
- unsafe {
- let dtend = ical::icalcomponent_get_dtend(self.ptr);
- trace!("{:?}", dtend);
- if ical::icaltime_is_null_time(dtend) == 1 {
- None
- } else {
- Some(ical::icaltime_as_timet_with_zone(dtend, dtend.zone))
- }
- }
- }
- }
-
- pub fn get_dtstart_unix(&self) -> Option<i64> {
- match self.instance_timestamp {
- Some(timestamp) => Some(timestamp.timestamp()),
- None => unsafe {
- let dtstart = ical::icalcomponent_get_dtstart(self.ptr);
- if ical::icaltime_is_null_time(dtstart) == 1 {
- None
- } else {
- Some(ical::icaltime_as_timet_with_zone(dtstart, dtstart.zone))
- }
- }
- }
- }
-
- pub fn get_dtend(&self) -> Option<DateTime<Local>> {
- let dtend = self.get_dtend_unix()?;
- Some(Utc.timestamp(dtend, 0).with_timezone(&Local))
- }
-
- pub fn get_dtstart(&self) -> Option<DateTime<Local>> {
- let dtstart = self.get_dtstart_unix()?;
- Some(Utc.timestamp(dtstart, 0).with_timezone(&Local))
- }
-
- pub fn get_dtstart_date(&self) -> Option<Date<Local>> {
- Some(self.get_dtstart()?.date())
- }
-
- pub fn get_dtend_date(&self) -> Option<Date<Local>> {
- Some(self.get_dtend()?.date())
- }
-
- pub fn has_recur(&self) -> bool {
- !self.get_properties(ical::icalproperty_kind_ICAL_RRULE_PROPERTY).is_empty()
- & self.instance_timestamp.is_none()
- }
-
- pub fn get_recur_datetimes(&self) -> Vec<DateTime<Utc>> {
- let mut result = vec!();
- let result_ptr: *mut ::std::os::raw::c_void = &mut result as *mut _ as *mut ::std::os::raw::c_void;
-
- unsafe {
- let dtstart = ical::icalcomponent_get_dtstart(self.ptr);
- let mut dtend = ical::icalcomponent_get_dtend(self.ptr);
-
- //unroll up to 1 year in the future
- dtend.year += 1;
-
- ical::icalcomponent_foreach_recurrence(self.ptr, dtstart, dtend, Some(recur_callback), result_ptr);
- }
-
- result
- }
-
- fn with_internal_timestamp(&self, datetime: DateTime<Utc>) -> IcalVEvent {
- IcalVEvent {
- ptr: self.ptr,
- parent: self.parent.as_ref().map(|parent| parent.shallow_copy()),
- instance_timestamp: Some(datetime),
- }
- }
-
- pub fn get_recur_instances(&self) -> impl Iterator<Item = IcalVEvent> + '_ {
- self.get_recur_datetimes().into_iter().map(move |rec| self.with_internal_timestamp(rec))
- }
-
- pub fn get_parent(&self) -> Option<&IcalVCalendar> {
- self.parent.as_ref()
- }
-
- pub fn get_khaleesi_line(&self) -> Option<String> {
- let dtstart = self.get_dtstart()?.timestamp();
- let dtstart_string = format!("{:010}", dtstart);
- let path_string = self.parent.as_ref()?.get_path_as_string();
- Some([dtstart_string, path_string].join(" "))
- }
-
- pub fn get_summary(&self) -> Option<String> {
- unsafe {
- let ptr = ical::icalcomponent_get_summary(self.ptr);
- if ! ptr.is_null() {
- Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
- } else {
- None
- }
- }
- }
-
- pub fn get_description(&self) -> Option<String> {
- unsafe {
- let ptr = ical::icalcomponent_get_description(self.ptr);
- if !ptr.is_null() {
- Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
- } else {
- None
- }
- }
- }
-
- pub fn get_location(&self) -> Option<String> {
- unsafe {
- let ptr = ical::icalcomponent_get_location(self.ptr);
- if !ptr.is_null() {
- Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
- } else {
- None
- }
- }
- }
-
- pub fn get_uid(&self) -> String {
- unsafe {
- let cstr = CStr::from_ptr(ical::icalcomponent_get_uid(self.ptr));
- cstr.to_string_lossy().into_owned()
- }
- }
-
- pub fn is_allday(&self) -> bool {
- unsafe {
- let dtstart = ical::icalcomponent_get_dtstart(self.ptr);
- dtstart.is_date == 1
- }
- }
}
impl<'a> IcalEventIter<'a> {
@@ -546,30 +253,6 @@ impl<'a> IcalEventIter<'a> {
}
}
-//impl<'a> IntoIterator for &'a IcalComponent {
-// type Item = IcalComponent;
-// type IntoIter = IcalCompIter<'a>;
-//
-// fn into_iter(self) -> Self::IntoIter {
-// IcalCompIter::from_comp(&self, ical::icalcomponent_kind_ICAL_ANY_COMPONENT)
-// }
-//}
-
-extern "C" fn recur_callback(
- _comp: *mut ical::icalcomponent,
- span: *mut ical::icaltime_span,
- data: *mut ::std::os::raw::c_void) {
- let data: &mut Vec<DateTime<Utc>> = unsafe { &mut *(data as *mut Vec<DateTime<Utc>>) };
-
- let spanstart = unsafe {
- trace!("callback!, {:?}", *span);
- let start = (*span).start;
- Utc.timestamp(start, 0)
- };
-
- data.push(spanstart);
-}
-
impl <'a> Iterator for IcalEventIter<'a> {
type Item = IcalVEvent;
@@ -587,10 +270,32 @@ impl <'a> Iterator for IcalEventIter<'a> {
}
}
+struct IcalComponentOwner {
+ ptr: *mut ical::icalcomponent
+}
+
+impl Deref for IcalComponentOwner {
+ type Target = *mut ical::icalcomponent;
+
+ fn deref(&self) -> &Self::Target {
+ &self.ptr
+ }
+}
+
+impl Drop for IcalComponentOwner {
+ fn drop(&mut self) {
+ unsafe {
+ // println!("free");
+ ical::icalcomponent_free(self.ptr);
+ }
+ }
+}
+
#[cfg(test)]
-mod test {
+mod tests {
use super::*;
use testdata;
+ use chrono::{Local, TimeZone};
#[test]
fn event_iterator_element_count() {
@@ -612,17 +317,6 @@ mod test {
}
#[test]
- fn recur_iterator_test() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_RECUR, None).unwrap();
- let event = cal.get_principal_event();
- assert_eq!(Local.ymd(2018, 10, 11), event.get_dtstart_date().unwrap());
- assert_eq!(Local.ymd(2018, 10, 13), event.get_dtend_date().unwrap());
- assert_eq!("RRULE:FREQ=WEEKLY;COUNT=10", event.get_property(ical::icalproperty_kind_ICAL_RRULE_PROPERTY).as_ical_string());
- assert_eq!(10, event.get_recur_datetimes().len());
- assert_eq!(10, event.get_recur_instances().count());
- }
-
- #[test]
fn get_khaleesi_line_test() {
let path = Some(PathBuf::from("test/path"));
let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, path).unwrap();
@@ -631,60 +325,11 @@ mod test {
}
#[test]
- fn test_get_all_properties() {
+ fn test_get_all_properties_cal() {
let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
let props = cal.get_properties_all();
assert_eq!(2, props.len());
-
- let event = cal.get_principal_event();
- let props = event.get_properties_all();
- assert_eq!(7, props.len());
- }
-
- #[test]
- fn test_get_property_get_value() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- let event = cal.get_principal_event();
- let prop = event.get_properties_by_name("DTSTART");
-
- assert_eq!(1, prop.len());
- assert_eq!("DTSTART", prop[0].get_name());
- assert_eq!("20070628", prop[0].get_value());
- assert_eq!(NaiveDate::from_ymd_opt(2007,6,28), prop[0].get_value_as_date());
- }
-
- #[test]
- fn test_get_property_debug() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- let event = cal.get_principal_event();
- let prop = event.get_properties_by_name("DTSTART");
-
- assert_eq!("DTSTART;VALUE=DATE:20070628", format!("{:?}", prop[0]));
- }
-
- #[test]
- fn test_get_sumary() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- let event = cal.get_principal_event();
-
- assert_eq!(Some("Festival International de Jazz de Montreal".to_string()), event.get_summary());
- }
-
- #[test]
- fn test_get_sumary_none() {
- let cal = IcalVCalendar::from_str(testdata::TEST_NO_SUMMARY, None).unwrap();
- let event = cal.get_principal_event();
-
- assert_eq!(None, event.get_summary());
- }
-
- #[test]
- fn test_get_description() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_ONE_MEETING, None).unwrap();
- let event = cal.get_principal_event();
-
- assert_eq!(Some("Discuss how we can test c&s interoperability\nusing iCalendar and other IETF standards.".to_string()), event.get_description());
}
#[test]
@@ -715,39 +360,6 @@ mod test {
}
#[test]
- fn test_get_location() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_ONE_MEETING, None).unwrap();
- let event = cal.get_principal_event();
-
- assert_eq!(Some("LDB Lobby".to_string()), event.get_location());
- }
-
-
- #[test]
- fn test_get_location_none() {
- let cal = IcalVCalendar::from_str(testdata::TEST_NO_SUMMARY, None).unwrap();
- let event = cal.get_principal_event();
-
- assert_eq!(None, event.get_location());
- }
-
- #[test]
- fn has_recur_test() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_RECUR, None).unwrap();
- assert!(cal.get_principal_event().has_recur());
- }
-
- #[test]
- fn recur_datetimes_test() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_RECUR, None).unwrap();
-
- let event = cal.get_principal_event();
- let mut recur_instances = event.get_recur_instances();
- assert_eq!(Utc.ymd(2018, 10, 11).and_hms(0, 0, 0).with_timezone(&Local), recur_instances.next().unwrap().get_dtstart().unwrap());
- assert_eq!(Utc.ymd(2018, 10, 18).and_hms(0, 0, 0).with_timezone(&Local), recur_instances.next().unwrap().get_dtstart().unwrap());
- }
-
- #[test]
fn test_with_internal_timestamp() {
let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
diff --git a/src/icalwrap/icalvevent.rs b/src/icalwrap/icalvevent.rs
new file mode 100644
index 0000000..96d0f11
--- /dev/null
+++ b/src/icalwrap/icalvevent.rs
@@ -0,0 +1,309 @@
+use chrono::{Duration, DateTime, Date, Utc, TimeZone, Local};
+use std::ffi::CStr;
+
+use super::IcalComponent;
+use super::IcalVCalendar;
+use ical;
+
+pub struct IcalVEvent {
+ ptr: *mut ical::icalcomponent,
+ parent: Option<IcalVCalendar>,
+ instance_timestamp: Option<DateTime<Utc>>,
+}
+
+impl Drop for IcalVEvent {
+ fn drop(&mut self) {
+ unsafe {
+ // println!("free");
+ ical::icalcomponent_free(self.ptr);
+ }
+ }
+}
+
+impl IcalComponent for IcalVEvent {
+ fn get_ptr (&self) -> *mut ical::icalcomponent {
+ self.ptr
+ }
+ fn as_component(&self) -> &dyn IcalComponent {
+ self
+ }
+}
+
+impl IcalVEvent {
+ pub fn from_ptr_with_parent(
+ ptr: *mut ical::icalcomponent,
+ parent: &IcalVCalendar,
+ ) -> IcalVEvent {
+ IcalVEvent {
+ ptr,
+ parent: Some(parent.shallow_copy()),
+ instance_timestamp: None,
+ }
+ }
+
+ pub fn get_dtend_unix(&self) -> Option<i64> {
+ match self.instance_timestamp {
+ Some(timestamp) => unsafe {
+ let icalduration = ical::icalcomponent_get_duration(self.ptr);
+ let duration = Duration::seconds(i64::from(ical::icaldurationtype_as_int(icalduration)));
+ Some(timestamp.checked_add_signed(duration)?.timestamp())
+ },
+ None =>
+ unsafe {
+ let dtend = ical::icalcomponent_get_dtend(self.ptr);
+ trace!("{:?}", dtend);
+ if ical::icaltime_is_null_time(dtend) == 1 {
+ None
+ } else {
+ Some(ical::icaltime_as_timet_with_zone(dtend, dtend.zone))
+ }
+ }
+ }
+ }
+
+ pub fn get_dtstart_unix(&self) -> Option<i64> {
+ match self.instance_timestamp {
+ Some(timestamp) => Some(timestamp.timestamp()),
+ None => unsafe {
+ let dtstart = ical::icalcomponent_get_dtstart(self.ptr);
+ if ical::icaltime_is_null_time(dtstart) == 1 {
+ None
+ } else {
+ Some(ical::icaltime_as_timet_with_zone(dtstart, dtstart.zone))
+ }
+ }
+ }
+ }
+
+ pub fn get_dtend(&self) -> Option<DateTime<Local>> {
+ let dtend = self.get_dtend_unix()?;
+ Some(Utc.timestamp(dtend, 0).with_timezone(&Local))
+ }
+
+ pub fn get_dtstart(&self) -> Option<DateTime<Local>> {
+ let dtstart = self.get_dtstart_unix()?;
+ Some(Utc.timestamp(dtstart, 0).with_timezone(&Local))
+ }
+
+ pub fn get_dtstart_date(&self) -> Option<Date<Local>> {
+ Some(self.get_dtstart()?.date())
+ }
+
+ pub fn get_dtend_date(&self) -> Option<Date<Local>> {
+ Some(self.get_dtend()?.date())
+ }
+
+ pub fn has_recur(&self) -> bool {
+ !self.get_properties(ical::icalproperty_kind_ICAL_RRULE_PROPERTY).is_empty()
+ & self.instance_timestamp.is_none()
+ }
+
+ pub fn get_recur_datetimes(&self) -> Vec<DateTime<Utc>> {
+ let mut result = vec!();
+ let result_ptr: *mut ::std::os::raw::c_void = &mut result as *mut _ as *mut ::std::os::raw::c_void;
+
+ unsafe {
+ let dtstart = ical::icalcomponent_get_dtstart(self.ptr);
+ let mut dtend = ical::icalcomponent_get_dtend(self.ptr);
+
+ //unroll up to 1 year in the future
+ dtend.year += 1;
+
+ ical::icalcomponent_foreach_recurrence(self.ptr, dtstart, dtend, Some(recur_callback), result_ptr);
+ }
+
+ result
+ }
+
+ pub fn with_internal_timestamp(&self, datetime: DateTime<Utc>) -> IcalVEvent {
+ IcalVEvent {
+ ptr: self.ptr,
+ parent: self.parent.as_ref().map(|parent| parent.shallow_copy()),
+ instance_timestamp: Some(datetime),
+ }
+ }
+
+ pub fn get_recur_instances(&self) -> impl Iterator<Item = IcalVEvent> + '_ {
+ self.get_recur_datetimes().into_iter().map(move |rec| self.with_internal_timestamp(rec))
+ }
+
+ pub fn get_parent(&self) -> Option<&IcalVCalendar> {
+ self.parent.as_ref()
+ }
+
+ pub fn get_khaleesi_line(&self) -> Option<String> {
+ let dtstart = self.get_dtstart()?.timestamp();
+ let dtstart_string = format!("{:010}", dtstart);
+ let path_string = self.parent.as_ref()?.get_path_as_string();
+ Some([dtstart_string, path_string].join(" "))
+ }
+
+ pub fn get_summary(&self) -> Option<String> {
+ unsafe {
+ let ptr = ical::icalcomponent_get_summary(self.ptr);
+ if ! ptr.is_null() {
+ Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
+ } else {
+ None
+ }
+ }
+ }
+
+ pub fn get_description(&self) -> Option<String> {
+ unsafe {
+ let ptr = ical::icalcomponent_get_description(self.ptr);
+ if !ptr.is_null() {
+ Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
+ } else {
+ None
+ }
+ }
+ }
+
+ pub fn get_location(&self) -> Option<String> {
+ unsafe {
+ let ptr = ical::icalcomponent_get_location(self.ptr);
+ if !ptr.is_null() {
+ Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
+ } else {
+ None
+ }
+ }
+ }
+
+ pub fn get_uid(&self) -> String {
+ unsafe {
+ let cstr = CStr::from_ptr(ical::icalcomponent_get_uid(self.ptr));
+ cstr.to_string_lossy().into_owned()
+ }
+ }
+
+ pub fn is_allday(&self) -> bool {
+ unsafe {
+ let dtstart = ical::icalcomponent_get_dtstart(self.ptr);
+ dtstart.is_date == 1
+ }
+ }
+}
+
+extern "C" fn recur_callback(
+ _comp: *mut ical::icalcomponent,
+ span: *mut ical::icaltime_span,
+ data: *mut ::std::os::raw::c_void) {
+ let data: &mut Vec<DateTime<Utc>> = unsafe { &mut *(data as *mut Vec<DateTime<Utc>>) };
+
+ let spanstart = unsafe {
+ trace!("callback!, {:?}", *span);
+ let start = (*span).start;
+ Utc.timestamp(start, 0)
+ };
+
+ data.push(spanstart);
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use testdata;
+ use chrono::NaiveDate;
+
+ #[test]
+ fn recur_iterator_test() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_RECUR, None).unwrap();
+ let event = cal.get_principal_event();
+ assert_eq!(Local.ymd(2018, 10, 11), event.get_dtstart_date().unwrap());
+ assert_eq!(Local.ymd(2018, 10, 13), event.get_dtend_date().unwrap());
+ assert_eq!("RRULE:FREQ=WEEKLY;COUNT=10", event.get_property(ical::icalproperty_kind_ICAL_RRULE_PROPERTY).as_ical_string());
+ assert_eq!(10, event.get_recur_datetimes().len());
+ assert_eq!(10, event.get_recur_instances().count());
+ }
+
+ #[test]
+ fn test_get_all_properties() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
+
+ let event = cal.get_principal_event();
+ let props = event.get_properties_all();
+ assert_eq!(7, props.len());
+ }
+
+ #[test]
+ fn test_get_property_get_value() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
+ let event = cal.get_principal_event();
+ let prop = event.get_properties_by_name("DTSTART");
+
+ assert_eq!(1, prop.len());
+ assert_eq!("DTSTART", prop[0].get_name());
+ assert_eq!("20070628", prop[0].get_value());
+ assert_eq!(NaiveDate::from_ymd_opt(2007,6,28), prop[0].get_value_as_date());
+ }
+
+ #[test]
+ fn test_get_property_debug() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
+ let event = cal.get_principal_event();
+ let prop = event.get_properties_by_name("DTSTART");
+
+ assert_eq!("DTSTART;VALUE=DATE:20070628", format!("{:?}", prop[0]));
+ }
+
+ #[test]
+ fn test_get_sumary() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
+ let event = cal.get_principal_event();
+
+ assert_eq!(Some("Festival International de Jazz de Montreal".to_string()), event.get_summary());
+ }
+
+ #[test]
+ fn test_get_sumary_none() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_NO_SUMMARY, None).unwrap();
+ let event = cal.get_principal_event();
+
+ assert_eq!(None, event.get_summary());
+ }
+
+ #[test]
+ fn test_get_description() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_ONE_MEETING, None).unwrap();
+ let event = cal.get_principal_event();
+
+ assert_eq!(Some("Discuss how we can test c&s interoperability\nusing iCalendar and other IETF standards.".to_string()), event.get_description());
+ }
+
+ #[test]
+ fn test_get_location() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_ONE_MEETING, None).unwrap();
+ let event = cal.get_principal_event();
+
+ assert_eq!(Some("LDB Lobby".to_string()), event.get_location());
+ }
+
+
+ #[test]
+ fn test_get_location_none() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_NO_SUMMARY, None).unwrap();
+ let event = cal.get_principal_event();
+
+ assert_eq!(None, event.get_location());
+ }
+
+ #[test]
+ fn has_recur_test() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_RECUR, None).unwrap();
+ assert!(cal.get_principal_event().has_recur());
+ }
+
+ #[test]
+ fn recur_datetimes_test() {
+ let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_RECUR, None).unwrap();
+
+ let event = cal.get_principal_event();
+ let mut recur_instances = event.get_recur_instances();
+ assert_eq!(Utc.ymd(2018, 10, 11).and_hms(0, 0, 0).with_timezone(&Local), recur_instances.next().unwrap().get_dtstart().unwrap());
+ assert_eq!(Utc.ymd(2018, 10, 18).and_hms(0, 0, 0).with_timezone(&Local), recur_instances.next().unwrap().get_dtstart().unwrap());
+ }
+
+
+}
diff --git a/src/icalwrap/mod.rs b/src/icalwrap/mod.rs
new file mode 100644
index 0000000..12d1a6f
--- /dev/null
+++ b/src/icalwrap/mod.rs
@@ -0,0 +1,10 @@
+mod icalvcalendar;
+mod icalvevent;
+mod icalproperty;
+mod icalcomponent;
+
+pub use self::icalvcalendar::IcalVCalendar;
+pub use self::icalvcalendar::IcalEventIter;
+pub use self::icalvevent::IcalVEvent;
+pub use self::icalproperty::IcalProperty;
+pub use self::icalcomponent::IcalComponent;