Migrate ical-rs to caldata-rs

This commit is contained in:
Lennart K
2026-01-22 11:01:00 +01:00
parent 11a61cf8b1
commit d5892ab56b
27 changed files with 93 additions and 94 deletions

47
Cargo.lock generated
View File

@@ -565,6 +565,23 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "caldata"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ae67018b0b95d01f20a5f6869810e83969baa94b2ff768de7f90a8a9e38efd"
dependencies = [
"chrono",
"chrono-tz",
"derive_more",
"itertools 0.14.0",
"lazy_static",
"phf 0.13.1",
"regex",
"rrule",
"thiserror 2.0.18",
]
[[package]]
name = "cast"
version = "0.3.0"
@@ -1768,22 +1785,6 @@ dependencies = [
"cc",
]
[[package]]
name = "ical"
version = "0.12.0-dev"
source = "git+https://github.com/lennart-k/ical-rs?rev=f1ad6456fd6cbd1e6da095297febddd2cfe61422#f1ad6456fd6cbd1e6da095297febddd2cfe61422"
dependencies = [
"chrono",
"chrono-tz",
"derive_more",
"itertools 0.14.0",
"lazy_static",
"phf 0.13.1",
"regex",
"rrule",
"thiserror 2.0.18",
]
[[package]]
name = "icu_collections"
version = "2.1.1"
@@ -3324,11 +3325,11 @@ dependencies = [
"async-trait",
"axum",
"axum-extra",
"caldata",
"clap",
"figment",
"headers",
"http",
"ical",
"insta",
"opentelemetry",
"opentelemetry-otlp",
@@ -3371,13 +3372,13 @@ dependencies = [
"axum",
"axum-extra",
"base64 0.22.1",
"caldata",
"chrono",
"chrono-tz",
"derive_more",
"futures-util",
"headers",
"http",
"ical",
"insta",
"percent-encoding",
"quick-xml",
@@ -3412,11 +3413,11 @@ dependencies = [
"axum",
"axum-extra",
"base64 0.22.1",
"caldata",
"chrono",
"derive_more",
"futures-util",
"http",
"ical",
"insta",
"percent-encoding",
"quick-xml",
@@ -3445,11 +3446,11 @@ dependencies = [
"async-trait",
"axum",
"axum-extra",
"caldata",
"derive_more",
"futures-util",
"headers",
"http",
"ical",
"itertools 0.14.0",
"log",
"matchit 0.9.1",
@@ -3530,10 +3531,10 @@ name = "rustical_ical"
version = "0.12.1"
dependencies = [
"axum",
"caldata",
"chrono",
"chrono-tz",
"derive_more",
"ical",
"regex",
"rrule",
"rstest",
@@ -3567,6 +3568,7 @@ dependencies = [
"anyhow",
"async-trait",
"axum",
"caldata",
"chrono",
"chrono-tz",
"clap",
@@ -3574,7 +3576,6 @@ dependencies = [
"futures-core",
"headers",
"http",
"ical",
"regex",
"rrule",
"rstest",
@@ -3598,10 +3599,10 @@ name = "rustical_store_sqlite"
version = "0.12.1"
dependencies = [
"async-trait",
"caldata",
"chrono",
"criterion",
"derive_more",
"ical",
"password-auth",
"password-hash",
"pbkdf2",

View File

@@ -107,9 +107,7 @@ strum = "0.27"
strum_macros = "0.27"
serde_json = { version = "1.0", features = ["raw_value"] }
sqlx-sqlite = { version = "0.8", features = ["bundled"] }
ical = { git = "https://github.com/lennart-k/ical-rs", rev = "f1ad6456fd6cbd1e6da095297febddd2cfe61422", features = [
"chrono-tz",
] }
caldata = { version = "0.12.1", features = ["chrono-tz"] }
toml = "0.9"
tower = "0.5"
tower-http = { version = "0.6", features = [
@@ -161,7 +159,7 @@ rustical_store_sqlite.workspace = true
rustical_caldav.workspace = true
rustical_carddav.workspace = true
rustical_frontend.workspace = true
ical.workspace = true
caldata.workspace = true
toml.workspace = true
serde.workspace = true
tokio.workspace = true

View File

@@ -34,7 +34,7 @@ rustical_store.workspace = true
chrono.workspace = true
chrono-tz.workspace = true
sha2.workspace = true
ical.workspace = true
caldata.workspace = true
percent-encoding.workspace = true
rustical_xml.workspace = true
uuid.workspace = true

View File

@@ -3,11 +3,11 @@ use crate::calendar::CalendarResourceService;
use axum::body::Body;
use axum::extract::State;
use axum::{extract::Path, response::Response};
use caldata::component::IcalCalendar;
use caldata::generator::Emitter;
use caldata::parser::ContentLine;
use headers::{ContentType, HeaderMapExt};
use http::{HeaderValue, Method, StatusCode, header};
use ical::component::IcalCalendar;
use ical::generator::Emitter;
use ical::property::ContentLine;
use percent_encoding::{CONTROLS, utf8_percent_encode};
use rustical_store::{CalendarStore, SubscriptionStore, auth::Principal};
use std::str::FromStr;

View File

@@ -4,8 +4,9 @@ use axum::{
extract::{Path, State},
response::{IntoResponse, Response},
};
use caldata::IcalParser;
use caldata::component::{Component, ComponentMut};
use http::StatusCode;
use ical::parser::{Component, ComponentMut};
use rustical_dav::header::Overwrite;
use rustical_ical::CalendarObjectType;
use rustical_store::{
@@ -25,7 +26,7 @@ pub async fn route_import<C: CalendarStore, S: SubscriptionStore>(
return Err(Error::Unauthorized);
}
let parser = ical::IcalParser::from_slice(body.as_bytes());
let parser = IcalParser::from_slice(body.as_bytes());
let mut cal = match parser.expect_one() {
Ok(cal) => cal.mutable(),
Err(err) => return Ok((StatusCode::BAD_REQUEST, err.to_string()).into_response()),

View File

@@ -6,8 +6,8 @@ use crate::calendar::prop::SupportedCalendarComponentSet;
use crate::error::Precondition;
use axum::extract::{Path, State};
use axum::response::{IntoResponse, Response};
use caldata::IcalParser;
use http::{Method, StatusCode};
use ical::IcalParser;
use rustical_dav::xml::HrefElement;
use rustical_ical::CalendarObjectType;
use rustical_store::auth::Principal;

View File

@@ -2,9 +2,12 @@ use crate::calendar::methods::report::calendar_query::{
TimeRangeElement,
prop_filter::{PropFilterElement, PropFilterable},
};
use ical::{
component::{CalendarInnerData, IcalAlarm, IcalCalendarObject, IcalEvent, IcalTodo},
parser::{Component, ical::component::IcalTimeZone},
use caldata::{
component::{
CalendarInnerData, Component, IcalAlarm, IcalCalendarObject, IcalEvent, IcalTimeZone,
IcalTodo,
},
parser::ContentLine,
};
use rustical_xml::XmlDeserialize;
@@ -112,10 +115,7 @@ impl CompFilterable for CalendarInnerData {
}
impl PropFilterable for IcalAlarm {
fn get_named_properties<'a>(
&'a self,
name: &'a str,
) -> impl Iterator<Item = &'a ical::property::ContentLine> {
fn get_named_properties<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a ContentLine> {
Component::get_named_properties(self, name)
}
}
@@ -139,7 +139,7 @@ impl PropFilterable for CalendarInnerData {
fn get_named_properties<'a>(
&'a self,
name: &'a str,
) -> Box<dyn Iterator<Item = &'a ical::property::ContentLine> + 'a> {
) -> Box<dyn Iterator<Item = &'a ContentLine> + 'a> {
// TODO: If we were pedantic, we would have to do recurrence expansion first
// and take into account the overrides :(
match self {
@@ -151,10 +151,7 @@ impl PropFilterable for CalendarInnerData {
}
impl PropFilterable for IcalCalendarObject {
fn get_named_properties<'a>(
&'a self,
name: &'a str,
) -> impl Iterator<Item = &'a ical::property::ContentLine> {
fn get_named_properties<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a ContentLine> {
Component::get_named_properties(self, name)
}
}
@@ -185,10 +182,7 @@ impl CompFilterable for IcalCalendarObject {
}
impl PropFilterable for IcalTimeZone {
fn get_named_properties<'a>(
&'a self,
name: &'a str,
) -> impl Iterator<Item = &'a ical::property::ContentLine> {
fn get_named_properties<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a ContentLine> {
Component::get_named_properties(self, name)
}
}

View File

@@ -1,6 +1,6 @@
use super::comp_filter::{CompFilterElement, CompFilterable};
use crate::calendar_object::CalendarObjectPropWrapperName;
use ical::{component::IcalCalendarObject, property::ContentLine};
use caldata::{component::IcalCalendarObject, parser::ContentLine};
use rustical_dav::xml::{PropfindType, TextMatchElement};
use rustical_ical::UtcDateTime;
use rustical_store::calendar_store::CalendarQuery;

View File

@@ -1,5 +1,5 @@
use super::{ParamFilterElement, TimeRangeElement};
use ical::{property::ContentLine, types::CalDateTime};
use caldata::{parser::ContentLine, types::CalDateTime};
use rustical_dav::xml::TextMatchElement;
use rustical_ical::UtcDateTime;
use rustical_xml::XmlDeserialize;

View File

@@ -1,10 +1,10 @@
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData};
use crate::Error;
use crate::calendar::prop::{ReportMethod, SupportedCollationSet};
use caldata::IcalParser;
use caldata::types::CalDateTime;
use chrono::{DateTime, Utc};
use derive_more::derive::{From, Into};
use ical::IcalParser;
use ical::types::CalDateTime;
use rustical_dav::extensions::{
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
};

View File

@@ -3,8 +3,8 @@ use super::prop::{
CalendarObjectPropWrapperName,
};
use crate::Error;
use caldata::generator::Emitter;
use derive_more::derive::{From, Into};
use ical::generator::Emitter;
use rustical_dav::{
extensions::CommonPropertiesExtension,
privileges::UserPrivilegeSet,

View File

@@ -32,7 +32,7 @@ rustical_ical.workspace = true
http.workspace = true
tower-http.workspace = true
percent-encoding.workspace = true
ical.workspace = true
caldata.workspace = true
strum.workspace = true
strum_macros.workspace = true
rstest.workspace = true

View File

@@ -7,8 +7,8 @@ use crate::{
AddressObjectPropWrapperName,
},
};
use caldata::property::VcardFNProperty;
use derive_more::derive::{From, Into};
use ical::parser::VcardFNProperty;
use rustical_dav::{
extensions::CommonPropertiesExtension,
privileges::UserPrivilegeSet,

View File

@@ -4,11 +4,12 @@ use axum::{
extract::{Path, State},
response::{IntoResponse, Response},
};
use http::StatusCode;
use ical::{
parser::{Component, ComponentMut, vcard},
property::ContentLine,
use caldata::{
VcardParser,
component::{Component, ComponentMut},
parser::ContentLine,
};
use http::StatusCode;
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore, auth::Principal};
use tracing::instrument;
@@ -23,7 +24,7 @@ pub async fn route_import<AS: AddressbookStore, S: SubscriptionStore>(
return Err(Error::Unauthorized);
}
let parser = vcard::VcardParser::from_slice(body.as_bytes());
let parser = VcardParser::from_slice(body.as_bytes());
let mut objects = vec![];
for res in parser {

View File

@@ -2,8 +2,8 @@ use crate::{
address_object::AddressObjectPropWrapperName,
addressbook::methods::report::addressbook_query::PropFilterElement,
};
use caldata::parser::ContentLine;
use derive_more::{From, Into};
use ical::property::ContentLine;
use rustical_dav::xml::{PropfindType, TextMatchElement};
use rustical_ical::{AddressObject, UtcDateTime};
use rustical_xml::{ValueDeserialize, XmlDeserialize, XmlRootTag};

View File

@@ -1,5 +1,5 @@
use super::{Allof, ParamFilterElement};
use ical::{parser::Component, property::ContentLine};
use caldata::{component::Component, parser::ContentLine};
use rustical_dav::xml::TextMatchElement;
use rustical_ical::AddressObject;
use rustical_xml::XmlDeserialize;

View File

@@ -28,7 +28,7 @@ headers.workspace = true
strum.workspace = true
matchit.workspace = true
matchit-serde.workspace = true
ical = { workspace = true, optional = true }
caldata = { workspace = true, optional = true }
[features]
ical = ["dep:ical"]
ical = ["dep:caldata"]

View File

@@ -1,4 +1,4 @@
use ical::property::ContentLine;
use caldata::parser::ContentLine;
use rustical_xml::{ValueDeserialize, XmlDeserialize};
use std::borrow::Cow;

View File

@@ -15,7 +15,7 @@ chrono-tz.workspace = true
thiserror.workspace = true
derive_more.workspace = true
rustical_xml.workspace = true
ical.workspace = true
caldata.workspace = true
regex.workspace = true
rrule.workspace = true
serde.workspace = true

View File

@@ -1,18 +1,21 @@
use crate::{CalendarObject, Error};
use caldata::{
VcardParser,
component::{
CalendarInnerDataBuilder, ComponentMut, IcalAlarmBuilder, IcalCalendarObjectBuilder,
IcalEventBuilder, VcardContact,
},
generator::Emitter,
parser::ContentLine,
property::{
Calscale, IcalCALSCALEProperty, IcalDTENDProperty, IcalDTSTAMPProperty,
IcalDTSTARTProperty, IcalPRODIDProperty, IcalRRULEProperty, IcalSUMMARYProperty,
IcalUIDProperty, IcalVERSIONProperty, IcalVersion, VcardANNIVERSARYProperty,
VcardBDAYProperty, VcardFNProperty,
},
types::{CalDate, PartialDate, Timezone},
};
use chrono::{NaiveDate, Utc};
use ical::component::{
CalendarInnerDataBuilder, IcalAlarmBuilder, IcalCalendarObjectBuilder, IcalEventBuilder,
};
use ical::generator::Emitter;
use ical::parser::vcard::{self, component::VcardContact};
use ical::parser::{
Calscale, ComponentMut, IcalCALSCALEProperty, IcalDTENDProperty, IcalDTSTAMPProperty,
IcalDTSTARTProperty, IcalPRODIDProperty, IcalRRULEProperty, IcalSUMMARYProperty,
IcalUIDProperty, IcalVERSIONProperty, IcalVersion, VcardANNIVERSARYProperty, VcardBDAYProperty,
VcardFNProperty,
};
use ical::property::ContentLine;
use ical::types::{CalDate, PartialDate};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::str::FromStr;
@@ -32,7 +35,7 @@ impl From<VcardContact> for AddressObject {
impl AddressObject {
pub fn from_vcf(vcf: String) -> Result<Self, Error> {
let parser = vcard::VcardParser::from_slice(vcf.as_bytes());
let parser = VcardParser::from_slice(vcf.as_bytes());
let vcard = parser.expect_one()?;
Ok(Self { vcf, vcard })
}
@@ -70,7 +73,7 @@ impl AddressObject {
let Some(dtstart) = NaiveDate::from_ymd_opt(year.unwrap_or(1900), month, day) else {
return Ok(None);
};
let start_date = CalDate(dtstart, ical::types::Timezone::Local);
let start_date = CalDate(dtstart, Timezone::Local);
let Some(end_date) = start_date.succ_opt() else {
// start_date is MAX_DATE, this should never happen but FAPP also not raise an error
return Ok(None);

View File

@@ -1,9 +1,10 @@
use crate::Error;
use caldata::{
IcalObjectParser,
component::{CalendarInnerData, IcalCalendarObject},
generator::Emitter,
};
use derive_more::Display;
use ical::IcalObjectParser;
use ical::component::CalendarInnerData;
use ical::component::IcalCalendarObject;
use ical::generator::Emitter;
use serde::Deserialize;
use serde::Serialize;
use sha2::{Digest, Sha256};

View File

@@ -1,7 +1,7 @@
#![warn(clippy::all, clippy::pedantic, clippy::nursery)]
#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)]
mod timestamp;
use ical::parser::ParserError;
use caldata::parser::ParserError;
pub use timestamp::*;
mod calendar_object;

View File

@@ -13,7 +13,7 @@ anyhow.workspace = true
async-trait.workspace = true
serde.workspace = true
sha2.workspace = true
ical.workspace = true
caldata.workspace = true
chrono.workspace = true
regex.workspace = true
thiserror.workspace = true

View File

@@ -26,7 +26,7 @@ pub enum Error {
Other(#[from] anyhow::Error),
#[error(transparent)]
IcalError(#[from] ical::parser::ParserError),
IcalError(#[from] caldata::parser::ParserError),
}
impl Error {

View File

@@ -20,7 +20,7 @@ rstest.workspace = true
criterion.workspace = true
[dependencies]
ical.workspace = true
caldata.workspace = true
tokio.workspace = true
rustical_store.workspace = true
async-trait.workspace = true

View File

@@ -1,8 +1,8 @@
use super::ChangeOperation;
use crate::BEGIN_IMMEDIATE;
use async_trait::async_trait;
use caldata::parser::ParserError;
use derive_more::derive::Constructor;
use ical::parser::ParserError;
use rustical_ical::AddressObject;
use rustical_store::{
Addressbook, AddressbookStore, CollectionMetadata, CollectionOperation,

View File

@@ -1,10 +1,10 @@
use super::ChangeOperation;
use crate::BEGIN_IMMEDIATE;
use async_trait::async_trait;
use caldata::parser::ParserError;
use caldata::types::CalDateTime;
use chrono::TimeDelta;
use derive_more::derive::Constructor;
use ical::parser::ParserError;
use ical::types::CalDateTime;
use regex::Regex;
use rustical_ical::{CalendarObject, CalendarObjectType};
use rustical_store::calendar_store::CalendarQuery;