From 7fc64d219c211c4fda2bf6b2b7af02ceefeb406a Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sat, 26 Jul 2025 13:32:28 +0200 Subject: [PATCH] outsource some more ical logic to ical-rs fork --- Cargo.lock | 5 ++- crates/ical/src/duration.rs | 53 ------------------------------ crates/ical/src/icalendar/event.rs | 26 ++++----------- crates/ical/src/lib.rs | 3 -- crates/ical/src/timestamp.rs | 30 ++++++++--------- crates/ical/src/timezone.rs | 14 ++++---- 6 files changed, 33 insertions(+), 98 deletions(-) delete mode 100644 crates/ical/src/duration.rs diff --git a/Cargo.lock b/Cargo.lock index 0c69298..e410834 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1598,9 +1598,12 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs#c5fa2217af23ba27ba80295a2c0eb922f08f6c97" +source = "git+https://github.com/lennart-k/ical-rs#c1a88dd68d9de79d6f71d6729aa4cb6e587b0d62" dependencies = [ + "chrono", "chrono-tz", + "lazy_static", + "regex", "serde", "thiserror 2.0.12", ] diff --git a/crates/ical/src/duration.rs b/crates/ical/src/duration.rs deleted file mode 100644 index 7758d9a..0000000 --- a/crates/ical/src/duration.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::CalDateTimeError; -use chrono::Duration; -use lazy_static::lazy_static; - -lazy_static! { - static ref RE_DURATION: regex::Regex = regex::Regex::new(r"^(?[+-])?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$").unwrap(); -} - -pub fn parse_duration(string: &str) -> Result { - let captures = RE_DURATION - .captures(string) - .ok_or(CalDateTimeError::InvalidDurationFormat(string.to_string()))?; - - let mut duration = Duration::zero(); - if let Some(weeks) = captures.name("W") { - duration += Duration::weeks(weeks.as_str().parse().unwrap()); - } - if let Some(days) = captures.name("D") { - duration += Duration::days(days.as_str().parse().unwrap()); - } - if let Some(hours) = captures.name("H") { - duration += Duration::hours(hours.as_str().parse().unwrap()); - } - if let Some(minutes) = captures.name("M") { - duration += Duration::minutes(minutes.as_str().parse().unwrap()); - } - if let Some(seconds) = captures.name("S") { - duration += Duration::seconds(seconds.as_str().parse().unwrap()); - } - if let Some(sign) = captures.name("sign") { - if sign.as_str() == "-" { - duration = -duration; - } - } - - Ok(duration) -} - -#[cfg(test)] -mod tests { - use chrono::Duration; - - use crate::parse_duration; - - #[test] - fn test_parse_duration() { - assert_eq!(parse_duration("P12W").unwrap(), Duration::weeks(12)); - assert_eq!(parse_duration("P12D").unwrap(), Duration::days(12)); - assert_eq!(parse_duration("PT12H").unwrap(), Duration::hours(12)); - assert_eq!(parse_duration("PT12M").unwrap(), Duration::minutes(12)); - assert_eq!(parse_duration("PT12S").unwrap(), Duration::seconds(12)); - } -} diff --git a/crates/ical/src/icalendar/event.rs b/crates/ical/src/icalendar/event.rs index 8cc9976..34baebc 100644 --- a/crates/ical/src/icalendar/event.rs +++ b/crates/ical/src/icalendar/event.rs @@ -1,5 +1,5 @@ +use crate::CalDateTime; use crate::Error; -use crate::{CalDateTime, parse_duration}; use chrono::{DateTime, Duration, Utc}; use ical::parser::ComponentMut; use ical::{generator::IcalEvent, parser::Component, property::Property}; @@ -16,7 +16,7 @@ pub struct EventObject { impl EventObject { pub fn get_dtstart(&self) -> Result, Error> { - if let Some(dtstart) = self.event.get_property("DTSTART") { + if let Some(dtstart) = self.event.get_dtstart() { Ok(Some(CalDateTime::parse_prop(dtstart, &self.timezones)?)) } else { Ok(None) @@ -24,7 +24,7 @@ impl EventObject { } pub fn get_dtend(&self) -> Result, Error> { - if let Some(dtend) = self.event.get_property("DTEND") { + if let Some(dtend) = self.event.get_dtend() { Ok(Some(CalDateTime::parse_prop(dtend, &self.timezones)?)) } else { Ok(None) @@ -32,33 +32,21 @@ impl EventObject { } pub fn get_last_occurence(&self) -> Result, Error> { - if let Some(_rrule) = self.event.get_property("RRULE") { + if self.event.get_rrule().is_some() { // TODO: understand recurrence rules return Ok(None); } - if let Some(dtend) = self.event.get_property("DTEND") { - return Ok(Some(CalDateTime::parse_prop(dtend, &self.timezones)?)); + if let Some(dtend) = self.get_dtend()? { + return Ok(Some(dtend)); }; - let duration = self.get_duration()?.unwrap_or(Duration::days(1)); + let duration = self.event.get_duration().unwrap_or(Duration::days(1)); let first_occurence = self.get_dtstart()?; Ok(first_occurence.map(|first_occurence| first_occurence + duration)) } - pub fn get_duration(&self) -> Result, Error> { - if let Some(Property { - value: Some(duration), - .. - }) = self.event.get_property("DURATION") - { - Ok(Some(parse_duration(duration)?)) - } else { - Ok(None) - } - } - pub fn recurrence_ruleset(&self) -> Result, Error> { let dtstart: DateTime = if let Some(dtstart) = self.get_dtstart()? { if let Some(dtend) = self.get_dtend()? { diff --git a/crates/ical/src/lib.rs b/crates/ical/src/lib.rs index b7c97e6..623c088 100644 --- a/crates/ical/src/lib.rs +++ b/crates/ical/src/lib.rs @@ -3,9 +3,6 @@ mod timezone; pub use timestamp::*; pub use timezone::*; -mod duration; -pub use duration::parse_duration; - mod icalendar; pub use icalendar::*; diff --git a/crates/ical/src/timestamp.rs b/crates/ical/src/timestamp.rs index ec4cb2c..f3006ae 100644 --- a/crates/ical/src/timestamp.rs +++ b/crates/ical/src/timestamp.rs @@ -1,4 +1,4 @@ -use super::timezone::CalTimezone; +use super::timezone::ICalTimezone; use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; use chrono_tz::Tz; use derive_more::derive::Deref; @@ -64,8 +64,8 @@ pub enum CalDateTime { // Form 2, example: 19980119T070000Z -> UTC // Form 3, example: TZID=America/New_York:19980119T020000 -> Olson // https://en.wikipedia.org/wiki/Tz_database - DateTime(DateTime), - Date(NaiveDate, CalTimezone), + DateTime(DateTime), + Date(NaiveDate, ICalTimezone), } impl From for DateTime { @@ -102,13 +102,13 @@ impl Ord for CalDateTime { impl From> for CalDateTime { fn from(value: DateTime) -> Self { - CalDateTime::DateTime(value.with_timezone(&CalTimezone::Local)) + CalDateTime::DateTime(value.with_timezone(&ICalTimezone::Local)) } } impl From> for CalDateTime { fn from(value: DateTime) -> Self { - CalDateTime::DateTime(value.with_timezone(&CalTimezone::Olson(chrono_tz::UTC))) + CalDateTime::DateTime(value.with_timezone(&ICalTimezone::Olson(chrono_tz::UTC))) } } @@ -160,7 +160,7 @@ impl CalDateTime { pub fn format(&self) -> String { match self { Self::DateTime(datetime) => match datetime.timezone() { - CalTimezone::Olson(chrono_tz::UTC) => datetime.format(UTC_DATE_TIME).to_string(), + ICalTimezone::Olson(chrono_tz::UTC) => datetime.format(UTC_DATE_TIME).to_string(), _ => datetime.format(LOCAL_DATE_TIME).to_string(), }, Self::Date(date, _) => date.format(LOCAL_DATE).to_string(), @@ -185,7 +185,7 @@ impl CalDateTime { matches!(&self, Self::Date(_, _)) } - pub fn as_datetime(&self) -> Cow<'_, DateTime> { + pub fn as_datetime(&self) -> Cow<'_, DateTime> { match self { Self::DateTime(datetime) => Cow::Borrowed(datetime), Self::Date(date, tz) => Cow::Owned( @@ -209,7 +209,7 @@ impl CalDateTime { } return Ok(CalDateTime::DateTime( datetime - .and_local_timezone(CalTimezone::Local) + .and_local_timezone(ICalTimezone::Local) .earliest() .ok_or(CalDateTimeError::LocalTimeGap)?, )); @@ -219,8 +219,8 @@ impl CalDateTime { return Ok(datetime.and_utc().into()); } let timezone = timezone - .map(CalTimezone::Olson) - .unwrap_or(CalTimezone::Local); + .map(ICalTimezone::Olson) + .unwrap_or(ICalTimezone::Local); if let Ok(date) = NaiveDate::parse_from_str(value, LOCAL_DATE) { return Ok(CalDateTime::Date(date, timezone)); } @@ -252,7 +252,7 @@ impl CalDateTime { CalDateTime::Date( NaiveDate::from_ymd_opt(year, month, day) .ok_or(CalDateTimeError::ParseError(value.to_string()))?, - CalTimezone::Local, + ICalTimezone::Local, ), false, )); @@ -264,7 +264,7 @@ impl CalDateTime { self.as_datetime().to_utc() } - pub fn timezone(&self) -> CalTimezone { + pub fn timezone(&self) -> ICalTimezone { match &self { CalDateTime::DateTime(datetime) => datetime.timezone(), CalDateTime::Date(_, tz) => tz.to_owned(), @@ -400,7 +400,7 @@ mod tests { ( CalDateTime::Date( NaiveDate::from_ymd_opt(1985, 4, 12).unwrap(), - crate::CalTimezone::Local + crate::ICalTimezone::Local ), true ) @@ -410,7 +410,7 @@ mod tests { ( CalDateTime::Date( NaiveDate::from_ymd_opt(1985, 4, 12).unwrap(), - crate::CalTimezone::Local + crate::ICalTimezone::Local ), true ) @@ -420,7 +420,7 @@ mod tests { ( CalDateTime::Date( NaiveDate::from_ymd_opt(1972, 4, 12).unwrap(), - crate::CalTimezone::Local + crate::ICalTimezone::Local ), false ) diff --git a/crates/ical/src/timezone.rs b/crates/ical/src/timezone.rs index 8a17f97..6205ab2 100644 --- a/crates/ical/src/timezone.rs +++ b/crates/ical/src/timezone.rs @@ -3,21 +3,21 @@ use chrono_tz::Tz; use derive_more::{Display, From}; #[derive(Debug, Clone, From, PartialEq, Eq)] -pub enum CalTimezone { +pub enum ICalTimezone { Local, Olson(Tz), } -impl From for rrule::Tz { - fn from(value: CalTimezone) -> Self { +impl From for rrule::Tz { + fn from(value: ICalTimezone) -> Self { match value { - CalTimezone::Local => Self::LOCAL, - CalTimezone::Olson(tz) => Self::Tz(tz), + ICalTimezone::Local => Self::LOCAL, + ICalTimezone::Olson(tz) => Self::Tz(tz), } } } -impl From for CalTimezone { +impl From for ICalTimezone { fn from(value: rrule::Tz) -> Self { match value { rrule::Tz::Local(_) => Self::Local, @@ -41,7 +41,7 @@ impl chrono::Offset for CalTimezoneOffset { } } -impl TimeZone for CalTimezone { +impl TimeZone for ICalTimezone { type Offset = CalTimezoneOffset; fn from_offset(offset: &Self::Offset) -> Self {