summaryrefslogtreecommitdiffstats
path: root/src/icalwrap/icalvcalendar.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/icalwrap/icalvcalendar.rs')
-rw-r--r--src/icalwrap/icalvcalendar.rs670
1 files changed, 0 insertions, 670 deletions
diff --git a/src/icalwrap/icalvcalendar.rs b/src/icalwrap/icalvcalendar.rs
deleted file mode 100644
index ced299a..0000000
--- a/src/icalwrap/icalvcalendar.rs
+++ /dev/null
@@ -1,670 +0,0 @@
-use std::ffi::{CStr, CString};
-use std::io;
-use std::path::{Path, PathBuf};
-use std::rc::Rc;
-
-use super::IcalComponent;
-use super::IcalTime;
-use super::IcalVEvent;
-use crate::ical;
-use crate::khevent::KhEvent;
-
-pub struct IcalVCalendar {
- comp: Rc<IcalComponentOwner>,
- path: Option<PathBuf>,
- instance_timestamp: Option<IcalTime>,
-}
-
-pub struct IcalEventIter<'a> {
- iter: ical::icalcompiter,
- parent: &'a IcalVCalendar,
-}
-
-impl IcalComponent for IcalVCalendar {
- fn get_ptr(&self) -> *mut ical::icalcomponent {
- self.comp.ptr
- }
-
- fn as_component(&self) -> &dyn IcalComponent {
- self
- }
-}
-
-impl Clone for IcalVCalendar {
- fn clone(&self) -> Self {
- let new_comp_ptr = unsafe { ical::icalcomponent_new_clone(self.comp.ptr) };
- let mut new_calendar = IcalVCalendar::from_ptr(new_comp_ptr);
- new_calendar.path = self.path.clone();
- new_calendar.instance_timestamp = self.instance_timestamp.clone();
- new_calendar
- }
-}
-
-impl IcalVCalendar {
- fn from_ptr(ptr: *mut ical::icalcomponent) -> Self {
- IcalVCalendar {
- comp: Rc::new(IcalComponentOwner { ptr }),
- path: None,
- instance_timestamp: None,
- }
- }
-
- pub fn shallow_copy(&self) -> Self {
- IcalVCalendar {
- comp: self.comp.clone(),
- path: self.path.clone(),
- instance_timestamp: self.instance_timestamp.clone(),
- }
- }
-
- //pub fn with_internal_timestamp(mut self, datetime: &IcalTime) -> IcalVCalendar {
- //self.instance_timestamp = Some(datetime.clone());
- //self
- //}
-
- pub fn with_path(mut self, path: &Path) -> IcalVCalendar {
- self.path = Some(path.to_path_buf());
- self
- }
-
- //TODO should probably be private
- pub fn from_str(str: &str, path: Option<&Path>) -> io::Result<Self> {
- unsafe {
- let c_str = CString::new(str).unwrap();
- let parsed_cal = ical::icalparser_parse_string(c_str.as_ptr());
- if parsed_cal.is_null() {
- return Err(io::Error::new(io::ErrorKind::Other, "calendar has no path"));
- }
-
- let kind = ical::icalcomponent_isa(parsed_cal);
- if kind != ical::icalcomponent_kind_ICAL_VCALENDAR_COMPONENT {
- let kind = CStr::from_ptr(ical::icalcomponent_kind_to_string(kind)).to_string_lossy();
- return Err(io::Error::new(
- io::ErrorKind::Other,
- format!("expected VCALENDAR component, got {}", kind),
- ));
- }
-
- let mut cal = IcalVCalendar::from_ptr(parsed_cal);
- cal.path = path.map(|path| path.to_path_buf());
-
- Ok(cal)
- }
- }
-
- pub fn with_normalize(self) -> Self {
- unsafe {
- ical::icalcomponent_normalize(self.get_ptr());
- }
- self
- }
-
- pub fn to_string(&self) -> String {
- unsafe {
- let ical_cstr = CStr::from_ptr(ical::icalcomponent_as_ical_string(self.get_ptr()));
- ical_cstr.to_string_lossy().into_owned()
- }
- }
-
- pub fn get_uid(&self) -> String {
- unsafe {
- let uid_cstr = CStr::from_ptr(ical::icalcomponent_get_uid(
- self.get_principal_event().get_ptr(),
- ));
- uid_cstr.to_string_lossy().into_owned()
- }
- }
-
- pub fn with_uid(mut self, uid: &str) -> Result<Self, String> {
- {
- let events = self.events_iter();
- if events.unique_uid_count() > 1 {
- return Err(format!(
- "More than one event in file: {}",
- self.get_path_as_string().unwrap_or_else(|| "".to_string())
- ));
- }
- let events = self.events_iter();
- let uid_cstr = CString::new(uid).unwrap();
- for event in events {
- unsafe {
- ical::icalcomponent_set_uid(event.get_ptr(), uid_cstr.as_ptr());
- }
- }
- }
- self.path = self
- .path
- .map(|path| path.with_file_name(uid.to_owned() + ".ics"));
- Ok(self)
- }
-
- pub fn with_dtstamp_now(self) -> Self {
- let dtstamp = IcalTime::utc();
- unsafe {
- ical::icalcomponent_set_dtstamp(self.get_ptr(), *dtstamp);
- }
- self
- }
-
- pub fn with_dtstart(self, dtstart: &IcalTime) -> Self {
- let event = self.get_principal_event();
- unsafe {
- let timezone = event.get_dtstart().and_then(|x| x.get_timezone());
- match timezone {
- Some(timezone) => {
- ical::icalcomponent_set_dtstart(event.get_ptr(), *dtstart.with_timezone(&timezone));
- }
- None => {
- ical::icalcomponent_set_dtstart(event.get_ptr(), **dtstart);
- }
- }
- }
- self
- }
-
- pub fn with_dtend(self, dtend: &IcalTime) -> Self {
- let event = self.get_principal_event();
- unsafe {
- let timezone = event.get_dtend().and_then(|x| x.get_timezone());
- match timezone {
- Some(timezone) => {
- ical::icalcomponent_set_dtend(event.get_ptr(), *dtend.with_timezone(&timezone));
- }
- None => {
- ical::icalcomponent_set_dtend(event.get_ptr(), **dtend);
- }
- }
- }
- self
- }
-
- pub fn with_location(self, location: &str) -> Self {
- let event = self.get_principal_event();
- unsafe {
- let c_str = CString::new(location).unwrap();
- ical::icalcomponent_set_location(event.get_ptr(), c_str.as_ptr());
- }
- self
- }
-
- pub fn with_summary(self, summary: &str) -> Self {
- let event = self.get_principal_event();
- unsafe {
- let c_str = CString::new(summary).unwrap();
- ical::icalcomponent_set_summary(event.get_ptr(), c_str.as_ptr());
- }
- self
- }
-
- pub fn with_last_modified_now(self) -> Self {
- let event = self.get_principal_event();
- unsafe {
- let now_icaltime = IcalTime::utc();
-
- let last_modified_kind = ical::icalproperty_kind_ICAL_LASTMODIFIED_PROPERTY;
- if let Some(prop) = event.get_property(last_modified_kind) {
- ical::icalproperty_set_lastmodified(prop.ptr, *now_icaltime);
- } else {
- let prop_lastmod = ical::icalproperty_new_lastmodified(*now_icaltime);
- ical::icalcomponent_add_property(event.get_ptr(), prop_lastmod);
- }
- }
- self
- }
-
- pub fn with_remove_property(self, property_name: &str) -> (Self, usize) {
- let property_kind = unsafe {
- let c_str = CString::new(property_name).unwrap();
- ical::icalproperty_string_to_kind(c_str.as_ptr())
- };
-
- let count = unsafe { IcalComponent::remove_property_all(&self, property_kind) };
- (self, count)
- }
-
- pub fn with_keep_uid(self, uid_to_keep: &str) -> Self {
- unsafe {
- ical::icalcomponent_get_first_component(
- self.comp.ptr,
- ical::icalcomponent_kind_ICAL_ANY_COMPONENT,
- );
-
- loop {
- let comp = ical::icalcomponent_get_current_component(self.comp.ptr);
- if comp.is_null() {
- return self;
- }
- let uid_ptr = ical::icalcomponent_get_uid(comp);
- if !uid_ptr.is_null() {
- let uid = CStr::from_ptr(uid_ptr).to_string_lossy();
- if uid != uid_to_keep {
- ical::icalcomponent_remove_component(self.comp.ptr, comp);
- continue;
- }
- }
- ical::icalcomponent_get_next_component(
- self.comp.ptr,
- ical::icalcomponent_kind_ICAL_ANY_COMPONENT,
- );
- }
- }
- }
-
- pub fn get_path_as_string(&self) -> Option<String> {
- self.path.as_ref().map(|path| format!("{}", path.display()))
- }
-
- pub fn get_path(&self) -> Option<&PathBuf> {
- self.path.as_ref()
- }
-
- pub fn get_calendar_name(&self) -> Option<String> {
- let calendar_name = self.path.as_ref()?.parent()?.file_name()?;
- Some(calendar_name.to_string_lossy().into_owned())
- }
-
- pub fn events_iter(&self) -> IcalEventIter {
- IcalEventIter::from_vcalendar(self)
- }
-
- pub fn get_first_event(&self) -> IcalVEvent {
- let event = unsafe {
- ical::icalcomponent_get_first_component(
- self.get_ptr(),
- ical::icalcomponent_kind_ICAL_VEVENT_COMPONENT,
- )
- };
- if self.events_iter().unique_uid_count() > 1 {
- warn!(
- "More than one event in file: {}",
- self.get_path_as_string().unwrap_or_else(|| "".to_string())
- )
- }
- IcalVEvent::from_ptr_with_parent(event, self)
- }
-
- pub(in crate::icalwrap) fn get_principal_event(&self) -> IcalVEvent {
- let mut event = self.get_first_event();
- if let Some(ref timestamp) = self.instance_timestamp {
- event = event.with_internal_timestamp(timestamp)
- }
- event
- }
-
- pub fn get_principal_khevent(&self) -> KhEvent {
- KhEvent::from_event_with_timestamp(self.get_first_event(), self.instance_timestamp.clone())
- }
-
- pub fn check_for_errors(&self) -> Option<Vec<String>> {
- unsafe { IcalVCalendar::check_icalcomponent(self.get_ptr()) }
- }
-
- /// to be used after parsing, parser adds X-LIC-ERROR properties for any error
- /// ical::icalrestriction_check() checks if the specification is violated and adds X-LIC-ERRORs accordingly
- /// ical::icalcomponent_count_errors() counts all X-LIC-ERROR properties
- unsafe fn check_icalcomponent(comp: *mut ical::icalcomponent) -> Option<Vec<String>> {
- ical::icalrestriction_check(comp);
- let error_count = ical::icalcomponent_count_errors(comp);
- let mut output: Vec<String> = Vec::new();
- if error_count > 0 {
- output.append(&mut IcalVCalendar::get_errors(comp));
-
- let mut inner_comp =
- ical::icalcomponent_get_first_component(comp, ical::icalcomponent_kind_ICAL_ANY_COMPONENT);
- while !inner_comp.is_null() {
- output.append(&mut IcalVCalendar::get_errors(inner_comp));
- inner_comp =
- ical::icalcomponent_get_next_component(comp, ical::icalcomponent_kind_ICAL_ANY_COMPONENT)
- }
- }
- if let Some(errstring) = IcalVCalendar::check_uid(comp) {
- output.push(errstring);
- }
- if output.is_empty() {
- None
- } else {
- Some(output)
- }
- }
-
- unsafe fn check_uid(comp: *mut ical::icalcomponent) -> Option<String> {
- let uid = ical::icalcomponent_get_uid(comp);
- if uid.is_null() {
- Some("missing required property: UID".to_string())
- } else {
- None
- }
- }
-
- unsafe fn get_errors(comp: *mut ical::icalcomponent) -> Vec<String> {
- let mut prop =
- ical::icalcomponent_get_first_property(comp, ical::icalproperty_kind_ICAL_XLICERROR_PROPERTY);
- let mut output: Vec<String> = Vec::new();
- while !prop.is_null() {
- let error_cstr = CStr::from_ptr(ical::icalproperty_get_xlicerror(prop))
- .to_str()
- .unwrap();
- output.push(error_cstr.to_owned());
- prop = ical::icalcomponent_get_next_property(
- comp,
- ical::icalproperty_kind_ICAL_XLICERROR_PROPERTY,
- );
- }
- output
- }
-}
-
-impl<'a> IcalEventIter<'a> {
- fn from_vcalendar(cal: &'a IcalVCalendar) -> Self {
- let vevent_kind = ical::icalcomponent_kind_ICAL_VEVENT_COMPONENT;
- let iter = unsafe { ical::icalcomponent_begin_component(cal.get_ptr(), vevent_kind) };
- IcalEventIter { iter, parent: &cal }
- }
-
- fn unique_uid_count(self) -> usize {
- let mut uids = self.map(|event| event.get_uid()).collect::<Vec<String>>();
- uids.sort_unstable();
- uids.dedup();
- uids.len()
- }
-}
-
-impl<'a> Iterator for IcalEventIter<'a> {
- type Item = IcalVEvent;
-
- fn next(&mut self) -> Option<Self::Item> {
- unsafe {
- let ptr = ical::icalcompiter_deref(&mut self.iter);
- if ptr.is_null() {
- None
- } else {
- ical::icalcompiter_next(&mut self.iter);
- let vevent = IcalVEvent::from_ptr_with_parent(ptr, self.parent);
- Some(vevent)
- }
- }
- }
-}
-
-struct IcalComponentOwner {
- ptr: *mut ical::icalcomponent,
-}
-
-impl Drop for IcalComponentOwner {
- fn drop(&mut self) {
- unsafe {
- // println!("free");
- ical::icalcomponent_free(self.ptr);
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::testdata;
- use chrono::{Local, TimeZone};
-
- #[test]
- fn test_from_str_empty() {
- assert!(IcalVCalendar::from_str("", None).is_err());
- }
-
- #[test]
- fn test_from_str_event() {
- assert!(IcalVCalendar::from_str(testdata::TEST_BARE_EVENT, None).is_err());
- }
-
- #[test]
- fn event_iterator_element_count() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- assert_eq!(cal.events_iter().count(), 1)
- }
-
- #[test]
- fn event_iterator_element_count_with_other() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_WITH_TIMEZONE_COMPONENT, None).unwrap();
- assert_eq!(cal.events_iter().count(), 1)
- }
-
- #[test]
- fn load_serialize() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- let back = cal.to_string().replace("\r\n", "\n");
- assert_eq!(back.trim(), testdata::TEST_EVENT_MULTIDAY)
- }
-
- #[test]
- fn load_serialize_with_error() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_WITH_X_LIC_ERROR, None).unwrap();
- let back = cal.to_string().replace("\r\n", "\n");
- assert_eq!(back.trim(), testdata::TEST_EVENT_WITH_X_LIC_ERROR)
- }
-
- #[test]
- fn with_dtstamp_test() {
- let mut cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- cal = cal.with_dtstamp_now();
- let event = cal.get_principal_event();
-
- let dtstamp_prop = ical::icalproperty_kind_ICAL_DTSTAMP_PROPERTY;
- let now_dtstamp = event.get_property(dtstamp_prop).unwrap().get_value();
- assert_eq!(now_dtstamp, "20130101T010203Z")
- }
-
- #[test]
- fn get_calendar_name_test() {
- let path = PathBuf::from("calname/event");
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY_ALLDAY, Some(&path)).unwrap();
- assert_eq!("calname".to_string(), cal.get_calendar_name().unwrap())
- }
-
- #[test]
- 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());
- }
-
- #[test]
- fn parse_checker_test_empty_summary() {
- let c_str = CString::new(testdata::TEST_EVENT_EMPTY_SUMMARY).unwrap();
- unsafe {
- let parsed_cal = ical::icalparser_parse_string(c_str.as_ptr());
- assert!(IcalVCalendar::check_icalcomponent(parsed_cal).is_some())
- }
- }
-
- #[test]
- fn parse_checker_test_no_uid() {
- let c_str = CString::new(testdata::TEST_EVENT_NO_UID).unwrap();
- unsafe {
- let parsed_cal = ical::icalparser_parse_string(c_str.as_ptr());
- assert!(IcalVCalendar::check_icalcomponent(parsed_cal).is_some())
- }
- }
-
- #[test]
- fn parse_checker_test_no_prodid() {
- let c_str = CString::new(testdata::TEST_EVENT_NO_PRODID).unwrap();
- unsafe {
- let parsed_cal = ical::icalparser_parse_string(c_str.as_ptr());
- assert!(IcalVCalendar::check_icalcomponent(parsed_cal).is_some())
- }
- }
-
- #[test]
- fn test_with_last_modified_now() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY_LASTMODIFIED, None).unwrap();
-
- let new_cal = cal.with_last_modified_now();
- let event = new_cal.get_principal_event();
-
- let last_modified_kind = ical::icalproperty_kind_ICAL_LASTMODIFIED_PROPERTY;
- assert_eq!(
- "20130101T010203Z",
- event.get_property(last_modified_kind).unwrap().get_value()
- );
- }
-
- #[test]
- fn test_with_last_modified_now_added() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
-
- let new_cal = cal.with_last_modified_now();
- let event = new_cal.get_principal_event();
-
- let last_modified_kind = ical::icalproperty_kind_ICAL_LASTMODIFIED_PROPERTY;
- assert_eq!(
- "20130101T010203Z",
- event.get_property(last_modified_kind).unwrap().get_value()
- );
- }
-
- #[test]
- fn test_with_location() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
-
- let location = "test";
- let new_cal = cal.with_location(location);
-
- let event = new_cal.get_principal_event();
- assert_eq!(location, event.get_location().unwrap())
- }
-
- #[test]
- fn test_with_summary() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
-
- let summary = "test";
- let new_cal = cal.with_summary(summary);
-
- let event = new_cal.get_principal_khevent();
- assert_eq!(summary, event.get_summary().unwrap())
- }
-
- #[test]
- fn test_with_dtend() {
- testdata::setup();
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
-
- let timestamp = IcalTime::floating_ymd(2018, 1, 1).and_hms(11, 30, 20);
- let new_cal = cal.with_dtend(&timestamp);
-
- let event = new_cal.get_principal_khevent();
- assert_eq!(timestamp, event.get_end().unwrap())
- }
-
- #[test]
- fn test_with_dtstart() {
- testdata::setup();
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
-
- let timestamp = IcalTime::floating_ymd(2018, 1, 1).and_hms(11, 30, 20);
- let new_cal = cal.with_dtstart(&timestamp);
-
- let event = new_cal.get_principal_khevent();
- assert_eq!(timestamp, event.get_start().unwrap())
- }
-
- #[test]
- fn test_with_dtstart_timezone() {
- testdata::setup();
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
-
- let local_date = Local.ymd(2018, 1, 1).and_hms(11, 30, 20);
- let timestamp = IcalTime::from(local_date);
-
- let new_cal = cal.with_dtstart(&timestamp);
-
- let event = new_cal.get_principal_khevent();
- assert_eq!(timestamp, event.get_start().unwrap());
- assert_eq!(
- "Europe/Berlin",
- event
- .get_start()
- .unwrap()
- .get_timezone()
- .unwrap()
- .get_name()
- );
- }
-
- //#[test]
- //fn test_with_internal_timestamp() {
- //let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- //
- //let timestamp = IcalTime::floating_ymd(2018, 1, 1).and_hms(11, 30, 20);
- //let new_cal = cal.with_internal_timestamp(&timestamp);
- //
- //let event = new_cal.get_principal_event();
- //assert_eq!(timestamp, event.get_dtstart().unwrap());
- //}
-
- #[test]
- fn with_uid_test() {
- let path = PathBuf::from("test/path");
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, Some(&path)).unwrap();
-
- let uid = "my_new_uid";
- let new_cal = cal.with_uid(uid).unwrap();
-
- for event in new_cal.events_iter() {
- assert_eq!(uid, event.get_uid());
- }
- assert_eq!(
- Some(path.with_file_name(uid.to_owned() + ".ics")),
- new_cal.path
- );
- }
-
- #[test]
- fn with_uid_multiple_test() {
- let path = PathBuf::from("test/path");
- let cal = IcalVCalendar::from_str(testdata::TEST_MULTIPLE_EVENTS, Some(&path)).unwrap();
-
- let uid = "my_new_uid";
- let new_cal = cal.with_uid(uid);
-
- assert!(new_cal.is_err());
- }
-
- #[test]
- fn with_keep_uid_test() {
- let path = PathBuf::from("test/path");
- let cal = IcalVCalendar::from_str(testdata::TEST_MULTIPLE_EVENTS, Some(&path)).unwrap();
-
- for uid in &["uid1", "uid2"] {
- let new_cal = cal.clone().with_keep_uid(uid);
-
- assert_eq!(1, new_cal.events_iter().count());
- assert_eq!(*uid, new_cal.get_uid());
- assert_eq!(*uid, new_cal.get_principal_event().get_uid());
- }
- }
-
- #[test]
- fn clone_test() {
- let path = PathBuf::from("test/path");
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, Some(&path)).unwrap();
- let cal2 = cal.clone().with_uid("my_new_uid").unwrap();
-
- assert_ne!(cal.get_uid(), cal2.get_uid());
- }
-
- #[test]
- fn parse_checker_test_negative() {
- let c_str = CString::new(testdata::TEST_EVENT_NO_PRODID).unwrap();
- unsafe {
- let parsed_cal = ical::icalparser_parse_string(c_str.as_ptr());
- assert!(IcalVCalendar::check_icalcomponent(parsed_cal).is_some())
- }
- }
-
- #[test]
- fn parse_checker_test() {
- let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap();
- assert!(cal.check_for_errors().is_none());
- }
-}