From 53f81a94336da78dc209b89e6ea55c639846cafa Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sat, 10 Jan 2026 13:22:49 +0100 Subject: [PATCH] Add a startup test to check whether existing data will be compatible with v0.12 --- Cargo.lock | 82 +++++++++++++++++++++++++++++++++++++------ Cargo.toml | 4 +++ src/main.rs | 11 +++++- src/migration_0_12.rs | 80 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 12 deletions(-) create mode 100644 src/migration_0_12.rs diff --git a/Cargo.lock b/Cargo.lock index 2c67e18..de3aead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -623,7 +623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" dependencies = [ "chrono", - "phf", + "phf 0.12.1", ] [[package]] @@ -1768,6 +1768,22 @@ dependencies = [ "cc", ] +[[package]] +name = "ical" +version = "0.11.0" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#64c342e7258ba445dc91b47cd8e20e0ac8ffc417" +dependencies = [ + "chrono", + "chrono-tz", + "derive_more", + "itertools 0.14.0", + "lazy_static", + "phf 0.13.1", + "regex", + "rrule", + "thiserror 2.0.17", +] + [[package]] name = "ical" version = "0.11.0" @@ -2593,7 +2609,18 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" dependencies = [ - "phf_shared", + "phf_shared 0.12.1", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared 0.13.1", + "serde", ] [[package]] @@ -2602,8 +2629,8 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.12.1", + "phf_shared 0.12.1", ] [[package]] @@ -2613,7 +2640,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" dependencies = [ "fastrand", - "phf_shared", + "phf_shared 0.12.1", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.112", ] [[package]] @@ -2625,6 +2675,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -3284,6 +3343,7 @@ dependencies = [ "figment", "headers", "http", + "ical 0.11.0 (git+https://github.com/lennart-k/ical-rs?branch=dev)", "insta", "opentelemetry", "opentelemetry-otlp", @@ -3331,7 +3391,7 @@ dependencies = [ "futures-util", "headers", "http", - "ical", + "ical 0.11.0 (git+https://github.com/lennart-k/ical-rs)", "insta", "percent-encoding", "quick-xml", @@ -3370,7 +3430,7 @@ dependencies = [ "derive_more", "futures-util", "http", - "ical", + "ical 0.11.0 (git+https://github.com/lennart-k/ical-rs)", "insta", "percent-encoding", "quick-xml", @@ -3403,7 +3463,7 @@ dependencies = [ "futures-util", "headers", "http", - "ical", + "ical 0.11.0 (git+https://github.com/lennart-k/ical-rs)", "itertools 0.14.0", "log", "matchit 0.9.1", @@ -3487,7 +3547,7 @@ dependencies = [ "chrono", "chrono-tz", "derive_more", - "ical", + "ical 0.11.0 (git+https://github.com/lennart-k/ical-rs)", "regex", "rrule", "rstest", @@ -3528,7 +3588,7 @@ dependencies = [ "futures-core", "headers", "http", - "ical", + "ical 0.11.0 (git+https://github.com/lennart-k/ical-rs)", "regex", "rrule", "rstest", @@ -4910,7 +4970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6728de8767c8dea44f41b88115a205ed23adc3302e1b4342be59d922934dae5" dependencies = [ "glob", - "phf", + "phf 0.12.1", "phf_codegen", ] diff --git a/Cargo.toml b/Cargo.toml index d386939..d5fba5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -201,3 +201,7 @@ tower-http.workspace = true axum-extra.workspace = true headers.workspace = true http.workspace = true +# TODO: Remove in next major release +ical_dev = { package = "ical", git = "https://github.com/lennart-k/ical-rs", branch = "dev", features = [ + "chrono-tz", +] } diff --git a/src/main.rs b/src/main.rs index 53e4e00..2641b82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ use std::sync::Arc; use tokio::sync::mpsc::Receiver; use tower::Layer; use tower_http::normalize_path::NormalizePathLayer; -use tracing::info; +use tracing::{info, warn}; mod app; mod commands; @@ -34,6 +34,9 @@ mod config; pub mod integration_tests; mod setup_tracing; +mod migration_0_12; +use migration_0_12::{validate_address_objects_0_12, validate_calendar_objects_0_12}; + #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { @@ -115,6 +118,12 @@ async fn main() -> Result<()> { let (addr_store, cal_store, subscription_store, principal_store, update_recv) = get_data_stores(!args.no_migrations, &config.data_store).await?; + warn!( + "Validating calendar data against the next-version ical parser.\nIn the next major release these will be rejected and cause errors.\nIf any errors occur, please open an issue so they can be fixed before the next major release." + ); + validate_calendar_objects_0_12(principal_store.as_ref(), cal_store.as_ref()).await?; + validate_address_objects_0_12(principal_store.as_ref(), addr_store.as_ref()).await?; + let mut tasks = vec![]; if config.dav_push.enabled { diff --git a/src/migration_0_12.rs b/src/migration_0_12.rs new file mode 100644 index 0000000..1e49aa3 --- /dev/null +++ b/src/migration_0_12.rs @@ -0,0 +1,80 @@ +use rustical_store::{AddressbookStore, CalendarStore, auth::AuthenticationProvider}; +use tracing::{error, info}; + +pub async fn validate_calendar_objects_0_12( + principal_store: &impl AuthenticationProvider, + cal_store: &impl CalendarStore, +) -> Result<(), rustical_store::Error> { + let mut success = true; + for principal in principal_store.get_principals().await? { + for calendar in cal_store.get_calendars(&principal.id).await? { + for object in cal_store + .get_objects(&calendar.principal, &calendar.id) + .await? + { + if let Err(err) = + ical_dev::parser::ical::IcalObjectParser::new(object.get_ics().as_bytes()) + .expect_one() + { + success = false; + error!( + "An error occured parsing a calendar object: principal={principal}, calendar={calendar}, object_id={object_id}: {err}", + principal = principal.id, + calendar = calendar.id, + object_id = object.get_id() + ); + println!("{}", object.get_ics()); + } + } + } + } + if success { + info!("Your calendar data seems to be valid in the next major version."); + } else { + error!( + "Not all calendar objects will be successfully parsed in the next major version (v0.12). +This will not cause issues in this version, but please comment under the tracking issue on GitHub: +https://github.com/lennart-k/rustical/issues/165" + ); + } + Ok(()) +} + +pub async fn validate_address_objects_0_12( + principal_store: &impl AuthenticationProvider, + addr_store: &impl AddressbookStore, +) -> Result<(), rustical_store::Error> { + let mut success = true; + for principal in principal_store.get_principals().await? { + for addressbook in addr_store.get_addressbooks(&principal.id).await? { + for object in addr_store + .get_objects(&addressbook.principal, &addressbook.id) + .await? + { + if let Err(err) = + ical_dev::parser::vcard::VcardParser::new(object.get_vcf().as_bytes()) + .expect_one() + { + success = false; + error!( + "An error occured parsing an address object: principal={principal}, addressbook={addressbook}, object_id={object_id}: {err}", + principal = principal.id, + addressbook = addressbook.id, + object_id = object.get_id() + ); + println!("{}", object.get_vcf()); + } + } + } + } + if success { + info!("Your addressbook data seems to be valid in the next major version."); + } else { + error!( + "Not all address objects will be successfully parsed in the next major version (v0.12). +This will not cause issues in this version, but please comment under the tracking issue on GitHub: +https://github.com/lennart-k/rustical/issues/165" + ); + } + Ok(()) +}