use std::ffi::CStr; use super::IcalComponent; use super::IcalVCalendar; use super::IcalTime; use super::IcalTimeZone; use super::IcalDuration; use crate::ical; pub struct IcalVEvent { ptr: *mut ical::icalcomponent, parent: Option, } 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(&self) -> Option { unsafe { let dtend = ical::icalcomponent_get_dtend(self.ptr); trace!("{:?}", dtend); if ical::icaltime_is_null_time(dtend) == 1 { None } else { Some(IcalTime::from(dtend)) } } } pub fn get_duration(&self) -> Option { unsafe { let duration = ical::icalcomponent_get_duration(self.ptr); if ical::icaldurationtype_is_bad_duration(duration) == 0 { Some(IcalDuration::from(duration)) } else { None } } } pub fn get_dtstart(&self) -> Option { unsafe { let dtstart = ical::icalcomponent_get_dtstart(self.ptr); if ical::icaltime_is_null_time(dtstart) == 1 { None } else { Some(IcalTime::from(dtstart)) } } } pub fn has_property_rrule(&self) -> bool { !self.get_properties(ical::icalproperty_kind_ICAL_RRULE_PROPERTY).is_empty() } pub fn get_recur_datetimes(&self) -> Vec { let mut result: Vec = vec!(); let result_ptr: *mut ::std::os::raw::c_void = &mut result as *mut _ as *mut ::std::os::raw::c_void; let dtstart = self.get_dtstart().unwrap(); unsafe { 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); } if dtstart.is_date() { result = result.into_iter().map(|time| time.as_date()).collect(); } result } pub fn shallow_copy(&self) -> IcalVEvent { IcalVEvent { ptr: self.ptr, parent: self.parent.as_ref().map(|parent| parent.shallow_copy()), } } //TODO remove this function pub(in crate::icalwrap) fn with_internal_timestamp(&self, _datetime: &IcalTime) -> IcalVEvent { IcalVEvent { ptr: self.ptr, parent: self.parent.as_ref().map(|parent| parent.shallow_copy()), } } pub fn get_parent(&self) -> Option<&IcalVCalendar> { self.parent.as_ref() } pub fn get_summary(&self) -> Option { 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 { 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 { 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 = unsafe { &mut *(data as *mut Vec) }; let spanstart = unsafe { let start = (*span).start; IcalTime::from_timestamp(start) }; data.push(spanstart); } #[cfg(test)] mod tests { use super::*; use crate::testdata; use chrono::NaiveDate; #[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_ALLDAY, 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_ALLDAY, None).unwrap(); let event = cal.get_principal_event(); let prop = event.get_property(ical::icalproperty_kind_ICAL_DTSTART_PROPERTY).unwrap(); assert_eq!("DTSTART;VALUE=DATE:20070628", format!("{:?}", prop)); } #[test] fn test_get_summary() { 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_summary_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_duration() { let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); let event = cal.get_principal_event(); assert_eq!(Some(IcalDuration::from_seconds(10*24*60*60 + 18*60*60)), event.get_duration()); } #[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_description_none() { let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); let event = cal.get_principal_event(); assert_eq!(None, 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()); } }