From 30b3b597669a15d8814f21b33bf11e071c87dc0d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Tue, 8 Oct 2019 19:33:50 +0200 Subject: Run cargo fmt --- src/component.rs | 250 ++++++----- src/duration.rs | 262 ++++++------ src/errors.rs | 106 ++--- src/lib.rs | 25 +- src/property.rs | 70 +-- src/testdata.rs | 102 +++-- src/testutils.rs | 14 +- src/time.rs | 586 +++++++++++++------------- src/timezone.rs | 218 +++++----- src/utils/dateutil.rs | 250 +++++------ src/utils/fileutil.rs | 73 ++-- src/utils/lock.rs | 66 +-- src/utils/misc.rs | 68 +-- src/vcalendar.rs | 1125 +++++++++++++++++++++++++------------------------ src/vevent.rs | 659 +++++++++++++++-------------- 15 files changed, 1980 insertions(+), 1894 deletions(-) diff --git a/src/component.rs b/src/component.rs index 11edc58..7de364c 100644 --- a/src/component.rs +++ b/src/component.rs @@ -3,128 +3,146 @@ use std::ffi::CString; use super::IcalProperty; 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) -> Option> { - let property = unsafe { - ical::icalcomponent_get_first_property(self.get_ptr(), property_kind) - }; - if !property.is_null() { - Some(IcalProperty::from_ptr(property, self.as_component())) - } else { - None + fn get_ptr(&self) -> *mut ical::icalcomponent; + fn as_component(&self) -> &dyn IcalComponent; + + fn get_property(&self, property_kind: ical::icalproperty_kind) -> Option> { + let property = + unsafe { ical::icalcomponent_get_first_property(self.get_ptr(), property_kind) }; + if !property.is_null() { + Some(IcalProperty::from_ptr(property, self.as_component())) + } else { + None + } } - } - - fn get_properties(self: &Self, property_kind: ical::icalproperty_kind) -> Vec> { - 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); - } + + fn get_properties( + self: &Self, + property_kind: ical::icalproperty_kind, + ) -> Vec> { + 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> { + self.get_properties(ical::icalproperty_kind_ICAL_ANY_PROPERTY) + } + + fn get_properties_by_name(&self, property_name: &str) -> Vec { + 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) } - properties - } - - fn get_properties_all(&self) -> Vec> { - self.get_properties(ical::icalproperty_kind_ICAL_ANY_PROPERTY) - } - - fn get_properties_by_name(&self, property_name: &str) -> Vec { - 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) - } - - fn get_property_by_name(&self, property_name: &str) -> Option { - let property_kind = unsafe { - let c_str = CString::new(property_name).unwrap(); - ical::icalproperty_string_to_kind(c_str.as_ptr()) - }; - self.get_property(property_kind) - } - - unsafe fn remove_property_all(&self, kind: ical::icalproperty_kind) -> usize { - - unsafe fn remove_property_inner(comp: *mut ical::icalcomponent, kind: ical::icalproperty_kind) -> usize { - let mut count = 0; - let mut prop = ical::icalcomponent_get_first_property(comp, kind); - while !prop.is_null() { - ical::icalcomponent_remove_property(comp, prop); - count += 1; - prop = ical::icalcomponent_get_current_property(comp); - } - let mut inner_comp = ical::icalcomponent_get_first_component(comp, ical::icalcomponent_kind_ICAL_ANY_COMPONENT); - while !inner_comp.is_null() { - count += remove_property_inner(inner_comp, kind); - inner_comp = ical::icalcomponent_get_next_component(comp, ical::icalcomponent_kind_ICAL_ANY_COMPONENT) - } - count + + fn get_property_by_name(&self, property_name: &str) -> Option { + let property_kind = unsafe { + let c_str = CString::new(property_name).unwrap(); + ical::icalproperty_string_to_kind(c_str.as_ptr()) + }; + self.get_property(property_kind) } - let comp = self.get_ptr(); - remove_property_inner(comp, kind) - } + unsafe fn remove_property_all(&self, kind: ical::icalproperty_kind) -> usize { + unsafe fn remove_property_inner( + comp: *mut ical::icalcomponent, + kind: ical::icalproperty_kind, + ) -> usize { + let mut count = 0; + let mut prop = ical::icalcomponent_get_first_property(comp, kind); + while !prop.is_null() { + ical::icalcomponent_remove_property(comp, prop); + count += 1; + prop = ical::icalcomponent_get_current_property(comp); + } + let mut inner_comp = ical::icalcomponent_get_first_component( + comp, + ical::icalcomponent_kind_ICAL_ANY_COMPONENT, + ); + while !inner_comp.is_null() { + count += remove_property_inner(inner_comp, kind); + inner_comp = ical::icalcomponent_get_next_component( + comp, + ical::icalcomponent_kind_ICAL_ANY_COMPONENT, + ) + } + count + } + + let comp = self.get_ptr(); + remove_property_inner(comp, kind) + } } #[cfg(test)] mod tests { - use super::*; - use crate::testdata; - use crate::IcalVCalendar; - - #[test] - fn get_property_test() { - let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); - let event = cal.get_principal_event(); - let prop_name = "SUMMARY"; - let prop_value: String = event.get_property_by_name(prop_name).unwrap().get_value(); - - assert_eq!("Festival International de Jazz de Montreal".to_string(), prop_value); - } - - #[test] - fn get_property_test_lastmodified() { - let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY_LASTMODIFIED, None).unwrap(); - let event = cal.get_principal_event(); - let prop_name = "LAST-MODIFIED"; - let prop_value: String = event.get_property_by_name(prop_name).unwrap().get_value(); - - assert_eq!("20070423T123432Z".to_string(), prop_value); - } - - #[test] - fn get_property_test_cal() { - let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); - let prop_name = "PRODID"; - let prop_value: String = cal.get_property_by_name(prop_name).unwrap().get_value(); - - assert_eq!("-//ABC Corporation//NONSGML My Product//EN".to_string(), prop_value); - } - - #[test] - fn get_property_test_negative() { - let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); - let event = cal.get_principal_event(); - let prop_name = "DESCRIPTION"; - let prop = event.get_property_by_name(prop_name); - - assert!(prop.is_none()); - } - - #[test] - fn get_property_by_name_test() { - let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); - let event = cal.get_principal_event(); - let prop_name = "NONSENSE"; - let prop = event.get_property_by_name(prop_name); - - assert!(prop.is_none()); - } + use super::*; + use crate::testdata; + use crate::IcalVCalendar; + + #[test] + fn get_property_test() { + let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); + let event = cal.get_principal_event(); + let prop_name = "SUMMARY"; + let prop_value: String = event.get_property_by_name(prop_name).unwrap().get_value(); + + assert_eq!( + "Festival International de Jazz de Montreal".to_string(), + prop_value + ); + } + + #[test] + fn get_property_test_lastmodified() { + let cal = + IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY_LASTMODIFIED, None).unwrap(); + let event = cal.get_principal_event(); + let prop_name = "LAST-MODIFIED"; + let prop_value: String = event.get_property_by_name(prop_name).unwrap().get_value(); + + assert_eq!("20070423T123432Z".to_string(), prop_value); + } + + #[test] + fn get_property_test_cal() { + let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); + let prop_name = "PRODID"; + let prop_value: String = cal.get_property_by_name(prop_name).unwrap().get_value(); + + assert_eq!( + "-//ABC Corporation//NONSGML My Product//EN".to_string(), + prop_value + ); + } + + #[test] + fn get_property_test_negative() { + let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); + let event = cal.get_principal_event(); + let prop_name = "DESCRIPTION"; + let prop = event.get_property_by_name(prop_name); + + assert!(prop.is_none()); + } + + #[test] + fn get_property_by_name_test() { + let cal = IcalVCalendar::from_str(testdata::TEST_EVENT_MULTIDAY, None).unwrap(); + let event = cal.get_principal_event(); + let prop_name = "NONSENSE"; + let prop = event.get_property_by_name(prop_name); + + assert!(prop.is_none()); + } } diff --git a/src/duration.rs b/src/duration.rs index 7ad1d13..d080584 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -1,177 +1,181 @@ -use std::ops::{Deref,Add}; -use std::ffi::{CStr,CString}; use crate::ical; -use std::fmt::{Error,Display,Formatter}; -use std::str::FromStr; use std::cmp::Ordering; +use std::ffi::{CStr, CString}; +use std::fmt::{Display, Error, Formatter}; +use std::ops::{Add, Deref}; +use std::str::FromStr; - -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub struct IcalDuration { - duration: ical::icaldurationtype, + duration: ical::icaldurationtype, } impl IcalDuration { - pub fn from_seconds(seconds: i32) -> IcalDuration { - let duration = unsafe { ical::icaldurationtype_from_int(seconds) }; - IcalDuration{ duration } - } - - pub fn to_seconds(&self) -> i32 { - unsafe { ical::icaldurationtype_as_int(self.duration) } - } + pub fn from_seconds(seconds: i32) -> IcalDuration { + let duration = unsafe { ical::icaldurationtype_from_int(seconds) }; + IcalDuration { duration } + } + + pub fn to_seconds(&self) -> i32 { + unsafe { ical::icaldurationtype_as_int(self.duration) } + } } impl Deref for IcalDuration { - type Target = ical::icaldurationtype; + type Target = ical::icaldurationtype; - fn deref(&self) -> &ical::icaldurationtype { - &self.duration - } + fn deref(&self) -> &ical::icaldurationtype { + &self.duration + } } impl Display for IcalDuration { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - let cstr = unsafe { - CStr::from_ptr(ical::icaldurationtype_as_ical_string(self.duration)) - }; - let string = cstr.to_string_lossy(); - write!(f, "{}", string) - } + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + let cstr = unsafe { CStr::from_ptr(ical::icaldurationtype_as_ical_string(self.duration)) }; + let string = cstr.to_string_lossy(); + write!(f, "{}", string) + } } impl FromStr for IcalDuration { - type Err = String; - - fn from_str(s: &str) -> Result { - let c_str = CString::new(s).unwrap(); - let duration = unsafe { - let duration = ical::icaldurationtype_from_string(c_str.as_ptr()); - if ical::icaldurationtype_is_null_duration(duration) == 0 { - Some(duration) - } else { - None - } - }; - if let Some(duration) = duration { - Ok(IcalDuration { duration }) - } else { - return Err(format!("Could not parse duration {}", s)); - } - } + type Err = String; + + fn from_str(s: &str) -> Result { + let c_str = CString::new(s).unwrap(); + let duration = unsafe { + let duration = ical::icaldurationtype_from_string(c_str.as_ptr()); + if ical::icaldurationtype_is_null_duration(duration) == 0 { + Some(duration) + } else { + None + } + }; + if let Some(duration) = duration { + Ok(IcalDuration { duration }) + } else { + return Err(format!("Could not parse duration {}", s)); + } + } } impl PartialEq for IcalDuration { - fn eq(&self, rhs: &IcalDuration) -> bool { - self.to_seconds() == rhs.to_seconds() - } + fn eq(&self, rhs: &IcalDuration) -> bool { + self.to_seconds() == rhs.to_seconds() + } } impl Eq for IcalDuration {} impl PartialOrd for IcalDuration { - fn partial_cmp(&self, rhs: &IcalDuration) -> Option { - Some(self.cmp(rhs)) - } + fn partial_cmp(&self, rhs: &IcalDuration) -> Option { + Some(self.cmp(rhs)) + } } impl Ord for IcalDuration { - fn cmp(&self, rhs: &IcalDuration) -> Ordering { - let left = self.to_seconds(); - let right = rhs.to_seconds(); - if left == right { - Ordering::Equal - } else if left < right { - Ordering::Less - } else { - Ordering::Greater - } - } + fn cmp(&self, rhs: &IcalDuration) -> Ordering { + let left = self.to_seconds(); + let right = rhs.to_seconds(); + if left == right { + Ordering::Equal + } else if left < right { + Ordering::Less + } else { + Ordering::Greater + } + } } impl From for IcalDuration { - fn from(duration: ical::icaldurationtype) -> IcalDuration { - IcalDuration { duration } - } + fn from(duration: ical::icaldurationtype) -> IcalDuration { + IcalDuration { duration } + } } impl From for chrono::Duration { - fn from(duration: IcalDuration) -> chrono::Duration { - chrono::Duration::seconds(i64::from(duration.to_seconds())) - } + fn from(duration: IcalDuration) -> chrono::Duration { + chrono::Duration::seconds(i64::from(duration.to_seconds())) + } } impl From for IcalDuration { - fn from(duration: chrono::Duration) -> IcalDuration { - IcalDuration::from_seconds(duration.num_seconds() as i32) - } + fn from(duration: chrono::Duration) -> IcalDuration { + IcalDuration::from_seconds(duration.num_seconds() as i32) + } } impl Add for IcalDuration { type Output = IcalDuration; fn add(self, other: IcalDuration) -> IcalDuration { - let seconds = self.to_seconds() + other.to_seconds(); - IcalDuration::from_seconds(seconds) + let seconds = self.to_seconds() + other.to_seconds(); + IcalDuration::from_seconds(seconds) } } - #[cfg(test)] mod tests { - use super::*; - - #[test] - fn test_parse() { - let duration = "PT86400S".parse::().unwrap(); - assert_eq!(IcalDuration::from_seconds(24*60*60), duration); - assert_eq!(86400, duration.to_seconds()); - } - - #[test] - fn test_parse_fail() { - let duration = "swag".parse::(); - assert!(duration.is_err()); - } - - #[test] - fn test_display() { - let duration = IcalDuration::from_seconds(5*24*60*60 + 22*60*60 + 33*60 + 33); - assert_eq!("P5DT22H33M33S", duration.to_string()); - } - - #[test] - fn test_to_chrono() { - let from_duration = IcalDuration::from_seconds(5*24*60*60 + 22*60*60 + 33*60 + 33); - let duration: chrono::Duration = from_duration.into(); - assert_eq!(chrono::Duration::seconds(5*24*60*60 + 22*60*60 + 33*60 + 33), duration); - } - - #[test] - fn test_from_chrono() { - let from_duration = chrono::Duration::seconds(5*24*60*60 + 22*60*60 + 33*60 + 33); - let duration: IcalDuration = from_duration.into(); - assert_eq!(IcalDuration::from_seconds(5*24*60*60 + 22*60*60 + 33*60 + 33), duration); - } - - #[test] - fn test_add() { - let fst = IcalDuration::from_seconds(123); - let snd = IcalDuration::from_seconds(4567); - - let sum = fst + snd; - - assert_eq!(IcalDuration::from_seconds(123+4567), sum); - } - - #[test] - fn test_cmp() { - let more = IcalDuration::from_seconds(49128); - let less = IcalDuration::from_seconds(5); - - assert!(less == less); - assert!(more == more); - assert!(less < more); - assert!(!(more < less)); - assert!(!(more == less)); - } + use super::*; + + #[test] + fn test_parse() { + let duration = "PT86400S".parse::().unwrap(); + assert_eq!(IcalDuration::from_seconds(24 * 60 * 60), duration); + assert_eq!(86400, duration.to_seconds()); + } + + #[test] + fn test_parse_fail() { + let duration = "swag".parse::(); + assert!(duration.is_err()); + } + + #[test] + fn test_display() { + let duration = IcalDuration::from_seconds(5 * 24 * 60 * 60 + 22 * 60 * 60 + 33 * 60 + 33); + assert_eq!("P5DT22H33M33S", duration.to_string()); + } + + #[test] + fn test_to_chrono() { + let from_duration = + IcalDuration::from_seconds(5 * 24 * 60 * 60 + 22 * 60 * 60 + 33 * 60 + 33); + let duration: chrono::Duration = from_duration.into(); + assert_eq!( + chrono::Duration::seconds(5 * 24 * 60 * 60 + 22 * 60 * 60 + 33 * 60 + 33), + duration + ); + } + + #[test] + fn test_from_chrono() { + let from_duration = + chrono::Duration::seconds(5 * 24 * 60 * 60 + 22 * 60 * 60 + 33 * 60 + 33); + let duration: IcalDuration = from_duration.into(); + assert_eq!( + IcalDuration::from_seconds(5 * 24 * 60 * 60 + 22 * 60 * 60 + 33 * 60 + 33), + duration + ); + } + + #[test] + fn test_add() { + let fst = IcalDuration::from_seconds(123); + let snd = IcalDuration::from_seconds(4567); + + let sum = fst + snd; + + assert_eq!(IcalDuration::from_seconds(123 + 4567), sum); + } + + #[test] + fn test_cmp() { + let more = IcalDuration::from_seconds(49128); + let less = IcalDuration::from_seconds(5); + + assert!(less == less); + assert!(more == more); + assert!(less < more); + assert!(!(more < less)); + assert!(!(more == less)); + } } diff --git a/src/errors.rs b/src/errors.rs index a4c16b0..7b66319 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -4,80 +4,86 @@ use std::fmt; #[derive(Debug)] pub struct KhError { - msg: String, - backtrace: Backtrace, - cause: Option>, + msg: String, + backtrace: Backtrace, + cause: Option>, } impl KhError { - pub fn new(msg: &str, cause: Option>) -> KhError { - KhError { - msg: msg.to_string(), - #[cfg(debug_assertions)] - backtrace: backtrace_strip_foreign(Backtrace::new()), - #[cfg(not(debug_assertions))] - backtrace: Backtrace::new_unresolved(), - cause + pub fn new(msg: &str, cause: Option>) -> KhError { + KhError { + msg: msg.to_string(), + #[cfg(debug_assertions)] + backtrace: backtrace_strip_foreign(Backtrace::new()), + #[cfg(not(debug_assertions))] + backtrace: Backtrace::new_unresolved(), + cause, + } } - } } #[cfg(debug_assertions)] fn backtrace_strip_foreign(backtrace: Backtrace) -> Backtrace { - use backtrace::BacktraceFrame; - let backtrace: Vec = backtrace.into(); - backtrace - .into_iter() - .filter(|frame| { - frame.symbols().iter().map(|symbol| { - symbol.name() - .and_then(|name| name.as_str()) - .map_or(false, |name| name.contains("khaleesi")) - }).any(|x| x) - }) - .collect::>().into() + use backtrace::BacktraceFrame; + let backtrace: Vec = backtrace.into(); + backtrace + .into_iter() + .filter(|frame| { + frame + .symbols() + .iter() + .map(|symbol| { + symbol + .name() + .and_then(|name| name.as_str()) + .map_or(false, |name| name.contains("khaleesi")) + }) + .any(|x| x) + }) + .collect::>() + .into() } impl fmt::Display for KhError { - #[cfg(debug_assertions)] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}\n{:?}", self.msg, self.backtrace) - } - #[cfg(not(debug_assertions))] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f , "{}", self.msg) - } + #[cfg(debug_assertions)] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}\n{:?}", self.msg, self.backtrace) + } + #[cfg(not(debug_assertions))] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } } impl Error for KhError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - // lol idunno - self.cause.as_ref().map(|cause| &**cause) - } + fn source(&self) -> Option<&(dyn Error + 'static)> { + // lol idunno + self.cause.as_ref().map(|cause| &**cause) + } } impl From<&str> for KhError { - fn from(e: &str) -> KhError { - KhError::new(e, None) - } + fn from(e: &str) -> KhError { + KhError::new(e, None) + } } impl From for KhError { - fn from(e: String) -> KhError { - KhError::new(&e, None) - } + fn from(e: String) -> KhError { + KhError::new(&e, None) + } } impl From for KhError { - fn from(e: std::path::StripPrefixError) -> KhError { - let description = e.to_string(); - KhError::new(&description, Some(Box::new(e))) - } + fn from(e: std::path::StripPrefixError) -> KhError { + let description = e.to_string(); + KhError::new(&description, Some(Box::new(e))) + } } impl From<::std::io::Error> for KhError { - fn from(e: ::std::io::Error) -> KhError { - let description = e.to_string(); - KhError::new(&description, Some(Box::new(e))) - } + fn from(e: ::std::io::Error) -> KhError { + let description = e.to_string(); + KhError::new(&description, Some(Box::new(e))) + } } diff --git a/src/lib.rs b/src/lib.rs index 69f5fdb..a49914c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,20 @@ #![allow(clippy::redundant_closure)] // disable "redundant closure" lint -#[cfg(test)] #[macro_use] extern crate maplit; -#[cfg(test)] #[macro_use] extern crate pretty_assertions; -#[cfg(test)] #[macro_use] extern crate indoc; +#[cfg(test)] +#[macro_use] +extern crate maplit; +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; +#[cfg(test)] +#[macro_use] +extern crate indoc; extern crate serde_derive; -#[macro_use] extern crate log; -#[macro_use] extern crate lazy_static; +#[macro_use] +extern crate log; +#[macro_use] +extern crate lazy_static; extern crate ical; // libical does some weird, non-threadsafe things in timezone methods, notably @@ -16,18 +24,18 @@ extern crate ical; // https://github.com/libical/libical/commit/0ebf2d9a7183be94991c2681c6e3f009c64cf7cc use std::sync::Mutex; lazy_static! { - static ref TZ_MUTEX: Mutex = Mutex::new(0); + static ref TZ_MUTEX: Mutex = Mutex::new(0); } -pub mod errors; pub mod component; pub mod duration; +pub mod errors; pub mod property; pub mod time; pub mod timezone; +pub mod utils; pub mod vcalendar; pub mod vevent; -pub mod utils; #[cfg(test)] pub mod testdata; @@ -43,4 +51,3 @@ pub use crate::timezone::IcalTimeZone; pub use crate::vcalendar::IcalEventIter; pub use crate::vcalendar::IcalVCalendar; pub use crate::vevent::IcalVEvent; - diff --git a/src/property.rs b/src/property.rs index 133f953..55b5914 100644 --- a/src/property.rs +++ b/src/property.rs @@ -6,55 +6,57 @@ use super::component::IcalComponent; use crate::ical; pub struct IcalProperty<'a> { - pub ptr: *mut ical::icalproperty, - _parent: &'a dyn IcalComponent, + pub ptr: *mut ical::icalproperty, + _parent: &'a dyn IcalComponent, } impl<'a> Drop for IcalProperty<'a> { - fn drop(&mut self) { - unsafe { - ical::icalproperty_free(self.ptr); + 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()) - } + 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 from_ptr(ptr: *mut ical::icalproperty, parent: &'a dyn IcalComponent) -> Self { + IcalProperty { + ptr, + _parent: parent, + } } - } - 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 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 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(&self) -> String { + unsafe { + let cstr = CStr::from_ptr(ical::icalproperty_get_value_as_string(self.ptr)); + cstr.to_string_lossy().into_owned() + } } - } - pub fn get_value_as_date(&self) -> Option { - 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) + 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 { + 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/testdata.rs b/src/testdata.rs index bbc013e..2c7b902 100644 --- a/src/testdata.rs +++ b/src/testdata.rs @@ -1,5 +1,6 @@ // from https://tools.ietf.org/html/rfc5545#section-3.6.1 -pub static TEST_EVENT_MULTIDAY_ALLDAY: &str = indoc!(" +pub static TEST_EVENT_MULTIDAY_ALLDAY: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -13,9 +14,11 @@ pub static TEST_EVENT_MULTIDAY_ALLDAY: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_EVENT_MULTIDAY: &str = indoc!(" +pub static TEST_EVENT_MULTIDAY: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -29,9 +32,11 @@ pub static TEST_EVENT_MULTIDAY: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_EVENT_MULTIDAY_LASTMODIFIED: &str = indoc!(" +pub static TEST_EVENT_MULTIDAY_LASTMODIFIED: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -46,9 +51,11 @@ pub static TEST_EVENT_MULTIDAY_LASTMODIFIED: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_DTSTART_ONLY_DATE: &str = indoc!(" +pub static TEST_DTSTART_ONLY_DATE: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -61,9 +68,11 @@ pub static TEST_DTSTART_ONLY_DATE: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_DTSTART_ONLY_DATETIME: &str = indoc!(" +pub static TEST_DTSTART_ONLY_DATETIME: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -76,9 +85,11 @@ pub static TEST_DTSTART_ONLY_DATETIME: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_EVENT_EMPTY_SUMMARY: &str = indoc!(" +pub static TEST_EVENT_EMPTY_SUMMARY: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -92,9 +103,11 @@ pub static TEST_EVENT_EMPTY_SUMMARY: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_EVENT_NO_UID: &str = indoc!(" +pub static TEST_EVENT_NO_UID: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -107,9 +120,11 @@ pub static TEST_EVENT_NO_UID: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_EVENT_NO_PRODID: &str = indoc!(" +pub static TEST_EVENT_NO_PRODID: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT @@ -122,10 +137,12 @@ pub static TEST_EVENT_NO_PRODID: &str = indoc!(" TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR -"); +" +); // from https://tools.ietf.org/html/rfc5545#section-4 -pub static TEST_EVENT_ONE_MEETING: &str = indoc!(r" +pub static TEST_EVENT_ONE_MEETING: &str = indoc!( + r" BEGIN:VCALENDAR METHOD:xyz VERSION:2.0 @@ -148,9 +165,11 @@ pub static TEST_EVENT_ONE_MEETING: &str = indoc!(r" END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_EVENT_RECUR: &str = indoc!(" +pub static TEST_EVENT_RECUR: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -163,10 +182,11 @@ pub static TEST_EVENT_RECUR: &str = indoc!(" END:VEVENT END:VCALENDAR -"); +" +); - -pub static TEST_EVENT_WITH_TIMEZONE_COMPONENT: &str = indoc!(" +pub static TEST_EVENT_WITH_TIMEZONE_COMPONENT: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//PIMUTILS.ORG//NONSGML khal / icalendar //EN @@ -195,9 +215,11 @@ pub static TEST_EVENT_WITH_TIMEZONE_COMPONENT: &str = indoc!(" LOCATION:Some Location END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_MULTIPLE_EVENTS: &str = indoc!(" +pub static TEST_MULTIPLE_EVENTS: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -214,9 +236,11 @@ pub static TEST_MULTIPLE_EVENTS: &str = indoc!(" SUMMARY:Second Event END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_NO_DTSTART: &str = indoc!(" +pub static TEST_NO_DTSTART: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -225,9 +249,11 @@ pub static TEST_NO_DTSTART: &str = indoc!(" DTSTAMP:20070423T123432Z END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_NO_SUMMARY: &str = indoc!(" +pub static TEST_NO_SUMMARY: &str = indoc!( + " BEGIN:VCALENDAR VERSION:2.0 PRODID:-//ABC Corporation//NONSGML My Product//EN @@ -237,17 +263,21 @@ pub static TEST_NO_SUMMARY: &str = indoc!(" DTSTART;VALUE=DATE:20070628 END:VEVENT END:VCALENDAR -"); +" +); -pub static TEST_BARE_EVENT: &str = indoc!(" +pub static TEST_BARE_EVENT: &str = indoc!( + " BEGIN:VEVENT UID:uid4 DTSTAMP:20070423T123432Z DTSTART;VALUE=DATE:20070628 END:VEVENT -"); +" +); -pub static TEST_EVENT_WITH_X_LIC_ERROR: &str = indoc!(" +pub static TEST_EVENT_WITH_X_LIC_ERROR: &str = indoc!( + " BEGIN:VCALENDAR PRODID:CommuniGate Pro 6.2.5 VERSION:2.0 @@ -265,10 +295,10 @@ pub static TEST_EVENT_WITH_X_LIC_ERROR: &str = indoc!(" X-LIC-ERROR:No value for LOCATION property. Removing entire property: END:VEVENT END:VCALENDAR -"); +" +); -use chrono::{Utc,DateTime,TimeZone}; +use chrono::{DateTime, TimeZone, Utc}; lazy_static! { - pub static ref NOW_TEST: DateTime = Utc.ymd(2013, 01, 01).and_hms(1, 2, 3); + pub static ref NOW_TEST: DateTime = Utc.ymd(2013, 01, 01).and_hms(1, 2, 3); } - diff --git a/src/testutils.rs b/src/testutils.rs index 0b1da83..1cc3fd4 100644 --- a/src/testutils.rs +++ b/src/testutils.rs @@ -1,15 +1,17 @@ +use assert_fs::fixture::{ChildPath, FixtureError}; use assert_fs::prelude::*; use assert_fs::TempDir; -use assert_fs::fixture::{ChildPath, FixtureError}; -use std::path::{PathBuf,Path}; use std::fs; +use std::path::{Path, PathBuf}; pub fn path_to(artifact: &str) -> PathBuf { - [env!("CARGO_MANIFEST_DIR"), "testdata", artifact].iter().collect() + [env!("CARGO_MANIFEST_DIR"), "testdata", artifact] + .iter() + .collect() } pub fn touch_testfile(testdir: &TempDir, relative_path: &Path) -> Result { - let testfile = testdir.child(relative_path); - testfile.touch()?; - Ok(testfile) + let testfile = testdir.child(relative_path); + testfile.touch()?; + Ok(testfile) } diff --git a/src/time.rs b/src/time.rs index 51fc99b..837fcc8 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,359 +1,357 @@ -use std::ops::{Add,Deref}; -use std::ffi::{CStr,CString}; -use chrono::{Date,DateTime,TimeZone,Utc,Local}; -use crate::ical; -use crate::utils::dateutil; -use super::IcalTimeZone; use super::IcalDuration; +use super::IcalTimeZone; use super::TZ_MUTEX; -use std::fmt::{Error,Display,Formatter}; +use crate::ical; +use crate::utils::dateutil; +use chrono::{Date, DateTime, Local, TimeZone, Utc}; +use std::ffi::{CStr, CString}; +use std::fmt::{Display, Error, Formatter}; +use std::ops::{Add, Deref}; use std::str::FromStr; -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub struct IcalTime { - time: ical::icaltimetype, + time: ical::icaltimetype, } impl IcalTime { - pub fn utc() -> Self { - dateutil::now().into() - } - - pub fn local() -> Self { - dateutil::now().with_timezone(&Local).into() - } - - pub fn floating_ymd(year: i32, month: i32, day: i32) -> Self { - let time = ical::icaltimetype{ - year, month, day, - hour: 0, minute: 0, second: 0, - is_date: 1, - is_daylight: 0, - zone: ::std::ptr::null(), - }; - IcalTime{ time } - } - - pub fn and_hms(&self, hour: i32, minute: i32, second: i32) -> Self { - let mut time = self.time; - time.hour = hour; - time.minute = minute; - time.second = second; - time.is_date = 0; - - IcalTime{ time } - } - - pub fn from_timestamp(timestamp: i64) -> Self { - let _lock = TZ_MUTEX.lock(); - let utc = IcalTimeZone::utc(); - let is_date = 0; - let time = unsafe { - ical::icaltime_from_timet_with_zone(timestamp, is_date, *utc) - }; - IcalTime{ time } - } - - pub fn timestamp(&self) -> i64 { - let _lock = TZ_MUTEX.lock(); - unsafe { ical::icaltime_as_timet_with_zone(self.time, self.time.zone) } - } - - pub fn is_date(&self) -> bool { - self.time.is_date != 0 - } - - pub fn as_date(&self) -> IcalTime { - let mut time = self.time; - time.is_date = 1; - IcalTime{ time } - } - - pub fn get_timezone(&self) -> Option { - if self.time.zone.is_null() { - return None; - } - let tz_ptr = unsafe { - ical::icaltime_get_timezone(self.time) - }; - Some(IcalTimeZone::from_ptr_copy(tz_ptr)) - } - - pub fn with_timezone(&self, timezone: &IcalTimeZone) -> IcalTime { - let _lock = TZ_MUTEX.lock(); - let mut time = unsafe { - ical::icaltime_convert_to_zone(self.time, **timezone) - }; - //icaltime_convert_to_zone does nothing if is_date == 1 - time.zone = **timezone; - IcalTime { time } - } - - pub fn pred(&self) -> IcalTime { - let mut time = self.time; - time.day -= 1; - let time = unsafe { ical::icaltime_normalize(time) }; - IcalTime{ time } - } - - pub fn succ(&self) -> IcalTime { - let mut time = self.time; - time.day += 1; - let time = unsafe { ical::icaltime_normalize(time) }; - IcalTime{ time } - } + pub fn utc() -> Self { + dateutil::now().into() + } + + pub fn local() -> Self { + dateutil::now().with_timezone(&Local).into() + } + + pub fn floating_ymd(year: i32, month: i32, day: i32) -> Self { + let time = ical::icaltimetype { + year, + month, + day, + hour: 0, + minute: 0, + second: 0, + is_date: 1, + is_daylight: 0, + zone: ::std::ptr::null(), + }; + IcalTime { time } + } + + pub fn and_hms(&self, hour: i32, minute: i32, second: i32) -> Self { + let mut time = self.time; + time.hour = hour; + time.minute = minute; + time.second = second; + time.is_date = 0; + + IcalTime { time } + } + + pub fn from_timestamp(timestamp: i64) -> Self { + let _lock = TZ_MUTEX.lock(); + let utc = IcalTimeZone::utc(); + let is_date = 0; + let time = unsafe { ical::icaltime_from_timet_with_zone(timestamp, is_date, *utc) }; + IcalTime { time } + } + + pub fn timestamp(&self) -> i64 { + let _lock = TZ_MUTEX.lock(); + unsafe { ical::icaltime_as_timet_with_zone(self.time, self.time.zone) } + } + + pub fn is_date(&self) -> bool { + self.time.is_date != 0 + } + + pub fn as_date(&self) -> IcalTime { + let mut time = self.time; + time.is_date = 1; + IcalTime { time } + } + + pub fn get_timezone(&self) -> Option { + if self.time.zone.is_null() { + return None; + } + let tz_ptr = unsafe { ical::icaltime_get_timezone(self.time) }; + Some(IcalTimeZone::from_ptr_copy(tz_ptr)) + } + + pub fn with_timezone(&self, timezone: &IcalTimeZone) -> IcalTime { + let _lock = TZ_MUTEX.lock(); + let mut time = unsafe { ical::icaltime_convert_to_zone(self.time, **timezone) }; + //icaltime_convert_to_zone does nothing if is_date == 1 + time.zone = **timezone; + IcalTime { time } + } + + pub fn pred(&self) -> IcalTime { + let mut time = self.time; + time.day -= 1; + let time = unsafe { ical::icaltime_normalize(time) }; + IcalTime { time } + } + + pub fn succ(&self) -> IcalTime { + let mut time = self.time; + time.day += 1; + let time = unsafe { ical::icaltime_normalize(time) }; + IcalTime { time } + } } impl Deref for IcalTime { - type Target = ical::icaltimetype; + type Target = ical::icaltimetype; - fn deref(&self) -> &ical::icaltimetype { - &self.time - } + fn deref(&self) -> &ical::icaltimetype { + &self.time + } } impl Display for IcalTime { - fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { - let cstr = unsafe { - CStr::from_ptr(ical::icaltime_as_ical_string(self.time)) - }; - let string = cstr.to_string_lossy(); - write!(f, "{}", string) - } + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + let cstr = unsafe { CStr::from_ptr(ical::icaltime_as_ical_string(self.time)) }; + let string = cstr.to_string_lossy(); + write!(f, "{}", string) + } } impl FromStr for IcalTime { - type Err = String; - - fn from_str(s: &str) -> Result { - unsafe { - let c_str = CString::new(s).unwrap(); - let time = ical::icaltime_from_string(c_str.as_ptr()); - if ical::icaltime_is_null_time(time) == 0 { - Ok(IcalTime { time }) - } else { - Err(format!("Could not parse time {}", s)) - } - } - } + type Err = String; + + fn from_str(s: &str) -> Result { + unsafe { + let c_str = CString::new(s).unwrap(); + let time = ical::icaltime_from_string(c_str.as_ptr()); + if ical::icaltime_is_null_time(time) == 0 { + Ok(IcalTime { time }) + } else { + Err(format!("Could not parse time {}", s)) + } + } + } } impl PartialEq for IcalTime { - fn eq(&self, rhs: &IcalTime) -> bool { - let _lock = TZ_MUTEX.lock(); - let cmp = unsafe { ical::icaltime_compare(self.time, rhs.time) }; - cmp == 0 - } + fn eq(&self, rhs: &IcalTime) -> bool { + let _lock = TZ_MUTEX.lock(); + let cmp = unsafe { ical::icaltime_compare(self.time, rhs.time) }; + cmp == 0 + } } impl Eq for IcalTime {} impl From for IcalTime { - fn from(time: ical::icaltimetype) -> IcalTime { - IcalTime { time } - } + fn from(time: ical::icaltimetype) -> IcalTime { + IcalTime { time } + } } impl Add for IcalTime { type Output = IcalTime; fn add(self, other: IcalDuration) -> IcalTime { - let time = unsafe { ical::icaltime_add(self.time, *other) }; - IcalTime { time } + let time = unsafe { ical::icaltime_add(self.time, *other) }; + IcalTime { time } } } impl + Clone> From<&T> for IcalTime { - fn from(time: &T) -> IcalTime { - time.clone().into() - } + fn from(time: &T) -> IcalTime { + time.clone().into() + } } impl From> for IcalTime { - fn from(time: DateTime) -> IcalTime { - let timestamp = time.timestamp(); - let local = IcalTimeZone::local(); - IcalTime::from_timestamp(timestamp).with_timezone(&local) - } + fn from(time: DateTime) -> IcalTime { + let timestamp = time.timestamp(); + let local = IcalTimeZone::local(); + IcalTime::from_timestamp(timestamp).with_timezone(&local) + } } impl From> for IcalTime { - fn from(time: DateTime) -> IcalTime { - let timestamp = time.timestamp(); - IcalTime::from_timestamp(timestamp) - } + fn from(time: DateTime) -> IcalTime { + let timestamp = time.timestamp(); + IcalTime::from_timestamp(timestamp) + } } impl From> for IcalTime { - fn from(date: Date) -> IcalTime { - let timestamp = date.with_timezone(&Utc).and_hms(0, 0, 0).timestamp(); - let timezone = IcalTimeZone::local(); - IcalTime::from_timestamp(timestamp).with_timezone(&timezone).as_date() - } + fn from(date: Date) -> IcalTime { + let timestamp = date.with_timezone(&Utc).and_hms(0, 0, 0).timestamp(); + let timezone = IcalTimeZone::local(); + IcalTime::from_timestamp(timestamp) + .with_timezone(&timezone) + .as_date() + } } impl From> for IcalTime { - fn from(date: Date) -> IcalTime { - let timestamp = date.and_hms(0, 0, 0).timestamp(); - IcalTime::from_timestamp(timestamp).as_date() - } + fn from(date: Date) -> IcalTime { + let timestamp = date.and_hms(0, 0, 0).timestamp(); + IcalTime::from_timestamp(timestamp).as_date() + } } impl From for Date { - fn from(time: IcalTime) -> Date { - Local.timestamp(time.timestamp(), 0).date() - } + fn from(time: IcalTime) -> Date { + Local.timestamp(time.timestamp(), 0).date() + } } impl From for DateTime { - fn from(time: IcalTime) -> DateTime { - Local.timestamp(time.timestamp(), 0) - } + fn from(time: IcalTime) -> DateTime { + Local.timestamp(time.timestamp(), 0) + } } impl From for Date { - fn from(time: IcalTime) -> Date { - Utc.timestamp(time.timestamp(), 0).date() - } + fn from(time: IcalTime) -> Date { + Utc.timestamp(time.timestamp(), 0).date() + } } impl From for DateTime { - fn from(time: IcalTime) -> DateTime { - Utc.timestamp(time.timestamp(), 0) - } + fn from(time: IcalTime) -> DateTime { + Utc.timestamp(time.timestamp(), 0) + } } #[cfg(test)] mod tests { - use super::*; - - use crate::testdata; - - #[test] - fn test_now() { - let now = IcalTime::utc(); - - assert_eq!("20130101T010203Z", now.to_string()); - assert_eq!(1357002123, now.timestamp()); - } - - #[test] - fn test_from_local() { - let local_time = Local.ymd(2014, 01, 01).and_hms(01, 02, 03); - let time = IcalTime::from(local_time); - - assert_eq!("Europe/Berlin", time.get_timezone().unwrap().get_name()); - assert_eq!(1388534523, time.timestamp()); - } - - #[test] - fn test_local() { - let time = IcalTime::local(); - assert_eq!("20130101T020203", time.to_string()); - } - - #[test] - fn test_parse() { - let time = "20130101T010203Z".parse::().unwrap(); - assert_eq!("20130101T010203Z", time.to_string()); - } - - #[test] - fn test_parse_negative() { - let time = "201XXX01T010203Z".parse::(); - assert!(time.is_err()); - } - - #[test] - fn test_with_timezone() { - let utc = IcalTime::utc(); - let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); - - let time = utc.with_timezone(&tz); - - assert_eq!("US/Eastern", time.get_timezone().unwrap().get_name()); - assert_eq!("20121231T200203", time.to_string()); - assert_eq!(1357002123, time.timestamp()); - } - - #[test] - fn test_get_timezone_negative() { - let time = IcalTime::floating_ymd(2018, 02, 03); - assert!(time.get_timezone().is_none()); - } - - #[test] - fn test_from_local_date() { - let local_date = Local.ymd(2014, 01, 01); - let time = IcalTime::from(local_date); - - assert_eq!("Europe/Berlin", time.get_timezone().unwrap().get_name()); - assert_eq!("20140101", time.to_string()); - } - - #[test] - fn test_from_utc_date() { - let utc_date = Utc.ymd(2014, 01, 01); - let time = IcalTime::from(utc_date); - - assert_eq!("UTC", time.get_timezone().unwrap().get_name()); - assert_eq!("20140101", time.to_string()); - } - - #[test] - fn test_into_utc_date() { - let time = IcalTimeZone::utc().ymd(2014, 02, 02); - let date: Date = time.into(); - assert_eq!(Utc.ymd(2014, 02, 02), date); - } - - #[test] - fn test_into_utc_datetime() { - let time = IcalTimeZone::utc().ymd(2014, 02, 02).and_hms(13, 37, 00); - let datetime: DateTime = time.into(); - assert_eq!(Utc.ymd(2014, 02, 02).and_hms(13, 37, 00), datetime); - } - - #[test] - fn test_into_local_date() { - let time = IcalTimeZone::local().ymd(2014, 02, 02); - let date: Date = time.into(); - assert_eq!(Local.ymd(2014, 02, 02), date); - } - - #[test] - fn test_into_local_datetime() { - let time = IcalTimeZone::local().ymd(2014, 02, 02).and_hms(13, 37, 00); - let datetime: DateTime = time.into(); - assert_eq!(Local.ymd(2014, 02, 02).and_hms(13, 37, 00), datetime); - } - - #[test] - fn test_into_local_datetime_utc() { - let time = IcalTimeZone::utc().ymd(2014, 02, 02).and_hms(13, 37, 00); - let datetime: DateTime = time.into(); - assert_eq!(Local.ymd(2014, 02, 02).and_hms(14, 37, 00), datetime); - } - - #[test] - fn test_add() { - let now = IcalTime::utc(); - let duration = IcalDuration::from_seconds(123); - - let sum = now + duration; - - assert_eq!(1357002123 + 123, sum.timestamp()); - } - - #[test] - fn test_pred() { - let time = IcalTime::utc(); - assert_eq!("20121231T010203Z", time.pred().to_string()); - } - - #[test] - fn test_succ() { - let time = IcalTime::utc(); - assert_eq!("20130102T010203Z", time.succ().to_string()); - } + use super::*; + + use crate::testdata; + + #[test] + fn test_now() { + let now = IcalTime::utc(); + + assert_eq!("20130101T010203Z", now.to_string()); + assert_eq!(1357002123, now.timestamp()); + } + + #[test] + fn test_from_local() { + let local_time = Local.ymd(2014, 01, 01).and_hms(01, 02, 03); + let time = IcalTime::from(local_time); + + assert_eq!("Europe/Berlin", time.get_timezone().unwrap().get_name()); + assert_eq!(1388534523, time.timestamp()); + } + + #[test] + fn test_local() { + let time = IcalTime::local(); + assert_eq!("20130101T020203", time.to_string()); + } + + #[test] + fn test_parse() { + let time = "20130101T010203Z".parse::().unwrap(); + assert_eq!("20130101T010203Z", time.to_string()); + } + + #[test] + fn test_parse_negative() { + let time = "201XXX01T010203Z".parse::(); + assert!(time.is_err()); + } + + #[test] + fn test_with_timezone() { + let utc = IcalTime::utc(); + let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); + + let time = utc.with_timezone(&tz); + + assert_eq!("US/Eastern", time.get_timezone().unwrap().get_name()); + assert_eq!("20121231T200203", time.to_string()); + assert_eq!(1357002123, time.timestamp()); + } + + #[test] + fn test_get_timezone_negative() { + let time = IcalTime::floating_ymd(2018, 02, 03); + assert!(time.get_timezone().is_none()); + } + + #[test] + fn test_from_local_date() { + let local_date = Local.ymd(2014, 01, 01); + let time = IcalTime::from(local_date); + + assert_eq!("Europe/Berlin", time.get_timezone().unwrap().get_name()); + assert_eq!("20140101", time.to_string()); + } + + #[test] + fn test_from_utc_date() { + let utc_date = Utc.ymd(2014, 01, 01); + let time = IcalTime::from(utc_date); + + assert_eq!("UTC", time.get_timezone().unwrap().get_name()); + assert_eq!("20140101", time.to_string()); + } + + #[test] + fn test_into_utc_date() { + let time = IcalTimeZone::utc().ymd(2014, 02, 02); + let date: Date = time.into(); + assert_eq!(Utc.ymd(2014, 02, 02), date); + } + + #[test] + fn test_into_utc_datetime() { + let time = IcalTimeZone::utc().ymd(2014, 02, 02).and_hms(13, 37, 00); + let datetime: DateTime = time.into(); + assert_eq!(Utc.ymd(2014, 02, 02).and_hms(13, 37, 00), datetime); + } + + #[test] + fn test_into_local_date() { + let time = IcalTimeZone::local().ymd(2014, 02, 02); + let date: Date = time.into(); + assert_eq!(Local.ymd(2014, 02, 02), date); + } + + #[test] + fn test_into_local_datetime() { + let time = IcalTimeZone::local().ymd(2014, 02, 02).and_hms(13, 37, 00); + let datetime: DateTime = time.into(); + assert_eq!(Local.ymd(2014, 02, 02).and_hms(13, 37, 00), datetime); + } + + #[test] + fn test_into_local_datetime_utc() { + let time = IcalTimeZone::utc().ymd(2014, 02, 02).and_hms(13, 37, 00); + let datetime: DateTime = time.into(); + assert_eq!(Local.ymd(2014, 02, 02).and_hms(14, 37, 00), datetime); + } + + #[test] + fn test_add() { + let now = IcalTime::utc(); + let duration = IcalDuration::from_seconds(123); + + let sum = now + duration; + + assert_eq!(1357002123 + 123, sum.timestamp()); + } + + #[test] + fn test_pred() { + let time = IcalTime::utc(); + assert_eq!("20121231T010203Z", time.pred().to_string()); + } + + #[test] + fn test_succ() { + let time = IcalTime::utc(); + assert_eq!("20130102T010203Z", time.succ().to_string()); + } } diff --git a/src/timezone.rs b/src/timezone.rs index 0d76e50..6dc5a80 100644 --- a/src/timezone.rs +++ b/src/timezone.rs @@ -1,134 +1,130 @@ -use std::ops::Deref; -use std::ffi::{CString,CStr}; use crate::ical; +use std::ffi::{CStr, CString}; +use std::ops::Deref; -use crate::utils::dateutil; use super::IcalTime; +use crate::utils::dateutil; pub struct IcalTimeZone { - timezone: *mut ical::icaltimezone, + timezone: *mut ical::icaltimezone, } impl Deref for IcalTimeZone { - type Target = *mut ical::icaltimezone; + type Target = *mut ical::icaltimezone; - fn deref(&self) -> &*mut ical::icaltimezone { - &self.timezone - } + fn deref(&self) -> &*mut ical::icaltimezone { + &self.timezone + } } - impl IcalTimeZone { - pub fn from_ptr_copy(ptr: *const ical::icaltimezone) -> Self { - let timezone = unsafe { - // unsafe, but we know icaltimezone_copy doesn't actually mutate - ical::icaltimezone_copy(ptr as *mut ical::icaltimezone) - }; - IcalTimeZone{ timezone } - } - - pub fn from_name(tz_name: &str) -> Result { - let tz_cstr = CString::new(tz_name).unwrap(); - let builtin = unsafe { ical::icaltimezone_get_builtin_timezone(tz_cstr.as_ptr()) }; - if !builtin.is_null() { - // need to copy here to guarantee we don't touch the builtin zones - let timezone = unsafe { ical::icaltimezone_copy(builtin) }; - Ok(IcalTimeZone{ timezone }) - } else { - Err(format!("Unknown timezone: {}", tz_name)) + pub fn from_ptr_copy(ptr: *const ical::icaltimezone) -> Self { + let timezone = unsafe { + // unsafe, but we know icaltimezone_copy doesn't actually mutate + ical::icaltimezone_copy(ptr as *mut ical::icaltimezone) + }; + IcalTimeZone { timezone } + } + + pub fn from_name(tz_name: &str) -> Result { + let tz_cstr = CString::new(tz_name).unwrap(); + let builtin = unsafe { ical::icaltimezone_get_builtin_timezone(tz_cstr.as_ptr()) }; + if !builtin.is_null() { + // need to copy here to guarantee we don't touch the builtin zones + let timezone = unsafe { ical::icaltimezone_copy(builtin) }; + Ok(IcalTimeZone { timezone }) + } else { + Err(format!("Unknown timezone: {}", tz_name)) + } } - } - - pub fn local() -> Self { - let tz_name = dateutil::find_local_timezone(); - IcalTimeZone::from_name(&tz_name).unwrap() - } - - pub fn utc() -> Self { - let timezone = unsafe { ical::icaltimezone_get_utc_timezone() }; - IcalTimeZone{ timezone } - } - - pub fn get_name(&self) -> String { - unsafe { - let name = ical::icaltimezone_get_display_name(self.timezone); - CStr::from_ptr(name).to_string_lossy().trim().to_string() + + pub fn local() -> Self { + let tz_name = dateutil::find_local_timezone(); + IcalTimeZone::from_name(&tz_name).unwrap() + } + + pub fn utc() -> Self { + let timezone = unsafe { ical::icaltimezone_get_utc_timezone() }; + IcalTimeZone { timezone } + } + + pub fn get_name(&self) -> String { + unsafe { + let name = ical::icaltimezone_get_display_name(self.timezone); + CStr::from_ptr(name).to_string_lossy().trim().to_string() + } } - } - pub fn get_offset_at_time(&self, time: &IcalTime) -> i32 { - let mut icaltime = **time; - let mut is_dst = 0; - unsafe { - ical::icaltimezone_get_utc_offset(self.timezone, &mut icaltime , &mut is_dst) + pub fn get_offset_at_time(&self, time: &IcalTime) -> i32 { + let mut icaltime = **time; + let mut is_dst = 0; + unsafe { ical::icaltimezone_get_utc_offset(self.timezone, &mut icaltime, &mut is_dst) } } - } - pub fn ymd(&self, year: i32, month: i32, day: i32) -> IcalTime { - IcalTime::floating_ymd(year, month, day).with_timezone(&self) - } + pub fn ymd(&self, year: i32, month: i32, day: i32) -> IcalTime { + IcalTime::floating_ymd(year, month, day).with_timezone(&self) + } } #[cfg(test)] mod tests { - use super::*; - - use crate::testdata; - - #[test] - fn test_utc() { - let tz = IcalTimeZone::utc(); - assert_eq!("UTC", tz.get_name()); - } - - #[test] - fn test_get_offset_utc() { - let tz = IcalTimeZone::utc(); - let time = IcalTime::utc(); - - let offset = tz.get_offset_at_time(&time); - - assert_eq!(0, offset); - } - - #[test] - fn test_ymd() { - let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); - let date = tz.ymd(2014,3,1); - assert_eq!(IcalTime::floating_ymd(2014,3,1), date); - assert_eq!(1393650000, date.timestamp()); - assert_eq!("US/Eastern", date.get_timezone().unwrap().get_name()); - } - - #[test] - fn test_ymd_and_hms() { - let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); - let date = tz.ymd(2014,3,1).and_hms(17, 20, 0); - assert_eq!(IcalTime::floating_ymd(2014,3,1).and_hms(17, 20, 0), date); - assert_eq!(1393712400, date.timestamp()); - assert_eq!("US/Eastern", date.get_timezone().unwrap().get_name()); - } - - #[test] - fn test_from_name() { - let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); - assert_eq!("US/Eastern", tz.get_name()); - } - - #[test] - fn test_from_name_fail() { - let tz = IcalTimeZone::from_name("lulz"); - assert!(tz.is_err()); - } - - #[test] - fn test_get_offset_eastern() { - let time = IcalTime::utc(); - let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); - - let offset = tz.get_offset_at_time(&time); - - assert_eq!(-5*60*60, offset); - } + use super::*; + + use crate::testdata; + + #[test] + fn test_utc() { + let tz = IcalTimeZone::utc(); + assert_eq!("UTC", tz.get_name()); + } + + #[test] + fn test_get_offset_utc() { + let tz = IcalTimeZone::utc(); + let time = IcalTime::utc(); + + let offset = tz.get_offset_at_time(&time); + + assert_eq!(0, offset); + } + + #[test] + fn test_ymd() { + let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); + let date = tz.ymd(2014, 3, 1); + assert_eq!(IcalTime::floating_ymd(2014, 3, 1), date); + assert_eq!(1393650000, date.timestamp()); + assert_eq!("US/Eastern", date.get_timezone().unwrap().get_name()); + } + + #[test] + fn test_ymd_and_hms() { + let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); + let date = tz.ymd(2014, 3, 1).and_hms(17, 20, 0); + assert_eq!(IcalTime::floating_ymd(2014, 3, 1).and_hms(17, 20, 0), date); + assert_eq!(1393712400, date.timestamp()); + assert_eq!("US/Eastern", date.get_timezone().unwrap().get_name()); + } + + #[test] + fn test_from_name() { + let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); + assert_eq!("US/Eastern", tz.get_name()); + } + + #[test] + fn test_from_name_fail() { + let tz = IcalTimeZone::from_name("lulz"); + assert!(tz.is_err()); + } + + #[test] + fn test_get_offset_eastern() { + let time = IcalTime::utc(); + let tz = IcalTimeZone::from_name("US/Eastern").unwrap(); + let offset = tz.get_offset_at_time(&time); + + assert_eq!(-5 * 60 * 60, offset); + } } diff --git a/src/utils/dateutil.rs b/src/utils/dateutil.rs index eca9a34..3d195b0 100644 --- a/src/utils/dateutil.rs +++ b/src/utils/dateutil.rs @@ -1,154 +1,158 @@ use chrono::*; -use std::path::PathBuf; -use std::env; use crate::utils::fileutil; +use std::env; +use std::path::PathBuf; pub fn date_from_str(date_str: &str) -> ParseResult> { - if date_str == "today" || date_str == "now" { - return Ok(Local::now().date()); - } - let naive_date = &NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?; - Ok(Local.from_local_date(naive_date).unwrap()) + if date_str == "today" || date_str == "now" { + return Ok(Local::now().date()); + } + let naive_date = &NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?; + Ok(Local.from_local_date(naive_date).unwrap()) } pub fn datetime_from_str(datetime_str: &str) -> ParseResult> { - if datetime_str == "now" { - return Ok(Local::now()); - } - let naive_datetime = &NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%dT%H:%M")?; - Ok(Local.from_local_datetime(naive_datetime).unwrap()) + if datetime_str == "now" { + return Ok(Local::now()); + } + let naive_datetime = &NaiveDateTime::parse_from_str(datetime_str, "%Y-%m-%dT%H:%M")?; + Ok(Local.from_local_datetime(naive_datetime).unwrap()) } -pub fn week_from_str_begin(date_str: &str) -> Result,String> { - let now = Local::now(); - if date_str == "toweek" || date_str == "thisweek" { - return Ok(Local.isoywd(now.year(), now.iso_week().week(), Weekday::Mon)); - } - if let Ok(date) = &NaiveDate::parse_from_str(&format!("{}-1", date_str), "%G-W%V-%u") { - return Ok(Local.from_local_date(date).unwrap()); - } - if let Ok(date) = &NaiveDate::parse_from_str(&format!("{}-{}-1", now.year(), date_str), "%G-W%V-%u") { - return Ok(Local.from_local_date(date).unwrap()); - } - Err("Could not parse '{}' as week".to_string()) +pub fn week_from_str_begin(date_str: &str) -> Result, String> { + let now = Local::now(); + if date_str == "toweek" || date_str == "thisweek" { + return Ok(Local.isoywd(now.year(), now.iso_week().week(), Weekday::Mon)); + } + if let Ok(date) = &NaiveDate::parse_from_str(&format!("{}-1", date_str), "%G-W%V-%u") { + return Ok(Local.from_local_date(date).unwrap()); + } + if let Ok(date) = + &NaiveDate::parse_from_str(&format!("{}-{}-1", now.year(), date_str), "%G-W%V-%u") + { + return Ok(Local.from_local_date(date).unwrap()); + } + Err("Could not parse '{}' as week".to_string()) } pub fn find_local_timezone() -> String { - if let Ok(candidate) = env::var("TZ") { - return candidate; - } - if let Ok(candidate) = fileutil::read_file_to_string(&PathBuf::from("/etc/timezone")) { - return candidate.trim().to_owned(); - } - if let Ok(candidate) = fileutil::read_file_to_string(&PathBuf::from("/etc/localtime")) { - return candidate.trim().to_owned(); - } - "UTC".to_owned() + if let Ok(candidate) = env::var("TZ") { + return candidate; + } + if let Ok(candidate) = fileutil::read_file_to_string(&PathBuf::from("/etc/timezone")) { + return candidate.trim().to_owned(); + } + if let Ok(candidate) = fileutil::read_file_to_string(&PathBuf::from("/etc/localtime")) { + return candidate.trim().to_owned(); + } + "UTC".to_owned() } #[cfg(not(test))] pub fn now() -> DateTime { - Utc::now() + Utc::now() } #[cfg(test)] pub fn now() -> DateTime { - use crate::testdata; - *testdata::NOW_TEST + use crate::testdata; + *testdata::NOW_TEST } -pub fn week_from_str_end(date_str: &str) -> Result,String> { - let now = Local::now(); - if date_str == "toweek" || date_str == "thisweek" { - return Ok(Local.isoywd(now.year(), now.iso_week().week(), Weekday::Sun)); - } - if let Ok(date) = &NaiveDate::parse_from_str(&format!("{}-7", date_str), "%G-W%V-%u") { - return Ok(Local.from_local_date(date).unwrap()); - } - if let Ok(date) = &NaiveDate::parse_from_str(&format!("{}-{}-7", now.year(), date_str), "%G-W%V-%u") { - return Ok(Local.from_local_date(date).unwrap()); - } - Err("Could not parse '{}' as week".to_string()) +pub fn week_from_str_end(date_str: &str) -> Result, String> { + let now = Local::now(); + if date_str == "toweek" || date_str == "thisweek" { + return Ok(Local.isoywd(now.year(), now.iso_week().week(), Weekday::Sun)); + } + if let Ok(date) = &NaiveDate::parse_from_str(&fo