Move ical-related stuff to dedicated rustical_ical crate

This commit is contained in:
Lennart
2025-05-18 13:46:08 +02:00
parent 3c7ee09116
commit 5ebcab7a19
21 changed files with 249 additions and 97 deletions

19
Cargo.lock generated
View File

@@ -3050,6 +3050,7 @@ dependencies = [
"quick-xml", "quick-xml",
"rustical_dav", "rustical_dav",
"rustical_dav_push", "rustical_dav_push",
"rustical_ical",
"rustical_store", "rustical_store",
"rustical_xml", "rustical_xml",
"serde", "serde",
@@ -3154,6 +3155,22 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "rustical_ical"
version = "0.1.0"
dependencies = [
"chrono",
"chrono-tz",
"derive_more 2.0.1",
"ical",
"lazy_static",
"regex",
"rustical_xml",
"strum",
"strum_macros",
"thiserror 2.0.12",
]
[[package]] [[package]]
name = "rustical_oidc" name = "rustical_oidc"
version = "0.1.0" version = "0.1.0"
@@ -3187,6 +3204,7 @@ dependencies = [
"rstest", "rstest",
"rstest_reuse", "rstest_reuse",
"rustical_dav", "rustical_dav",
"rustical_ical",
"rustical_store_sqlite", "rustical_store_sqlite",
"rustical_xml", "rustical_xml",
"serde", "serde",
@@ -3210,6 +3228,7 @@ dependencies = [
"password-hash", "password-hash",
"pbkdf2", "pbkdf2",
"rand 0.8.5", "rand 0.8.5",
"rustical_ical",
"rustical_store", "rustical_store",
"serde", "serde",
"sqlx", "sqlx",

View File

@@ -100,6 +100,7 @@ rustical_carddav = { path = "./crates/carddav/" }
rustical_frontend = { path = "./crates/frontend/" } rustical_frontend = { path = "./crates/frontend/" }
rustical_xml = { path = "./crates/xml/" } rustical_xml = { path = "./crates/xml/" }
rustical_oidc = { path = "./crates/oidc/" } rustical_oidc = { path = "./crates/oidc/" }
rustical_ical = { path = "./crates/ical/" }
chrono-tz = "0.10" chrono-tz = "0.10"
chrono-humanize = "0.2" chrono-humanize = "0.2"
rand = "0.8" rand = "0.8"

View File

@@ -28,3 +28,4 @@ sha2 = { workspace = true }
rustical_xml.workspace = true rustical_xml.workspace = true
uuid.workspace = true uuid.workspace = true
rustical_dav_push.workspace = true rustical_dav_push.workspace = true
rustical_ical.workspace = true

View File

@@ -3,9 +3,8 @@ use rustical_dav::{
resource::Resource, resource::Resource,
xml::{MultistatusElement, PropfindType}, xml::{MultistatusElement, PropfindType},
}; };
use rustical_store::{ use rustical_ical::UtcDateTime;
CalendarObject, CalendarStore, auth::User, calendar::UtcDateTime, calendar_store::CalendarQuery, use rustical_store::{CalendarObject, CalendarStore, auth::User, calendar_store::CalendarQuery};
};
use rustical_xml::XmlDeserialize; use rustical_xml::XmlDeserialize;
use std::ops::Deref; use std::ops::Deref;

View File

@@ -142,7 +142,7 @@ mod tests {
use super::*; use super::*;
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement}; use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
use rustical_dav::xml::{PropElement, PropfindType, Propname}; use rustical_dav::xml::{PropElement, PropfindType, Propname};
use rustical_store::calendar::UtcDateTime; use rustical_ical::UtcDateTime;
use rustical_xml::ValueDeserialize; use rustical_xml::ValueDeserialize;
#[test] #[test]

View File

@@ -18,8 +18,8 @@ use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{Resource, ResourceService}; use rustical_dav::resource::{Resource, ResourceService};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp}; use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
use rustical_ical::CalDateTime;
use rustical_store::auth::User; use rustical_store::auth::User;
use rustical_store::calendar::CalDateTime;
use rustical_store::{Calendar, CalendarStore, SubscriptionStore}; use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
use rustical_xml::{EnumUnitVariants, EnumVariants}; use rustical_xml::{EnumUnitVariants, EnumVariants};
use rustical_xml::{XmlDeserialize, XmlSerialize}; use rustical_xml::{XmlDeserialize, XmlSerialize};
@@ -170,10 +170,10 @@ impl Resource for CalendarResource {
self.cal.subscription_url.to_owned().map(HrefElement::from), self.cal.subscription_url.to_owned().map(HrefElement::from),
), ),
CalendarPropName::MinDateTime => { CalendarPropName::MinDateTime => {
CalendarProp::MinDateTime(CalDateTime::Utc(DateTime::<Utc>::MIN_UTC).format()) CalendarProp::MinDateTime(CalDateTime::from(DateTime::<Utc>::MIN_UTC).format())
} }
CalendarPropName::MaxDateTime => { CalendarPropName::MaxDateTime => {
CalendarProp::MaxDateTime(CalDateTime::Utc(DateTime::<Utc>::MAX_UTC).format()) CalendarProp::MaxDateTime(CalDateTime::from(DateTime::<Utc>::MAX_UTC).format())
} }
}), }),
CalendarPropWrapperName::SyncToken(prop) => { CalendarPropWrapperName::SyncToken(prop) => {

18
crates/ical/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "rustical_ical"
version.workspace = true
edition.workspace = true
description.workspace = true
repository.workspace = true
[dependencies]
chrono.workspace = true
chrono-tz.workspace = true
thiserror.workspace = true
derive_more.workspace = true
rustical_xml.workspace = true
ical.workspace = true
lazy_static.workspace = true
regex.workspace = true
strum.workspace = true
strum_macros.workspace = true

9
crates/ical/src/lib.rs Normal file
View File

@@ -0,0 +1,9 @@
pub mod rrule;
mod property_ext;
pub use property_ext::*;
mod timestamp;
mod timezone;
pub use timestamp::*;
pub use timezone::*;

View File

@@ -1,4 +1,4 @@
use crate::calendar::CalDateTime; use crate::CalDateTime;
use super::{RecurrenceLimit, RecurrenceRule}; use super::{RecurrenceLimit, RecurrenceRule};
@@ -7,23 +7,26 @@ impl RecurrenceRule {
&self, &self,
start: CalDateTime, start: CalDateTime,
end: Option<CalDateTime>, end: Option<CalDateTime>,
) -> impl IntoIterator<Item = CalDateTime> { limit: Option<usize>,
let start = start.cal_utc(); ) -> Vec<CalDateTime> {
let start = start;
// Terrible code, should clean this up later. // Terrible code, should clean this up later.
let mut end = end.map(|end| CalDateTime::cal_utc(&end)); let mut end = end;
if let Some(RecurrenceLimit::Until(until)) = &self.limit { if let Some(RecurrenceLimit::Until(until)) = &self.limit {
let until = until.cal_utc();
let mut _end = end.unwrap_or(until.clone()); let mut _end = end.unwrap_or(until.clone());
if until.utc() < _end.utc() { if until.utc() < _end.utc() {
_end = until; _end = until.clone();
} }
end = Some(_end); end = Some(_end);
} }
let count = if let Some(RecurrenceLimit::Count(count)) = &self.limit { let mut count = if let Some(RecurrenceLimit::Count(count)) = &self.limit {
*count *count
} else { } else {
2048 2048
}; };
if let Some(limit) = limit {
count = count.min(limit)
}
let mut datetimes = vec![start.clone()]; let mut datetimes = vec![start.clone()];
let mut datetime_utc = start.utc(); let mut datetime_utc = start.utc();
@@ -32,10 +35,22 @@ impl RecurrenceRule {
if datetime_utc > end.utc() { if datetime_utc > end.utc() {
break; break;
} }
datetimes.push(CalDateTime::Utc(datetime_utc));
} }
datetimes.push(datetime_utc.into());
} }
datetimes datetimes
} }
} }
#[cfg(test)]
mod tests {
use crate::{CalDateTime, rrule::RecurrenceRule};
#[test]
fn test_between() {
let rrule = RecurrenceRule::parse("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1").unwrap();
let start = CalDateTime::parse("20250516T133000Z", None).unwrap();
assert_eq!(rrule.between(start, None, Some(4)), vec![]);
}
}

View File

@@ -55,7 +55,7 @@ pub struct RecurrenceRule {
pub frequency: RecurrenceFrequency, pub frequency: RecurrenceFrequency,
pub limit: Option<RecurrenceLimit>, pub limit: Option<RecurrenceLimit>,
// Repeat every n-th time // Repeat every n-th time
pub interval: Option<usize>, pub interval: usize,
pub bysecond: Option<Vec<usize>>, pub bysecond: Option<Vec<usize>>,
pub byminute: Option<Vec<usize>>, pub byminute: Option<Vec<usize>>,
@@ -74,7 +74,7 @@ impl RecurrenceRule {
pub fn parse(rule: &str) -> Result<Self, ParserError> { pub fn parse(rule: &str) -> Result<Self, ParserError> {
let mut frequency = None; let mut frequency = None;
let mut limit = None; let mut limit = None;
let mut interval = None; let mut interval = 1;
let mut bysecond = None; let mut bysecond = None;
let mut byminute = None; let mut byminute = None;
let mut byhour = None; let mut byhour = None;
@@ -98,7 +98,7 @@ impl RecurrenceRule {
("UNTIL", val) => { ("UNTIL", val) => {
limit = Some(RecurrenceLimit::Until(CalDateTime::parse(val, None)?)) limit = Some(RecurrenceLimit::Until(CalDateTime::parse(val, None)?))
} }
("INTERVAL", val) => interval = Some(val.parse()?), ("INTERVAL", val) => interval = val.parse()?,
("BYSECOND", val) => { ("BYSECOND", val) => {
bysecond = Some( bysecond = Some(
val.split(',') val.split(',')
@@ -196,7 +196,7 @@ impl RecurrenceRule {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::calendar::{ use crate::{
CalDateTime, CalDateTime,
rrule::{RecurrenceFrequency, RecurrenceLimit, Weekday}, rrule::{RecurrenceFrequency, RecurrenceLimit, Weekday},
}; };
@@ -212,7 +212,7 @@ mod tests {
limit: Some(RecurrenceLimit::Until( limit: Some(RecurrenceLimit::Until(
CalDateTime::parse("20250516T133000Z", None).unwrap() CalDateTime::parse("20250516T133000Z", None).unwrap()
)), )),
interval: Some(3), interval: 3,
..Default::default() ..Default::default()
} }
); );
@@ -221,7 +221,7 @@ mod tests {
RecurrenceRule { RecurrenceRule {
frequency: RecurrenceFrequency::Weekly, frequency: RecurrenceFrequency::Weekly,
limit: Some(RecurrenceLimit::Count(4)), limit: Some(RecurrenceLimit::Count(4)),
interval: Some(2), interval: 2,
byday: Some(vec![ byday: Some(vec![
(None, Weekday::Tu), (None, Weekday::Tu),
(None, Weekday::Th), (None, Weekday::Th),

View File

@@ -1,4 +1,6 @@
use super::IcalProperty; use crate::IcalProperty;
use super::timezone::CalTimezone;
use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc};
use chrono_tz::Tz; use chrono_tz::Tz;
use derive_more::derive::Deref; use derive_more::derive::Deref;
@@ -65,27 +67,35 @@ impl ValueSerialize for UtcDateTime {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum CalDateTime { pub enum CalDateTime {
// Form 1, example: 19980118T230000 // Form 1, example: 19980118T230000 -> Local
Local(DateTime<Local>), // Form 2, example: 19980119T070000Z -> UTC
// Form 2, example: 19980119T070000Z // Form 3, example: TZID=America/New_York:19980119T020000 -> Olson
Utc(DateTime<Utc>),
// Form 3, example: TZID=America/New_York:19980119T020000
// https://en.wikipedia.org/wiki/Tz_database // https://en.wikipedia.org/wiki/Tz_database
OlsonTZ(DateTime<Tz>), DateTime(DateTime<CalTimezone>),
Date(NaiveDate), Date(NaiveDate),
} }
impl From<DateTime<Local>> for CalDateTime {
fn from(value: DateTime<Local>) -> Self {
CalDateTime::DateTime(value.with_timezone(&CalTimezone::Local))
}
}
impl From<DateTime<Utc>> for CalDateTime {
fn from(value: DateTime<Utc>) -> Self {
CalDateTime::DateTime(value.with_timezone(&CalTimezone::Utc))
}
}
impl Add<Duration> for CalDateTime { impl Add<Duration> for CalDateTime {
type Output = Self; type Output = Self;
fn add(self, duration: Duration) -> Self::Output { fn add(self, duration: Duration) -> Self::Output {
match self { match self {
Self::Local(datetime) => Self::Local(datetime + duration), Self::DateTime(datetime) => Self::DateTime(datetime + duration),
Self::Utc(datetime) => Self::Utc(datetime + duration), Self::Date(date) => Self::DateTime(
Self::OlsonTZ(datetime) => Self::OlsonTZ(datetime + duration),
Self::Date(date) => Self::Local(
date.and_time(NaiveTime::default()) date.and_time(NaiveTime::default())
.and_local_timezone(Local) .and_local_timezone(CalTimezone::Local)
.earliest() .earliest()
.expect("Local timezone has constant offset") .expect("Local timezone has constant offset")
+ duration, + duration,
@@ -142,42 +152,41 @@ impl CalDateTime {
pub fn format(&self) -> String { pub fn format(&self) -> String {
match self { match self {
Self::Utc(utc) => utc.format(UTC_DATE_TIME).to_string(), Self::DateTime(datetime) => match datetime.timezone() {
CalTimezone::Utc => datetime.format(UTC_DATE_TIME).to_string(),
_ => datetime.format(LOCAL_DATE_TIME).to_string(),
},
Self::Date(date) => date.format(LOCAL_DATE).to_string(), Self::Date(date) => date.format(LOCAL_DATE).to_string(),
Self::Local(datetime) => datetime.format(LOCAL_DATE_TIME).to_string(),
Self::OlsonTZ(datetime) => datetime.format(LOCAL_DATE_TIME).to_string(),
} }
} }
pub fn date(&self) -> NaiveDate { pub fn date(&self) -> NaiveDate {
match self { match self {
Self::Utc(utc) => utc.date_naive(), Self::DateTime(datetime) => datetime.date_naive(),
Self::Date(date) => date.to_owned(), Self::Date(date) => date.to_owned(),
Self::Local(datetime) => datetime.date_naive(),
Self::OlsonTZ(datetime) => datetime.date_naive(),
} }
} }
pub fn parse(value: &str, timezone: Option<Tz>) -> Result<Self, CalDateTimeError> { pub fn parse(value: &str, timezone: Option<Tz>) -> Result<Self, CalDateTimeError> {
if let Ok(datetime) = NaiveDateTime::parse_from_str(value, LOCAL_DATE_TIME) { if let Ok(datetime) = NaiveDateTime::parse_from_str(value, LOCAL_DATE_TIME) {
if let Some(timezone) = timezone { if let Some(timezone) = timezone {
return Ok(CalDateTime::OlsonTZ( return Ok(CalDateTime::DateTime(
datetime datetime
.and_local_timezone(timezone) .and_local_timezone(timezone.into())
.earliest() .earliest()
.ok_or(CalDateTimeError::LocalTimeGap)?, .ok_or(CalDateTimeError::LocalTimeGap)?,
)); ));
} }
return Ok(CalDateTime::Local( return Ok(CalDateTime::DateTime(
datetime datetime
.and_local_timezone(chrono::Local) .and_local_timezone(CalTimezone::Local)
.earliest() .earliest()
.ok_or(CalDateTimeError::LocalTimeGap)?, .ok_or(CalDateTimeError::LocalTimeGap)?,
)); ));
} }
if let Ok(datetime) = NaiveDateTime::parse_from_str(value, UTC_DATE_TIME) { if let Ok(datetime) = NaiveDateTime::parse_from_str(value, UTC_DATE_TIME) {
return Ok(CalDateTime::Utc(datetime.and_utc())); return Ok(datetime.and_utc().into());
} }
if let Ok(date) = NaiveDate::parse_from_str(value, LOCAL_DATE) { if let Ok(date) = NaiveDate::parse_from_str(value, LOCAL_DATE) {
return Ok(CalDateTime::Date(date)); return Ok(CalDateTime::Date(date));
@@ -207,16 +216,10 @@ impl CalDateTime {
pub fn utc(&self) -> DateTime<Utc> { pub fn utc(&self) -> DateTime<Utc> {
match &self { match &self {
CalDateTime::Local(local_datetime) => local_datetime.to_utc(), CalDateTime::DateTime(datetime) => datetime.to_utc(),
CalDateTime::Utc(utc_datetime) => utc_datetime.to_owned(),
CalDateTime::OlsonTZ(datetime) => datetime.to_utc(),
CalDateTime::Date(date) => date.and_time(NaiveTime::default()).and_utc(), CalDateTime::Date(date) => date.and_time(NaiveTime::default()).and_utc(),
} }
} }
pub fn cal_utc(&self) -> Self {
Self::Utc(self.utc())
}
} }
impl From<CalDateTime> for DateTime<Utc> { impl From<CalDateTime> for DateTime<Utc> {
@@ -255,27 +258,33 @@ pub fn parse_duration(string: &str) -> Result<Duration, CalDateTimeError> {
Ok(duration) Ok(duration)
} }
#[test] #[cfg(test)]
fn test_parse_duration() { mod tests {
assert_eq!(parse_duration("P12W").unwrap(), Duration::weeks(12)); use crate::{CalDateTime, parse_duration};
assert_eq!(parse_duration("P12D").unwrap(), Duration::days(12)); use chrono::{Duration, NaiveDate};
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));
}
#[test] #[test]
fn test_vcard_date() { fn test_parse_duration() {
assert_eq!( assert_eq!(parse_duration("P12W").unwrap(), Duration::weeks(12));
CalDateTime::parse("19850412", None).unwrap(), assert_eq!(parse_duration("P12D").unwrap(), Duration::days(12));
CalDateTime::Date(NaiveDate::from_ymd_opt(1985, 4, 12).unwrap()) assert_eq!(parse_duration("PT12H").unwrap(), Duration::hours(12));
); assert_eq!(parse_duration("PT12M").unwrap(), Duration::minutes(12));
assert_eq!( assert_eq!(parse_duration("PT12S").unwrap(), Duration::seconds(12));
CalDateTime::parse("1985-04-12", None).unwrap(), }
CalDateTime::Date(NaiveDate::from_ymd_opt(1985, 4, 12).unwrap())
); #[test]
assert_eq!( fn test_vcard_date() {
CalDateTime::parse("--0412", None).unwrap(), assert_eq!(
CalDateTime::Date(NaiveDate::from_ymd_opt(1972, 4, 12).unwrap()) CalDateTime::parse("19850412", None).unwrap(),
); CalDateTime::Date(NaiveDate::from_ymd_opt(1985, 4, 12).unwrap())
);
assert_eq!(
CalDateTime::parse("1985-04-12", None).unwrap(),
CalDateTime::Date(NaiveDate::from_ymd_opt(1985, 4, 12).unwrap())
);
assert_eq!(
CalDateTime::parse("--0412", None).unwrap(),
CalDateTime::Date(NaiveDate::from_ymd_opt(1972, 4, 12).unwrap())
);
}
} }

View File

@@ -0,0 +1,86 @@
use chrono::{Local, NaiveDate, NaiveDateTime, TimeZone, Utc};
use chrono_tz::Tz;
use derive_more::{Display, From};
#[derive(Debug, Clone, From)]
pub enum CalTimezone {
Local,
Utc,
Olson(Tz),
}
#[derive(Debug, Clone, PartialEq, Display)]
pub enum CalTimezoneOffset {
Local(chrono::FixedOffset),
Utc(chrono::Utc),
Olson(chrono_tz::TzOffset),
}
impl chrono::Offset for CalTimezoneOffset {
fn fix(&self) -> chrono::FixedOffset {
match self {
Self::Local(local) => local.fix(),
Self::Utc(utc) => utc.fix(),
Self::Olson(olson) => olson.fix(),
}
}
}
impl TimeZone for CalTimezone {
type Offset = CalTimezoneOffset;
fn from_offset(offset: &Self::Offset) -> Self {
match offset {
CalTimezoneOffset::Local(_) => Self::Local,
CalTimezoneOffset::Utc(_) => Self::Utc,
CalTimezoneOffset::Olson(offset) => Self::Olson(Tz::from_offset(offset)),
}
}
fn offset_from_local_date(&self, local: &NaiveDate) -> chrono::MappedLocalTime<Self::Offset> {
match self {
Self::Local => Local
.offset_from_local_date(local)
.map(CalTimezoneOffset::Local),
Self::Utc => Utc
.offset_from_local_date(local)
.map(CalTimezoneOffset::Utc),
Self::Olson(tz) => tz
.offset_from_local_date(local)
.map(CalTimezoneOffset::Olson),
}
}
fn offset_from_local_datetime(
&self,
local: &NaiveDateTime,
) -> chrono::MappedLocalTime<Self::Offset> {
match self {
Self::Local => Local
.offset_from_local_datetime(local)
.map(CalTimezoneOffset::Local),
Self::Utc => Utc
.offset_from_local_datetime(local)
.map(CalTimezoneOffset::Utc),
Self::Olson(tz) => tz
.offset_from_local_datetime(local)
.map(CalTimezoneOffset::Olson),
}
}
fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset {
match self {
Self::Local => CalTimezoneOffset::Local(Local.offset_from_utc_datetime(utc)),
Self::Utc => CalTimezoneOffset::Utc(Utc.offset_from_utc_datetime(utc)),
Self::Olson(tz) => CalTimezoneOffset::Olson(tz.offset_from_utc_datetime(utc)),
}
}
fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset {
match self {
Self::Local => CalTimezoneOffset::Local(Local.offset_from_utc_date(utc)),
Self::Utc => CalTimezoneOffset::Utc(Utc.offset_from_utc_date(utc)),
Self::Olson(tz) => CalTimezoneOffset::Olson(tz.offset_from_utc_date(utc)),
}
}
}

View File

@@ -30,6 +30,7 @@ clap.workspace = true
rustical_dav.workspace = true rustical_dav.workspace = true
strum.workspace = true strum.workspace = true
strum_macros.workspace = true strum_macros.workspace = true
rustical_ical.workspace = true
[dev-dependencies] [dev-dependencies]
rstest = { workspace = true } rstest = { workspace = true }

View File

@@ -1,15 +1,12 @@
use std::{collections::HashMap, io::BufReader}; use crate::{CalendarObject, Error};
use crate::{
calendar::{CalDateTime, LOCAL_DATE},
CalendarObject, Error,
};
use chrono::Datelike; use chrono::Datelike;
use ical::parser::{ use ical::parser::{
vcard::{self, component::VcardContact},
Component, Component,
vcard::{self, component::VcardContact},
}; };
use rustical_ical::{CalDateTime, LOCAL_DATE};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::{collections::HashMap, io::BufReader};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AddressObject { pub struct AddressObject {

View File

@@ -1,14 +1,14 @@
use super::{ use crate::Error;
CalDateTime, parse_duration,
rrule::{ParserError, RecurrenceRule},
};
use crate::{Error, calendar::ComponentMut};
use chrono::Duration; use chrono::Duration;
use ical::{ use ical::{
generator::IcalEvent, generator::IcalEvent,
parser::{Component, ical::component::IcalTimeZone}, parser::{Component, ical::component::IcalTimeZone},
property::Property, property::Property,
}; };
use rustical_ical::{
CalDateTime, ComponentMut, parse_duration,
rrule::{ParserError, RecurrenceRule},
};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -71,10 +71,10 @@ impl EventObject {
if let Some(rrule) = self.recurrence_rule()? { if let Some(rrule) = self.recurrence_rule()? {
let mut events = vec![]; let mut events = vec![];
let first_occurence = self.get_first_occurence()?.unwrap(); let first_occurence = self.get_first_occurence()?.unwrap();
let dates = rrule.between(first_occurence, None); let dates = rrule.between(first_occurence, None, None);
for date in dates { for date in dates {
let dtstart_utc = date.cal_utc(); let dtstart_utc = date;
let mut ev = self.event.clone(); let mut ev = self.event.clone();
ev.remove_property("RRULE"); ev.remove_property("RRULE");
ev.set_property(Property { ev.set_property(Property {

View File

@@ -1,16 +1,11 @@
mod calendar; mod calendar;
mod event; mod event;
mod ical;
mod journal; mod journal;
mod object; mod object;
pub mod rrule;
mod timestamp;
mod todo; mod todo;
pub use calendar::*; pub use calendar::*;
pub use event::*; pub use event::*;
pub use ical::*;
pub use journal::*; pub use journal::*;
pub use object::*; pub use object::*;
pub use timestamp::*;
pub use todo::*; pub use todo::*;

View File

@@ -1,9 +1,10 @@
use super::{CalDateTime, EventObject, JournalObject, TodoObject}; use super::{EventObject, JournalObject, TodoObject};
use crate::Error; use crate::Error;
use ical::{ use ical::{
generator::{Emitter, IcalCalendar}, generator::{Emitter, IcalCalendar},
parser::{Component, ical::component::IcalTimeZone}, parser::{Component, ical::component::IcalTimeZone},
}; };
use rustical_ical::CalDateTime;
use serde::Serialize; use serde::Serialize;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::{collections::HashMap, io::BufReader}; use std::{collections::HashMap, io::BufReader};

View File

@@ -1,6 +1,5 @@
use actix_web::{ResponseError, http::StatusCode}; use actix_web::{ResponseError, http::StatusCode};
use rustical_ical::CalDateTimeError;
use crate::calendar::CalDateTimeError;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
@@ -14,7 +13,7 @@ pub enum Error {
InvalidData(String), InvalidData(String),
#[error(transparent)] #[error(transparent)]
RRuleParserError(#[from] crate::calendar::rrule::ParserError), RRuleParserError(#[from] rustical_ical::rrule::ParserError),
#[error("Read-only")] #[error("Read-only")]
ReadOnly, ReadOnly,

View File

@@ -21,3 +21,4 @@ password-hash.workspace = true
uuid.workspace = true uuid.workspace = true
rand.workspace = true rand.workspace = true
pbkdf2.workspace = true pbkdf2.workspace = true
rustical_ical.workspace = true

View File

@@ -2,7 +2,8 @@ use super::ChangeOperation;
use async_trait::async_trait; use async_trait::async_trait;
use chrono::TimeDelta; use chrono::TimeDelta;
use derive_more::derive::Constructor; use derive_more::derive::Constructor;
use rustical_store::calendar::{CalDateTime, CalendarObjectType}; use rustical_ical::CalDateTime;
use rustical_store::calendar::CalendarObjectType;
use rustical_store::calendar_store::CalendarQuery; use rustical_store::calendar_store::CalendarQuery;
use rustical_store::synctoken::format_synctoken; use rustical_store::synctoken::format_synctoken;
use rustical_store::{Calendar, CalendarObject, CalendarStore, Error}; use rustical_store::{Calendar, CalendarObject, CalendarStore, Error};