From 69acde10baf0ed0bb19815a0f8fe988cc819cf3e Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:32:53 +0100 Subject: [PATCH 01/26] migrate to new ical-rs version --- Cargo.lock | 29 +- Cargo.toml | 8 +- crates/caldav/src/calendar/methods/get.rs | 148 +++---- crates/caldav/src/calendar/methods/import.rs | 114 +++--- .../caldav/src/calendar/methods/mkcalendar.rs | 5 +- .../report/calendar_query/comp_filter.rs | 52 +-- .../methods/report/calendar_query/elements.rs | 10 +- .../methods/report/calendar_query/mod.rs | 4 +- .../report/calendar_query/prop_filter.rs | 65 +-- crates/caldav/src/calendar/resource.rs | 8 +- .../carddav/src/addressbook/methods/import.rs | 10 +- .../report/addressbook_query/elements.rs | 6 +- .../report/addressbook_query/prop_filter.rs | 8 +- crates/dav/src/xml/text_match.rs | 4 +- crates/ical/src/address_object.rs | 96 +---- crates/ical/src/icalendar/event.rs | 386 ------------------ crates/ical/src/icalendar/mod.rs | 2 - crates/ical/src/icalendar/object.rs | 283 ++----------- crates/ical/src/lib.rs | 2 - crates/ical/src/timestamp.rs | 381 +---------------- crates/ical/src/timezone.rs | 92 ----- crates/store_sqlite/Cargo.toml | 1 + crates/store_sqlite/src/calendar_store.rs | 11 +- 23 files changed, 227 insertions(+), 1498 deletions(-) delete mode 100644 crates/ical/src/icalendar/event.rs delete mode 100644 crates/ical/src/timezone.rs diff --git a/Cargo.lock b/Cargo.lock index 80ac433..90cd229 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1768,25 +1768,10 @@ dependencies = [ "cc", ] -[[package]] -name = "ical" -version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?rev=7c2ab1f3#7c2ab1f3abdca768f22d8a36627eebbdd7947e29" -dependencies = [ - "chrono", - "chrono-tz", - "derive_more", - "itertools 0.14.0", - "lazy_static", - "regex", - "rrule", - "thiserror 2.0.17", -] - [[package]] name = "ical" version = "0.12.0-dev" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#d2226f6b92fa45dcc5a243adc57e6a07a67741a8" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#5e61c25646c3785448d349e7d18b2833fc483c53" dependencies = [ "chrono", "chrono-tz", @@ -3343,7 +3328,6 @@ dependencies = [ "figment", "headers", "http", - "ical 0.12.0-dev", "insta", "opentelemetry", "opentelemetry-otlp", @@ -3391,7 +3375,7 @@ dependencies = [ "futures-util", "headers", "http", - "ical 0.11.0", + "ical", "insta", "percent-encoding", "quick-xml", @@ -3430,7 +3414,7 @@ dependencies = [ "derive_more", "futures-util", "http", - "ical 0.11.0", + "ical", "insta", "percent-encoding", "quick-xml", @@ -3463,7 +3447,7 @@ dependencies = [ "futures-util", "headers", "http", - "ical 0.11.0", + "ical", "itertools 0.14.0", "log", "matchit 0.9.1", @@ -3547,7 +3531,7 @@ dependencies = [ "chrono", "chrono-tz", "derive_more", - "ical 0.11.0", + "ical", "regex", "rrule", "rstest", @@ -3588,7 +3572,7 @@ dependencies = [ "futures-core", "headers", "http", - "ical 0.11.0", + "ical", "regex", "rrule", "rstest", @@ -3615,6 +3599,7 @@ dependencies = [ "chrono", "criterion", "derive_more", + "ical", "password-auth", "password-hash", "pbkdf2", diff --git a/Cargo.toml b/Cargo.toml index f3ac0f2..ead46ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,9 @@ 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 = "7c2ab1f3" } +ical = { git = "https://github.com/lennart-k/ical-rs", branch = "dev", features = [ + "chrono-tz", +] } toml = "0.9" tower = "0.5" tower-http = { version = "0.6", features = [ @@ -199,7 +201,3 @@ 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/crates/caldav/src/calendar/methods/get.rs b/crates/caldav/src/calendar/methods/get.rs index f69843e..368870b 100644 --- a/crates/caldav/src/calendar/methods/get.rs +++ b/crates/caldav/src/calendar/methods/get.rs @@ -5,11 +5,9 @@ use axum::extract::State; use axum::{extract::Path, response::Response}; use headers::{ContentType, HeaderMapExt}; use http::{HeaderValue, Method, StatusCode, header}; -use ical::builder::calendar::IcalCalendarBuilder; use ical::generator::Emitter; -use ical::property::Property; +use ical::property::ContentLine; use percent_encoding::{CONTROLS, utf8_percent_encode}; -use rustical_ical::{CalendarObjectComponent, EventObject}; use rustical_store::{CalendarStore, SubscriptionStore, auth::Principal}; use std::collections::HashMap; use std::str::FromStr; @@ -33,77 +31,79 @@ pub async fn route_get( return Err(crate::Error::Unauthorized); } - let mut vtimezones = HashMap::new(); - let objects = cal_store.get_objects(&principal, &calendar_id).await?; + // let mut vtimezones = HashMap::new(); + // let objects = cal_store.get_objects(&principal, &calendar_id).await?; - let mut ical_calendar_builder = IcalCalendarBuilder::version("2.0") - .gregorian() - .prodid("RustiCal"); - if let Some(displayname) = calendar.meta.displayname { - ical_calendar_builder = ical_calendar_builder.set(Property { - name: "X-WR-CALNAME".to_owned(), - value: Some(displayname), - params: vec![], - }); - } - if let Some(description) = calendar.meta.description { - ical_calendar_builder = ical_calendar_builder.set(Property { - name: "X-WR-CALDESC".to_owned(), - value: Some(description), - params: vec![], - }); - } - if let Some(timezone_id) = calendar.timezone_id { - ical_calendar_builder = ical_calendar_builder.set(Property { - name: "X-WR-TIMEZONE".to_owned(), - value: Some(timezone_id), - params: vec![], - }); - } + todo!() - for object in &objects { - vtimezones.extend(object.get_vtimezones()); - match object.get_data() { - CalendarObjectComponent::Event(EventObject { event, .. }, overrides) => { - ical_calendar_builder = ical_calendar_builder - .add_event(event.clone()) - .add_events(overrides.iter().map(|ev| ev.event.clone())); - } - CalendarObjectComponent::Todo(todo, overrides) => { - ical_calendar_builder = ical_calendar_builder - .add_todo(todo.clone()) - .add_todos(overrides.iter().cloned()); - } - CalendarObjectComponent::Journal(journal, overrides) => { - ical_calendar_builder = ical_calendar_builder - .add_journal(journal.clone()) - .add_journals(overrides.iter().cloned()); - } - } - } - - ical_calendar_builder = ical_calendar_builder.add_timezones(vtimezones.into_values().cloned()); - - let ical_calendar = ical_calendar_builder - .build() - .map_err(|parser_error| Error::IcalError(parser_error.into()))?; - - let mut resp = Response::builder().status(StatusCode::OK); - let hdrs = resp.headers_mut().unwrap(); - hdrs.typed_insert(ContentType::from_str("text/calendar; charset=utf-8").unwrap()); - - let filename = format!("{}_{}.ics", calendar.principal, calendar.id); - let filename = utf8_percent_encode(&filename, CONTROLS); - hdrs.insert( - header::CONTENT_DISPOSITION, - HeaderValue::from_str(&format!( - "attachement; filename*=UTF-8''{filename}; filename={filename}", - )) - .unwrap(), - ); - if matches!(method, Method::HEAD) { - Ok(resp.body(Body::empty()).unwrap()) - } else { - Ok(resp.body(Body::new(ical_calendar.generate())).unwrap()) - } + // let mut ical_calendar_builder = IcalCalendarBuilder::version("2.0") + // .gregorian() + // .prodid("RustiCal"); + // if let Some(displayname) = calendar.meta.displayname { + // ical_calendar_builder = ical_calendar_builder.set(ContentLine { + // name: "X-WR-CALNAME".to_owned(), + // value: Some(displayname), + // params: vec![].into(), + // }); + // } + // if let Some(description) = calendar.meta.description { + // ical_calendar_builder = ical_calendar_builder.set(ContentLine { + // name: "X-WR-CALDESC".to_owned(), + // value: Some(description), + // params: vec![].into(), + // }); + // } + // if let Some(timezone_id) = calendar.timezone_id { + // ical_calendar_builder = ical_calendar_builder.set(ContentLine { + // name: "X-WR-TIMEZONE".to_owned(), + // value: Some(timezone_id), + // params: vec![].into(), + // }); + // } + // + // for object in &objects { + // vtimezones.extend(object.get_vtimezones()); + // match object.get_data() { + // CalendarObjectComponent::Event(EventObject { event, .. }, overrides) => { + // ical_calendar_builder = ical_calendar_builder + // .add_event(event.clone()) + // .add_events(overrides.iter().map(|ev| ev.event.clone())); + // } + // CalendarObjectComponent::Todo(todo, overrides) => { + // ical_calendar_builder = ical_calendar_builder + // .add_todo(todo.clone()) + // .add_todos(overrides.iter().cloned()); + // } + // CalendarObjectComponent::Journal(journal, overrides) => { + // ical_calendar_builder = ical_calendar_builder + // .add_journal(journal.clone()) + // .add_journals(overrides.iter().cloned()); + // } + // } + // } + // + // ical_calendar_builder = ical_calendar_builder.add_timezones(vtimezones.into_values().cloned()); + // + // let ical_calendar = ical_calendar_builder + // .build() + // .map_err(|parser_error| Error::IcalError(parser_error.into()))?; + // + // let mut resp = Response::builder().status(StatusCode::OK); + // let hdrs = resp.headers_mut().unwrap(); + // hdrs.typed_insert(ContentType::from_str("text/calendar; charset=utf-8").unwrap()); + // + // let filename = format!("{}_{}.ics", calendar.principal, calendar.id); + // let filename = utf8_percent_encode(&filename, CONTROLS); + // hdrs.insert( + // header::CONTENT_DISPOSITION, + // HeaderValue::from_str(&format!( + // "attachement; filename*=UTF-8''{filename}; filename={filename}", + // )) + // .unwrap(), + // ); + // if matches!(method, Method::HEAD) { + // Ok(resp.body(Body::empty()).unwrap()) + // } else { + // Ok(resp.body(Body::new(ical_calendar.generate())).unwrap()) + // } } diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index deb3b05..fe4f153 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -5,10 +5,7 @@ use axum::{ response::{IntoResponse, Response}, }; use http::StatusCode; -use ical::{ - generator::Emitter, - parser::{Component, ComponentMut}, -}; +use ical::{generator::Emitter, parser::Component}; use rustical_dav::header::Overwrite; use rustical_ical::{CalendarObject, CalendarObjectType}; use rustical_store::{ @@ -53,58 +50,59 @@ pub async fn route_import( .get_property("X-WR-TIMEZONE") .and_then(|prop| prop.value.clone()); // These properties should not appear in the expanded calendar objects - cal.remove_property("X-WR-CALNAME"); - cal.remove_property("X-WR-CALDESC"); - cal.remove_property("X-WR-TIMEZONE"); - let cal = cal.verify().unwrap(); - // Make sure timezone is valid - if let Some(timezone_id) = timezone_id.as_ref() { - assert!( - vtimezones_rs::VTIMEZONES.contains_key(timezone_id), - "Invalid calendar timezone id" - ); - } - - // Extract necessary component types - let mut cal_components = vec![]; - if !cal.events.is_empty() { - cal_components.push(CalendarObjectType::Event); - } - if !cal.journals.is_empty() { - cal_components.push(CalendarObjectType::Journal); - } - if !cal.todos.is_empty() { - cal_components.push(CalendarObjectType::Todo); - } - - let expanded_cals = cal.expand_calendar(); - // Janky way to convert between IcalCalendar and CalendarObject - let objects = expanded_cals - .into_iter() - .map(|cal| cal.generate()) - .map(|ics| CalendarObject::from_ics(ics, None)) - .collect::, _>>()?; - let new_cal = Calendar { - principal, - id: cal_id, - meta: CalendarMetadata { - displayname, - order: 0, - description, - color: None, - }, - timezone_id, - deleted_at: None, - synctoken: 0, - subscription_url: None, - push_topic: uuid::Uuid::new_v4().to_string(), - components: cal_components, - }; - - let cal_store = resource_service.cal_store; - cal_store - .import_calendar(new_cal, objects, overwrite) - .await?; - - Ok(StatusCode::OK.into_response()) + todo!(); + // cal.remove_property("X-WR-CALNAME"); + // cal.remove_property("X-WR-CALDESC"); + // cal.remove_property("X-WR-TIMEZONE"); + // let cal = cal.verify().unwrap(); + // // Make sure timezone is valid + // if let Some(timezone_id) = timezone_id.as_ref() { + // assert!( + // vtimezones_rs::VTIMEZONES.contains_key(timezone_id), + // "Invalid calendar timezone id" + // ); + // } + // + // // Extract necessary component types + // let mut cal_components = vec![]; + // if !cal.events.is_empty() { + // cal_components.push(CalendarObjectType::Event); + // } + // if !cal.journals.is_empty() { + // cal_components.push(CalendarObjectType::Journal); + // } + // if !cal.todos.is_empty() { + // cal_components.push(CalendarObjectType::Todo); + // } + // + // let expanded_cals = cal.expand_calendar(); + // // Janky way to convert between IcalCalendar and CalendarObject + // let objects = expanded_cals + // .into_iter() + // .map(|cal| cal.generate()) + // .map(|ics| CalendarObject::from_ics(ics, None)) + // .collect::, _>>()?; + // let new_cal = Calendar { + // principal, + // id: cal_id, + // meta: CalendarMetadata { + // displayname, + // order: 0, + // description, + // color: None, + // }, + // timezone_id, + // deleted_at: None, + // synctoken: 0, + // subscription_url: None, + // push_topic: uuid::Uuid::new_v4().to_string(), + // components: cal_components, + // }; + // + // let cal_store = resource_service.cal_store; + // cal_store + // .import_calendar(new_cal, objects, overwrite) + // .await?; + // + // Ok(StatusCode::OK.into_response()) } diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs index 0847a57..71a8a09 100644 --- a/crates/caldav/src/calendar/methods/mkcalendar.rs +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -92,10 +92,11 @@ pub async fn route_mkcalendar( .ok_or_else(|| rustical_dav::Error::BadRequest("No timezone data provided".to_owned()))? .map_err(|_| rustical_dav::Error::BadRequest("Error parsing timezone".to_owned()))?; - let timezone = calendar.timezones.first().ok_or_else(|| { + let timezone = calendar.vtimezones.first().ok_or_else(|| { rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) })?; - let timezone: chrono_tz::Tz = timezone.try_into().map_err(|_| { + let timezone: Option = timezone.into(); + let timezone = timezone.ok_or_else(|| { rustical_dav::Error::BadRequest("Cannot translate VTIMEZONE into IANA TZID".to_owned()) })?; diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs index cc34a88..7e773c8 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs @@ -1,9 +1,10 @@ use crate::calendar::methods::report::calendar_query::{ - TimeRangeElement, - prop_filter::{PropFilterElement, PropFilterable}, + TimeRangeElement, prop_filter::PropFilterElement, +}; +use ical::{ + component::IcalCalendarObject, + parser::{Component, ical::component::IcalTimeZone}, }; -use ical::parser::ical::component::IcalTimeZone; -use rustical_ical::{CalendarObject, CalendarObjectComponent, CalendarObjectType}; use rustical_xml::XmlDeserialize; #[derive(XmlDeserialize, Clone, Debug, PartialEq)] @@ -24,9 +25,7 @@ pub struct CompFilterElement { pub(crate) name: String, } -pub trait CompFilterable: PropFilterable + Sized { - fn get_comp_name(&self) -> &'static str; - +pub trait CompFilterable: Component + Sized { fn match_time_range(&self, time_range: &TimeRangeElement) -> bool; fn match_subcomponents(&self, comp_filter: &CompFilterElement) -> bool; @@ -68,11 +67,7 @@ pub trait CompFilterable: PropFilterable + Sized { } } -impl CompFilterable for CalendarObject { - fn get_comp_name(&self) -> &'static str { - "VCALENDAR" - } - +impl CompFilterable for IcalCalendarObject { fn match_time_range(&self, _time_range: &TimeRangeElement) -> bool { // VCALENDAR has no concept of time range false @@ -83,7 +78,7 @@ impl CompFilterable for CalendarObject { .get_vtimezones() .values() .map(|tz| tz.matches(comp_filter)) - .chain([self.get_data().matches(comp_filter)]); + .chain([self.matches(comp_filter)]); if comp_filter.is_not_defined.is_some() { matches.all(|x| x) @@ -94,10 +89,6 @@ impl CompFilterable for CalendarObject { } impl CompFilterable for IcalTimeZone { - fn get_comp_name(&self) -> &'static str { - "VTIMEZONE" - } - fn match_time_range(&self, _time_range: &TimeRangeElement) -> bool { false } @@ -107,33 +98,6 @@ impl CompFilterable for IcalTimeZone { } } -impl CompFilterable for CalendarObjectComponent { - fn get_comp_name(&self) -> &'static str { - CalendarObjectType::from(self).as_str() - } - - fn match_time_range(&self, time_range: &TimeRangeElement) -> bool { - if let Some(start) = &time_range.start - && let Some(last_occurence) = self.get_last_occurence().unwrap_or(None) - && **start > last_occurence.utc() - { - return false; - } - if let Some(end) = &time_range.end - && let Some(first_occurence) = self.get_first_occurence().unwrap_or(None) - && **end < first_occurence.utc() - { - return false; - } - true - } - - fn match_subcomponents(&self, _comp_filter: &CompFilterElement) -> bool { - // TODO: Properly check subcomponents - true - } -} - #[cfg(test)] mod tests { use chrono::{TimeZone, Utc}; diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs b/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs index d9ca17b..48f1c59 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/elements.rs @@ -1,8 +1,8 @@ use super::comp_filter::{CompFilterElement, CompFilterable}; use crate::calendar_object::CalendarObjectPropWrapperName; -use ical::property::Property; +use ical::{component::IcalCalendarObject, property::ContentLine}; use rustical_dav::xml::{PropfindType, TextMatchElement}; -use rustical_ical::{CalendarObject, UtcDateTime}; +use rustical_ical::UtcDateTime; use rustical_store::calendar_store::CalendarQuery; use rustical_xml::{XmlDeserialize, XmlRootTag}; @@ -30,8 +30,8 @@ pub struct ParamFilterElement { impl ParamFilterElement { #[must_use] - pub fn match_property(&self, prop: &Property) -> bool { - let Some(param) = prop.get_param(&self.name) else { + pub fn match_property(&self, prop: &ContentLine) -> bool { + let Some(param) = prop.params.get_param(&self.name) else { return self.is_not_defined.is_some(); }; if self.is_not_defined.is_some() { @@ -57,7 +57,7 @@ pub struct FilterElement { impl FilterElement { #[must_use] - pub fn matches(&self, cal_object: &CalendarObject) -> bool { + pub fn matches(&self, cal_object: &IcalCalendarObject) -> bool { cal_object.matches(&self.comp_filter) } } diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs index d46d271..b674142 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs @@ -11,7 +11,7 @@ mod tests; pub use comp_filter::{CompFilterElement, CompFilterable}; pub use elements::*; #[allow(unused_imports)] -pub use prop_filter::{PropFilterElement, PropFilterable}; +pub use prop_filter::PropFilterElement; pub async fn get_objects_calendar_query( cal_query: &CalendarQueryRequest, @@ -23,7 +23,7 @@ pub async fn get_objects_calendar_query( .calendar_query(principal, cal_id, cal_query.into()) .await?; if let Some(filter) = &cal_query.filter { - objects.retain(|object| filter.matches(object)); + objects.retain(|object| filter.matches(object.get_inner())); } Ok(objects) } diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs b/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs index 406166d..889d0ee 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs @@ -1,14 +1,7 @@ use super::{ParamFilterElement, TimeRangeElement}; -use ical::{ - generator::{IcalCalendar, IcalEvent}, - parser::{ - Component, - ical::component::{IcalJournal, IcalTimeZone, IcalTodo}, - }, - property::Property, -}; +use ical::{parser::Component, property::ContentLine, types::CalDateTime}; use rustical_dav::xml::TextMatchElement; -use rustical_ical::{CalDateTime, CalendarObject, CalendarObjectComponent, UtcDateTime}; +use rustical_ical::UtcDateTime; use rustical_xml::XmlDeserialize; use std::collections::HashMap; @@ -31,7 +24,7 @@ pub struct PropFilterElement { impl PropFilterElement { #[must_use] - pub fn match_property(&self, property: &Property) -> bool { + pub fn match_property(&self, property: &ContentLine) -> bool { if let Some(TimeRangeElement { start, end }) = &self.time_range { // TODO: Respect timezones let Ok(timestamp) = CalDateTime::parse_prop(property, &HashMap::default()) else { @@ -68,7 +61,7 @@ impl PropFilterElement { true } - pub fn match_component(&self, comp: &impl PropFilterable) -> bool { + pub fn match_component(&self, comp: &impl Component) -> bool { let properties = comp.get_named_properties(&self.name); if self.is_not_defined.is_some() { return properties.is_empty(); @@ -79,53 +72,3 @@ impl PropFilterElement { properties.iter().any(|prop| self.match_property(prop)) } } - -pub trait PropFilterable { - fn get_named_properties(&self, name: &str) -> Vec<&Property>; -} - -impl PropFilterable for CalendarObject { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { - Self::get_named_properties(self, name) - } -} - -impl PropFilterable for IcalEvent { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { - Component::get_named_properties(self, name) - } -} - -impl PropFilterable for IcalTodo { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { - Component::get_named_properties(self, name) - } -} - -impl PropFilterable for IcalJournal { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { - Component::get_named_properties(self, name) - } -} - -impl PropFilterable for IcalCalendar { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { - Component::get_named_properties(self, name) - } -} - -impl PropFilterable for IcalTimeZone { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { - Component::get_named_properties(self, name) - } -} - -impl PropFilterable for CalendarObjectComponent { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { - match self { - Self::Event(event, _) => PropFilterable::get_named_properties(&event.event, name), - Self::Todo(todo, _) => PropFilterable::get_named_properties(todo, name), - Self::Journal(journal, _) => PropFilterable::get_named_properties(journal, name), - } - } -} diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index c1a59eb..cb37d71 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -4,6 +4,7 @@ use crate::calendar::prop::{ReportMethod, SupportedCollationSet}; 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, }; @@ -11,7 +12,6 @@ use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{PrincipalUri, Resource, ResourceName}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner, SupportedReportSet}; use rustical_dav_push::{DavPushExtension, DavPushExtensionProp}; -use rustical_ical::CalDateTime; use rustical_store::Calendar; use rustical_store::auth::Principal; use rustical_xml::{EnumVariants, PropName}; @@ -215,13 +215,13 @@ impl Resource for CalendarResource { ) })?; - let timezone = calendar.timezones.first().ok_or_else(|| { + let timezone = calendar.vtimezones.first().ok_or_else(|| { rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) })?; - let timezone: chrono_tz::Tz = timezone.try_into().map_err(|_| { + let timezone: Option = timezone.into(); + let timezone = timezone.ok_or_else(|| { rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) })?; - self.cal.timezone_id = Some(timezone.name().to_owned()); } Ok(()) diff --git a/crates/carddav/src/addressbook/methods/import.rs b/crates/carddav/src/addressbook/methods/import.rs index ff7dff2..3f4bc2a 100644 --- a/crates/carddav/src/addressbook/methods/import.rs +++ b/crates/carddav/src/addressbook/methods/import.rs @@ -1,4 +1,4 @@ -use std::io::BufReader; +use std::{collections::HashMap, io::BufReader}; use crate::Error; use crate::addressbook::AddressbookResourceService; @@ -9,7 +9,7 @@ use axum::{ use http::StatusCode; use ical::{ parser::{Component, ComponentMut, vcard}, - property::Property, + property::ContentLine, }; use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore, auth::Principal}; use tracing::instrument; @@ -33,12 +33,12 @@ pub async fn route_import( let uid = card.get_uid(); if uid.is_none() { let mut card_mut = card.mutable(); - card_mut.set_property(Property { + card_mut.add_content_line(ContentLine { name: "UID".to_owned(), value: Some(uuid::Uuid::new_v4().to_string()), - params: vec![], + params: vec![].into(), }); - card = card_mut.verify().unwrap(); + card = card_mut.build(&HashMap::new()).unwrap(); } objects.push(card.try_into().unwrap()); diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_query/elements.rs b/crates/carddav/src/addressbook/methods/report/addressbook_query/elements.rs index b294482..b89985b 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_query/elements.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_query/elements.rs @@ -3,7 +3,7 @@ use crate::{ addressbook::methods::report::addressbook_query::PropFilterElement, }; use derive_more::{From, Into}; -use ical::property::Property; +use ical::property::ContentLine; use rustical_dav::xml::{PropfindType, TextMatchElement}; use rustical_ical::{AddressObject, UtcDateTime}; use rustical_xml::{ValueDeserialize, XmlDeserialize, XmlRootTag}; @@ -32,8 +32,8 @@ pub struct ParamFilterElement { impl ParamFilterElement { #[must_use] - pub fn match_property(&self, prop: &Property) -> bool { - let Some(param) = prop.get_param(&self.name) else { + pub fn match_property(&self, prop: &ContentLine) -> bool { + let Some(param) = prop.params.get_param(&self.name) else { return self.is_not_defined.is_some(); }; if self.is_not_defined.is_some() { diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs b/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs index e8de84d..eec0e96 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs @@ -1,5 +1,5 @@ use super::{Allof, ParamFilterElement}; -use ical::{parser::Component, property::Property}; +use ical::{parser::Component, property::ContentLine}; use rustical_dav::xml::TextMatchElement; use rustical_ical::AddressObject; use rustical_xml::XmlDeserialize; @@ -31,7 +31,7 @@ pub struct PropFilterElement { impl PropFilterElement { #[must_use] - pub fn match_property(&self, property: &Property) -> bool { + pub fn match_property(&self, property: &ContentLine) -> bool { if self.param_filter.is_empty() && self.text_match.is_empty() { // Filter empty return true; @@ -67,11 +67,11 @@ impl PropFilterElement { } pub trait PropFilterable { - fn get_named_properties(&self, name: &str) -> Vec<&Property>; + fn get_named_properties(&self, name: &str) -> Vec<&ContentLine>; } impl PropFilterable for AddressObject { - fn get_named_properties(&self, name: &str) -> Vec<&Property> { + fn get_named_properties(&self, name: &str) -> Vec<&ContentLine> { self.get_vcard().get_named_properties(name) } } diff --git a/crates/dav/src/xml/text_match.rs b/crates/dav/src/xml/text_match.rs index a8db120..2188fe5 100644 --- a/crates/dav/src/xml/text_match.rs +++ b/crates/dav/src/xml/text_match.rs @@ -1,4 +1,4 @@ -use ical::property::Property; +use ical::property::ContentLine; use rustical_xml::{ValueDeserialize, XmlDeserialize}; use std::borrow::Cow; @@ -128,7 +128,7 @@ impl TextMatchElement { negate_condition.0 ^ matches } #[must_use] - pub fn match_property(&self, property: &Property) -> bool { + pub fn match_property(&self, property: &ContentLine) -> bool { let text = property.value.as_deref().unwrap_or(""); self.match_text(text) } diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index b0c5450..9f63fb6 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -1,11 +1,10 @@ -use crate::{CalDateTime, LOCAL_DATE}; use crate::{CalendarObject, Error}; -use chrono::Datelike; use ical::generator::Emitter; use ical::parser::{ Component, vcard::{self, component::VcardContact}, }; +use ical::types::CalDateTime; use sha2::{Digest, Sha256}; use std::{collections::HashMap, io::BufReader}; @@ -65,14 +64,16 @@ impl AddressObject { #[must_use] pub fn get_anniversary(&self) -> Option<(CalDateTime, bool)> { - let prop = self.vcard.get_property("ANNIVERSARY")?.value.as_deref()?; - CalDateTime::parse_vcard(prop).ok() + // let prop = self.vcard.get_property("ANNIVERSARY")?.value.as_deref()?; + // CalDateTime::parse_vcard(prop).ok() + todo!() } #[must_use] pub fn get_birthday(&self) -> Option<(CalDateTime, bool)> { - let prop = self.vcard.get_property("BDAY")?.value.as_deref()?; - CalDateTime::parse_vcard(prop).ok() + todo!() + // let prop = self.vcard.get_property("BDAY")?.value.as_deref()?; + // CalDateTime::parse_vcard(prop).ok() } #[must_use] @@ -82,90 +83,11 @@ impl AddressObject { } pub fn get_anniversary_object(&self) -> Result, Error> { - Ok( - if let Some((anniversary, contains_year)) = self.get_anniversary() { - let Some(fullname) = self.get_full_name() else { - return Ok(None); - }; - let anniversary = anniversary.date(); - let year = contains_year.then_some(anniversary.year()); - let anniversary_start = anniversary.format(LOCAL_DATE); - let anniversary_end = anniversary - .succ_opt() - .unwrap_or(anniversary) - .format(LOCAL_DATE); - let uid = format!("{}-anniversary", self.get_id()); - - let year_suffix = year.map(|year| format!(" ({year})")).unwrap_or_default(); - Some(CalendarObject::from_ics( - format!( - r"BEGIN:VCALENDAR -VERSION:2.0 -CALSCALE:GREGORIAN -PRODID:-//github.com/lennart-k/rustical birthday calendar//EN -BEGIN:VEVENT -DTSTART;VALUE=DATE:{anniversary_start} -DTEND;VALUE=DATE:{anniversary_end} -UID:{uid} -RRULE:FREQ=YEARLY -SUMMARY:💍 {fullname}{year_suffix} -TRANSP:TRANSPARENT -BEGIN:VALARM -TRIGGER;VALUE=DURATION:-PT0M -ACTION:DISPLAY -DESCRIPTION:💍 {fullname}{year_suffix} -END:VALARM -END:VEVENT -END:VCALENDAR", - ), - None, - )?) - } else { - None - }, - ) + todo!(); } pub fn get_birthday_object(&self) -> Result, Error> { - Ok( - if let Some((birthday, contains_year)) = self.get_birthday() { - let Some(fullname) = self.get_full_name() else { - return Ok(None); - }; - let birthday = birthday.date(); - let year = contains_year.then_some(birthday.year()); - let birthday_start = birthday.format(LOCAL_DATE); - let birthday_end = birthday.succ_opt().unwrap_or(birthday).format(LOCAL_DATE); - let uid = format!("{}-birthday", self.get_id()); - - let year_suffix = year.map(|year| format!(" ({year})")).unwrap_or_default(); - Some(CalendarObject::from_ics( - format!( - r"BEGIN:VCALENDAR -VERSION:2.0 -CALSCALE:GREGORIAN -PRODID:-//github.com/lennart-k/rustical birthday calendar//EN -BEGIN:VEVENT -DTSTART;VALUE=DATE:{birthday_start} -DTEND;VALUE=DATE:{birthday_end} -UID:{uid} -RRULE:FREQ=YEARLY -SUMMARY:🎂 {fullname}{year_suffix} -TRANSP:TRANSPARENT -BEGIN:VALARM -TRIGGER;VALUE=DURATION:-PT0M -ACTION:DISPLAY -DESCRIPTION:🎂 {fullname}{year_suffix} -END:VALARM -END:VEVENT -END:VCALENDAR", - ), - None, - )?) - } else { - None - }, - ) + todo!(); } /// Get significant dates associated with this address object diff --git a/crates/ical/src/icalendar/event.rs b/crates/ical/src/icalendar/event.rs deleted file mode 100644 index 2b2d929..0000000 --- a/crates/ical/src/icalendar/event.rs +++ /dev/null @@ -1,386 +0,0 @@ -use crate::CalDateTime; -use crate::Error; -use chrono::{DateTime, Duration, Utc}; -use ical::parser::ComponentMut; -use ical::{generator::IcalEvent, parser::Component, property::Property}; -use rrule::{RRule, RRuleSet}; -use std::{collections::HashMap, str::FromStr}; - -#[derive(Debug, Clone, Default)] -pub struct EventObject { - pub event: IcalEvent, - // If a timezone is None that means that in the VCALENDAR object there's a timezone defined - // with that name but its not from the Olson DB - pub timezones: HashMap>, -} - -impl EventObject { - #[must_use] - pub fn get_uid(&self) -> &str { - self.event.get_uid() - } - - pub fn get_dtstart(&self) -> Result, Error> { - if let Some(dtstart) = self.event.get_dtstart() { - Ok(Some(CalDateTime::parse_prop(dtstart, &self.timezones)?)) - } else { - Ok(None) - } - } - - pub fn get_dtend(&self) -> Result, Error> { - if let Some(dtend) = self.event.get_dtend() { - Ok(Some(CalDateTime::parse_prop(dtend, &self.timezones)?)) - } else { - Ok(None) - } - } - - pub fn get_last_occurence(&self) -> Result, Error> { - if self.event.get_rrule().is_some() { - // TODO: understand recurrence rules - return Ok(None); - } - - if let Some(dtend) = self.get_dtend()? { - return Ok(Some(dtend)); - } - - 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 recurrence_ruleset(&self) -> Result, Error> { - let dtstart: DateTime = if let Some(dtstart) = self.get_dtstart()? { - if let Some(dtend) = self.get_dtend()? { - // DTSTART and DTEND MUST have the same timezone - assert_eq!(dtstart.timezone(), dtend.timezone()); - } - - dtstart - .as_datetime() - .with_timezone(&dtstart.timezone().into()) - } else { - return Ok(None); - }; - - let mut rrule_set = RRuleSet::new(dtstart); - // TODO: Make nice, this is just a bodge to get correct behaviour - let mut empty = true; - - for prop in &self.event.properties { - rrule_set = match prop.name.as_str() { - "RRULE" => { - let rrule = RRule::from_str(prop.value.as_ref().ok_or_else(|| { - Error::RRuleError(rrule::ParseError::MissingDateGenerationRules.into()) - })?)? - .validate(dtstart) - .unwrap(); - empty = false; - rrule_set.rrule(rrule) - } - "RDATE" => { - let rdate = CalDateTime::parse_prop(prop, &self.timezones)?.into(); - empty = false; - rrule_set.rdate(rdate) - } - "EXDATE" => { - let exdate = CalDateTime::parse_prop(prop, &self.timezones)?.into(); - empty = false; - rrule_set.exdate(exdate) - } - _ => rrule_set, - } - } - if empty { - return Ok(None); - } - - Ok(Some(rrule_set)) - } - - // The returned calendar components MUST NOT use recurrence - // properties (i.e., EXDATE, EXRULE, RDATE, and RRULE) and MUST NOT - // have reference to or include VTIMEZONE components. Date and local - // time with reference to time zone information MUST be converted - // into date with UTC time. - pub fn expand_recurrence( - &self, - start: Option>, - end: Option>, - overrides: &[Self], - ) -> Result, Error> { - let mut events = vec![]; - let dtstart = self.get_dtstart()?.expect("We must have a DTSTART here"); - let computed_duration = self - .get_dtend()? - .map(|dtend| dtend.as_datetime().into_owned() - dtstart.as_datetime().as_ref()); - - let Some(mut rrule_set) = self.recurrence_ruleset()? else { - // If ruleset empty simply return main event AND all overrides - return Ok(std::iter::once(self.clone()) - .chain(overrides.iter().cloned()) - .map(|event| event.event) - .collect()); - }; - if let Some(start) = start { - rrule_set = rrule_set.after(start.with_timezone(&rrule::Tz::UTC)); - } - if let Some(end) = end { - rrule_set = rrule_set.before(end.with_timezone(&rrule::Tz::UTC)); - } - let dates = rrule_set.all(2048).dates; - 'recurrence: for date in dates { - let date = CalDateTime::from(date.to_utc()); - let recurrence_id = if dtstart.is_date() { - date.format_date() - } else { - date.format() - }; - - for ev_override in overrides { - if let Some(override_id) = &ev_override - .event - .get_recurrence_id() - .as_ref() - .expect("overrides have a recurrence id") - .value - && override_id == &recurrence_id - { - // We have an override for this occurence - // - events.push(ev_override.event.clone()); - continue 'recurrence; - } - } - - let mut ev = self.event.clone().mutable(); - ev.remove_property("RRULE"); - ev.remove_property("RDATE"); - ev.remove_property("EXDATE"); - ev.remove_property("EXRULE"); - let dtstart_prop = ev - .get_property("DTSTART") - .expect("We must have a DTSTART here") - .clone(); - ev.remove_property("DTSTART"); - ev.remove_property("DTEND"); - - ev.set_property(Property { - name: "RECURRENCE-ID".to_string(), - value: Some(recurrence_id.clone()), - params: vec![], - }); - ev.set_property(Property { - name: "DTSTART".to_string(), - value: Some(recurrence_id), - params: vec![], - }); - if let Some(duration) = computed_duration { - let dtend = date + duration; - let dtendformat = if dtstart.is_date() { - dtend.format_date() - } else { - dtend.format() - }; - ev.set_property(Property { - name: "DTEND".to_string(), - value: Some(dtendformat), - params: dtstart_prop.params, - }); - } - events.push(ev.verify()?); - } - Ok(events) - } -} - -#[cfg(test)] -mod tests { - use crate::{CalDateTime, CalendarObject}; - use chrono::{DateTime, Utc}; - use ical::generator::Emitter; - use rstest::rstest; - - const ICS_1: &str = r"BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VTIMEZONE -TZID:Europe/Berlin -X-LIC-LOCATION:Europe/Berlin -END:VTIMEZONE - -BEGIN:VEVENT -UID:318ec6503573d9576818daf93dac07317058d95c -DTSTAMP:20250502T132758Z -DTSTART;TZID=Europe/Berlin:20250506T090000 -DTEND;TZID=Europe/Berlin:20250506T092500 -SEQUENCE:2 -SUMMARY:weekly stuff -TRANSP:OPAQUE -RRULE:FREQ=WEEKLY;COUNT=4;INTERVAL=2;BYDAY=TU,TH,SU -END:VEVENT -END:VCALENDAR"; - - const EXPANDED_1: &[&str] = &[ - "BEGIN:VEVENT\r -UID:318ec6503573d9576818daf93dac07317058d95c\r -DTSTAMP:20250502T132758Z\r -SEQUENCE:2\r -SUMMARY:weekly stuff\r -TRANSP:OPAQUE\r -RECURRENCE-ID:20250506T070000Z\r -DTSTART:20250506T070000Z\r -DTEND:20250506T072500Z\r -END:VEVENT\r\n", - "BEGIN:VEVENT\r -UID:318ec6503573d9576818daf93dac07317058d95c\r -DTSTAMP:20250502T132758Z\r -SEQUENCE:2\r -SUMMARY:weekly stuff\r -TRANSP:OPAQUE\r -RECURRENCE-ID:20250508T070000Z\r -DTSTART:20250508T070000Z\r -DTEND:20250508T072500Z\r -END:VEVENT\r\n", - "BEGIN:VEVENT\r -UID:318ec6503573d9576818daf93dac07317058d95c\r -DTSTAMP:20250502T132758Z\r -SEQUENCE:2\r -SUMMARY:weekly stuff\r -TRANSP:OPAQUE\r -RECURRENCE-ID:20250511T090000\r -DTSTART:20250511T070000Z\r -DTEND:20250511T072500Z\r -END:VEVENT\r\n", - "BEGIN:VEVENT\r -UID:318ec6503573d9576818daf93dac07317058d95c\r -DTSTAMP:20250502T132758Z\r -SEQUENCE:2\r -SUMMARY:weekly stuff\r -TRANSP:OPAQUE\r -RECURRENCE-ID:20250520T090000\r -DTSTA:20250520T070000Z\r -DTEND:20250520T072500Z\r -END:VEVENT\r\n", - ]; - - const ICS_2: &str = r"BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VTIMEZONE -TZID:US/Eastern -END:VTIMEZONE -BEGIN:VEVENT -DTSTAMP:20060206T001121Z -DTSTART;TZID=US/Eastern:20060102T120000 -DURATION:PT1H -RRULE:FREQ=DAILY;COUNT=5 -SUMMARY:Event #2 -UID:abcd2 -END:VEVENT -BEGIN:VEVENT -DTSTAMP:20060206T001121Z -DTSTART;TZID=US/Eastern:20060104T140000 -DURATION:PT1H -RECURRENCE-ID;TZID=US/Eastern:20060104T120000 -SUMMARY:Event #2 bis -UID:abcd2 -END:VEVENT -END:VCALENDAR -"; - - const EXPANDED_2: &[&str] = &[ - "BEGIN:VEVENT\r -DTSTAMP:20060206T001121Z\r -DURATION:PT1H\r -SUMMARY:Event #2\r -UID:abcd2\r -RECURRENCE-ID:20060103T170000\r -DTSTART:20060103T170000\r -END:VEVENT\r\n", - "BEGIN:VEVENT\r -DTSTAMP:20060206T001121Z\r -DURATION:PT1H\r -SUMMARY:Event #2 bis\r -UID:abcd2\r -RECURRENCE-ID:20060104T170000\r -DTSTART:20060104T190000\r -END:VEVENT\r -END:VCALENDAR\r\n", - ]; - - const ICS_3: &str = r"BEGIN:VCALENDAR -CALSCALE:GREGORIAN -VERSION:2.0 -BEGIN:VTIMEZONE -TZID:US/Eastern -END:VTIMEZONE -BEGIN:VEVENT -ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com -ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com -DTSTAMP:20060206T001220Z -DTSTART;TZID=US/Eastern:20060104T100000 -DURATION:PT1H -LAST-MODIFIED:20060206T001330Z -ORGANIZER:mailto:cyrus@example.com -SEQUENCE:1 -STATUS:TENTATIVE -SUMMARY:Event #3 -UID:abcd3 -END:VEVENT -END:VCALENDAR -"; - - const EXPANDED_3: &[&str] = &["BEGIN:VEVENT -ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com -ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com -DTSTAMP:20060206T001220Z -DTSTART:20060104T150000 -DURATION:PT1H -LAST-MODIFIED:20060206T001330Z -ORGANIZER:mailto:cyrus@example.com -SEQUENCE:1 -STATUS:TENTATIVE -SUMMARY:Event #3 -UID:abcd3 -X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com -END:VEVENT"]; - - // The implementation never was entirely correct but will be fixed in v0.12 - // #[rstest] - // #[case(ICS_1, EXPANDED_1, None, None)] - // // from https://datatracker.ietf.org/doc/html/rfc4791#section-7.8.3 - // #[case(ICS_2, EXPANDED_2, - // Some(CalDateTime::parse("20060103T000000Z", Some(chrono_tz::US::Eastern)).unwrap().utc()), - // Some(CalDateTime::parse("20060105T000000Z", Some(chrono_tz::US::Eastern)).unwrap().utc()) - // )] - // #[case(ICS_3, EXPANDED_3, - // Some(CalDateTime::parse("20060103T000000Z", Some(chrono_tz::US::Eastern)).unwrap().utc()), - // Some(CalDateTime::parse("20060105T000000Z", Some(chrono_tz::US::Eastern)).unwrap().utc()) - // )] - // fn test_expand_recurrence( - // #[case] ics: &'static str, - // #[case] expanded: &[&str], - // #[case] from: Option>, - // #[case] to: Option>, - // ) { - // let event = CalendarObject::from_ics(ics.to_string(), None).unwrap(); - // let crate::CalendarObjectComponent::Event(event, overrides) = event.get_data() else { - // panic!() - // }; - // - // let events: Vec = event - // .expand_recurrence(from, to, overrides) - // .unwrap() - // .into_iter() - // .map(|event| Emitter::generate(&event)) - // .collect(); - // assert_eq!(events.len(), expanded.len()); - // for (output, reference) in events.iter().zip(expanded) { - // similar_asserts::assert_eq!(output, reference); - // } - // } -} diff --git a/crates/ical/src/icalendar/mod.rs b/crates/ical/src/icalendar/mod.rs index 4883949..75ce6bc 100644 --- a/crates/ical/src/icalendar/mod.rs +++ b/crates/ical/src/icalendar/mod.rs @@ -1,5 +1,3 @@ -mod event; mod object; -pub use event::*; pub use object::*; diff --git a/crates/ical/src/icalendar/object.rs b/crates/ical/src/icalendar/object.rs index fa99059..f959d1a 100644 --- a/crates/ical/src/icalendar/object.rs +++ b/crates/ical/src/icalendar/object.rs @@ -1,18 +1,14 @@ -use super::EventObject; -use crate::CalDateTime; use crate::Error; use chrono::DateTime; use chrono::Utc; use derive_more::Display; -use ical::generator::{Emitter, IcalCalendar}; -use ical::parser::ical::component::IcalJournal; -use ical::parser::ical::component::IcalTimeZone; -use ical::parser::ical::component::IcalTodo; -use ical::property::Property; +use ical::component::CalendarInnerData; +use ical::component::IcalCalendarObject; +use ical::parser::ComponentParser; +use ical::types::CalDateTime; use serde::Deserialize; use serde::Serialize; use sha2::{Digest, Sha256}; -use std::{collections::HashMap, io::BufReader}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Display)] // specified in https://datatracker.ietf.org/doc/html/rfc5545#section-3.6 @@ -25,6 +21,16 @@ pub enum CalendarObjectType { Journal = 2, } +impl From<&IcalCalendarObject> for CalendarObjectType { + fn from(value: &IcalCalendarObject) -> Self { + match value.get_inner() { + CalendarInnerData::Event(_, _) => Self::Event, + CalendarInnerData::Todo(_, _) => Self::Todo, + CalendarInnerData::Journal(_, _) => Self::Journal, + } + } +} + impl CalendarObjectType { #[must_use] pub const fn as_str(&self) -> &'static str { @@ -57,241 +63,39 @@ impl rustical_xml::ValueDeserialize for CalendarObjectType { } } -#[derive(Debug, Clone)] -pub enum CalendarObjectComponent { - Event(EventObject, Vec), - Todo(IcalTodo, Vec), - Journal(IcalJournal, Vec), -} - -impl CalendarObjectComponent { - #[must_use] - pub fn get_uid(&self) -> &str { - match &self { - // We've made sure before that the first component exists and all components share the - // same UID - Self::Todo(todo, _) => todo.get_uid(), - Self::Event(event, _) => event.event.get_uid(), - Self::Journal(journal, _) => journal.get_uid(), - } - } -} - -impl From<&CalendarObjectComponent> for CalendarObjectType { - fn from(value: &CalendarObjectComponent) -> Self { - match value { - CalendarObjectComponent::Event(..) => Self::Event, - CalendarObjectComponent::Todo(..) => Self::Todo, - CalendarObjectComponent::Journal(..) => Self::Journal, - } - } -} - -impl CalendarObjectComponent { - fn from_events(mut events: Vec) -> Result { - // A calendar object does not necessarily have to contain a main VOBJECT - if events.is_empty() { - return Err(Error::MissingCalendar); - } - #[allow(clippy::option_if_let_else)] - let main_event = if let Some(main) = events - .extract_if(.., |event| event.event.get_recurrence_id().is_none()) - .next() - { - main - } else { - events.remove(0) - }; - let overrides = events; - for event in &overrides { - if event.get_uid() != main_event.get_uid() { - return Err(Error::InvalidData( - "Calendar object contains multiple UIDs".to_owned(), - )); - } - if event.event.get_recurrence_id().is_none() { - return Err(Error::InvalidData( - "Calendar object can only contain one main component".to_owned(), - )); - } - } - Ok(Self::Event(main_event, overrides)) - } - fn from_todos(mut todos: Vec) -> Result { - // A calendar object does not necessarily have to contain a main VOBJECT - if todos.is_empty() { - return Err(Error::MissingCalendar); - } - #[allow(clippy::option_if_let_else)] - let main_todo = if let Some(main) = todos - .extract_if(.., |todo| todo.get_recurrence_id().is_none()) - .next() - { - main - } else { - todos.remove(0) - }; - let overrides = todos; - for todo in &overrides { - if todo.get_uid() != main_todo.get_uid() { - return Err(Error::InvalidData( - "Calendar object contains multiple UIDs".to_owned(), - )); - } - if todo.get_recurrence_id().is_none() { - return Err(Error::InvalidData( - "Calendar object can only contain one main component".to_owned(), - )); - } - } - Ok(Self::Todo(main_todo, overrides)) - } - fn from_journals(mut journals: Vec) -> Result { - // A calendar object does not necessarily have to contain a main VOBJECT - if journals.is_empty() { - return Err(Error::MissingCalendar); - } - #[allow(clippy::option_if_let_else)] - let main_journal = if let Some(main) = journals - .extract_if(.., |journal| journal.get_recurrence_id().is_none()) - .next() - { - main - } else { - journals.remove(0) - }; - let overrides = journals; - for journal in &overrides { - if journal.get_uid() != main_journal.get_uid() { - return Err(Error::InvalidData( - "Calendar object contains multiple UIDs".to_owned(), - )); - } - if journal.get_recurrence_id().is_none() { - return Err(Error::InvalidData( - "Calendar object can only contain one main component".to_owned(), - )); - } - } - Ok(Self::Journal(main_journal, overrides)) - } - - pub fn get_first_occurence(&self) -> Result, Error> { - match &self { - Self::Event(main_event, overrides) => Ok(overrides - .iter() - .chain(std::iter::once(main_event)) - .map(super::event::EventObject::get_dtstart) - .collect::, _>>()? - .into_iter() - .flatten() - .min()), - _ => Ok(None), - } - } - - pub fn get_last_occurence(&self) -> Result, Error> { - match &self { - Self::Event(main_event, overrides) => Ok(overrides - .iter() - .chain(std::iter::once(main_event)) - .map(super::event::EventObject::get_last_occurence) - .collect::, _>>()? - .into_iter() - .flatten() - .max()), - _ => Ok(None), - } - } -} - #[derive(Debug, Clone)] pub struct CalendarObject { - data: CalendarObjectComponent, - properties: Vec, id: String, + inner: IcalCalendarObject, ics: String, - vtimezones: HashMap, } impl CalendarObject { pub fn from_ics(ics: String, id: Option) -> Result { - let mut parser = ical::IcalParser::new(BufReader::new(ics.as_bytes())); - let cal = parser.next().ok_or(Error::MissingCalendar)??; + let mut parser: ComponentParser<_, IcalCalendarObject> = + ComponentParser::new(ics.as_bytes()); + let inner = parser.next().ok_or(Error::MissingCalendar)??; if parser.next().is_some() { return Err(Error::InvalidData( "multiple calendars, only one allowed".to_owned(), )); } - if u8::from(!cal.events.is_empty()) - + u8::from(!cal.todos.is_empty()) - + u8::from(!cal.journals.is_empty()) - + u8::from(!cal.free_busys.is_empty()) - != 1 - { - // https://datatracker.ietf.org/doc/html/rfc4791#section-4.1 - return Err(Error::InvalidData( - "iCalendar object must have exactly one component type".to_owned(), - )); - } - - let timezones: HashMap> = cal - .timezones - .clone() - .into_iter() - .map(|timezone| (timezone.get_tzid().to_owned(), (&timezone).try_into().ok())) - .collect(); - - let vtimezones = cal - .timezones - .clone() - .into_iter() - .map(|timezone| (timezone.get_tzid().to_owned(), timezone)) - .collect(); - - let data = if !cal.events.is_empty() { - CalendarObjectComponent::from_events( - cal.events - .into_iter() - .map(|event| EventObject { - event, - timezones: timezones.clone(), - }) - .collect(), - )? - } else if !cal.todos.is_empty() { - CalendarObjectComponent::from_todos(cal.todos)? - } else if !cal.journals.is_empty() { - CalendarObjectComponent::from_journals(cal.journals)? - } else { - return Err(Error::InvalidData( - "iCalendar component type not supported :(".to_owned(), - )); - }; - Ok(Self { - id: id.unwrap_or_else(|| data.get_uid().to_owned()), - data, - properties: cal.properties, + id: id.unwrap_or_else(|| inner.get_uid().to_owned()), + inner, ics, - vtimezones, }) } #[must_use] - pub const fn get_vtimezones(&self) -> &HashMap { - &self.vtimezones - } - - #[must_use] - pub const fn get_data(&self) -> &CalendarObjectComponent { - &self.data + pub const fn get_inner(&self) -> &IcalCalendarObject { + &self.inner } #[must_use] pub fn get_uid(&self) -> &str { - self.data.get_uid() + self.inner.get_uid() } #[must_use] @@ -319,15 +123,17 @@ impl CalendarObject { #[must_use] pub fn get_object_type(&self) -> CalendarObjectType { - (&self.data).into() + (&self.inner).into() } - pub fn get_first_occurence(&self) -> Result, Error> { - self.data.get_first_occurence() + pub fn get_first_occurence(&self) -> Option { + // TODO: Implement + None } - pub fn get_last_occurence(&self) -> Result, Error> { - self.data.get_last_occurence() + pub fn get_last_occurence(&self) -> Option { + // TODO: Implement + None } pub fn expand_recurrence( @@ -335,32 +141,7 @@ impl CalendarObject { start: Option>, end: Option>, ) -> Result { - // Only events can be expanded - match &self.data { - CalendarObjectComponent::Event(main_event, overrides) => { - let cal = IcalCalendar { - properties: self.properties.clone(), - events: main_event.expand_recurrence(start, end, overrides)?, - ..Default::default() - }; - Ok(cal.generate()) - } - _ => Ok(self.get_ics().to_string()), - } - } - - #[must_use] - pub fn get_property(&self, name: &str) -> Option<&Property> { - self.properties - .iter() - .find(|property| property.name == name) - } - - #[must_use] - pub fn get_named_properties(&self, name: &str) -> Vec<&Property> { - self.properties - .iter() - .filter(|property| property.name == name) - .collect() + // Ok(self.inner.expand_recurrence(start, end)?) + todo!() } } diff --git a/crates/ical/src/lib.rs b/crates/ical/src/lib.rs index 9c00d52..42f646f 100644 --- a/crates/ical/src/lib.rs +++ b/crates/ical/src/lib.rs @@ -1,9 +1,7 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] mod timestamp; -mod timezone; pub use timestamp::*; -pub use timezone::*; mod icalendar; pub use icalendar::*; diff --git a/crates/ical/src/timestamp.rs b/crates/ical/src/timestamp.rs index 2dcf037..bf053f1 100644 --- a/crates/ical/src/timestamp.rs +++ b/crates/ical/src/timestamp.rs @@ -1,13 +1,6 @@ -use super::timezone::ICalTimezone; -use chrono::{DateTime, Datelike, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; -use chrono_tz::Tz; +use chrono::{DateTime, NaiveDateTime, Utc}; use derive_more::derive::Deref; -use ical::property::Property; use rustical_xml::{ValueDeserialize, ValueSerialize}; -use std::{borrow::Cow, collections::HashMap, ops::Add, sync::LazyLock}; - -static RE_VCARD_DATE_MM_DD: LazyLock = - LazyLock::new(|| regex::Regex::new(r"^--(?\d{2})(?\d{2})$").unwrap()); const LOCAL_DATE_TIME: &str = "%Y%m%dT%H%M%S"; const UTC_DATE_TIME: &str = "%Y%m%dT%H%M%SZ"; @@ -54,375 +47,3 @@ impl ValueSerialize for UtcDateTime { format!("{}", self.0.format(UTC_DATE_TIME)) } } - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CalDateTime { - // Form 1, example: 19980118T230000 -> Local - // 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, ICalTimezone), -} - -impl From for DateTime { - fn from(value: CalDateTime) -> Self { - value - .as_datetime() - .into_owned() - .with_timezone(&value.timezone().into()) - } -} - -impl From> for CalDateTime { - fn from(value: DateTime) -> Self { - Self::DateTime(value.with_timezone(&value.timezone().into())) - } -} - -impl PartialOrd for CalDateTime { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for CalDateTime { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match (&self, &other) { - (Self::DateTime(a), Self::DateTime(b)) => a.cmp(b), - (Self::DateTime(a), Self::Date(..)) => a.cmp(&other.as_datetime()), - (Self::Date(..), Self::DateTime(b)) => self.as_datetime().as_ref().cmp(b), - (Self::Date(..), Self::Date(..)) => self.as_datetime().cmp(&other.as_datetime()), - } - } -} - -impl From> for CalDateTime { - fn from(value: DateTime) -> Self { - Self::DateTime(value.with_timezone(&ICalTimezone::Local)) - } -} - -impl From> for CalDateTime { - fn from(value: DateTime) -> Self { - Self::DateTime(value.with_timezone(&ICalTimezone::Olson(chrono_tz::UTC))) - } -} - -impl Add for CalDateTime { - type Output = Self; - - fn add(self, duration: Duration) -> Self::Output { - match self { - Self::DateTime(datetime) => Self::DateTime(datetime + duration), - Self::Date(date, tz) => Self::DateTime( - date.and_time(NaiveTime::default()) - .and_local_timezone(tz) - .earliest() - .expect("Local timezone has constant offset") - + duration, - ), - } - } -} - -impl CalDateTime { - pub fn parse_prop( - prop: &Property, - timezones: &HashMap>, - ) -> Result { - let prop_value = prop - .value - .as_ref() - .ok_or_else(|| CalDateTimeError::InvalidDatetimeFormat("empty property".into()))?; - - let timezone = if let Some(tzid) = prop.get_param("TZID") { - if let Some(timezone) = timezones.get(tzid) { - timezone.to_owned() - } else { - // TZID refers to timezone that does not exist - return Err(CalDateTimeError::InvalidTZID(tzid.to_string())); - } - } else { - // No explicit timezone specified. - // This is valid and will be localtime or UTC depending on the value - // We will stick to this default as documented in https://github.com/lennart-k/rustical/issues/102 - None - }; - - Self::parse(prop_value, timezone) - } - - #[must_use] - pub fn format(&self) -> String { - match self { - Self::DateTime(datetime) => match datetime.timezone() { - 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(), - } - } - - #[must_use] - pub fn format_date(&self) -> String { - match self { - Self::DateTime(datetime) => datetime.format(LOCAL_DATE).to_string(), - Self::Date(date, _) => date.format(LOCAL_DATE).to_string(), - } - } - - #[must_use] - pub fn date(&self) -> NaiveDate { - match self { - Self::DateTime(datetime) => datetime.date_naive(), - Self::Date(date, _) => date.to_owned(), - } - } - - #[must_use] - pub const fn is_date(&self) -> bool { - matches!(&self, Self::Date(_, _)) - } - - #[must_use] - pub fn as_datetime(&self) -> Cow<'_, DateTime> { - match self { - Self::DateTime(datetime) => Cow::Borrowed(datetime), - Self::Date(date, tz) => Cow::Owned( - date.and_time(NaiveTime::default()) - .and_local_timezone(tz.to_owned()) - .earliest() - .expect("Midnight always exists"), - ), - } - } - - #[must_use] - pub fn with_timezone(&self, tz: &ICalTimezone) -> Self { - match self { - Self::DateTime(datetime) => Self::DateTime(datetime.with_timezone(tz)), - Self::Date(date, _) => Self::Date(date.to_owned(), tz.to_owned()), - } - } - - pub fn parse(value: &str, timezone: Option) -> Result { - if let Ok(datetime) = NaiveDateTime::parse_from_str(value, LOCAL_DATE_TIME) { - if let Some(timezone) = timezone { - return Ok(Self::DateTime( - datetime - .and_local_timezone(timezone.into()) - .earliest() - .ok_or(CalDateTimeError::LocalTimeGap)?, - )); - } - return Ok(Self::DateTime( - datetime - .and_local_timezone(ICalTimezone::Local) - .earliest() - .ok_or(CalDateTimeError::LocalTimeGap)?, - )); - } - - if let Ok(datetime) = NaiveDateTime::parse_from_str(value, UTC_DATE_TIME) { - return Ok(datetime.and_utc().into()); - } - let timezone = timezone.map_or(ICalTimezone::Local, ICalTimezone::Olson); - if let Ok(date) = NaiveDate::parse_from_str(value, LOCAL_DATE) { - return Ok(Self::Date(date, timezone)); - } - - if let Ok(date) = NaiveDate::parse_from_str(value, "%Y-%m-%d") { - return Ok(Self::Date(date, timezone)); - } - if let Ok(date) = NaiveDate::parse_from_str(value, "%Y%m%d") { - return Ok(Self::Date(date, timezone)); - } - - Err(CalDateTimeError::InvalidDatetimeFormat(value.to_string())) - } - - // Also returns whether the date contains a year - pub fn parse_vcard(value: &str) -> Result<(Self, bool), CalDateTimeError> { - if let Ok(datetime) = Self::parse(value, None) { - return Ok((datetime, true)); - } - - if let Some(captures) = RE_VCARD_DATE_MM_DD.captures(value) { - // Because 1972 is a leap year - let year = 1972; - // Cannot fail because of the regex - let month = captures.name("m").unwrap().as_str().parse().ok().unwrap(); - let day = captures.name("d").unwrap().as_str().parse().ok().unwrap(); - - return Ok(( - Self::Date( - NaiveDate::from_ymd_opt(year, month, day) - .ok_or_else(|| CalDateTimeError::ParseError(value.to_string()))?, - ICalTimezone::Local, - ), - false, - )); - } - Err(CalDateTimeError::InvalidDatetimeFormat(value.to_string())) - } - - #[must_use] - pub fn utc(&self) -> DateTime { - self.as_datetime().to_utc() - } - - #[must_use] - pub fn timezone(&self) -> ICalTimezone { - match &self { - Self::DateTime(datetime) => datetime.timezone(), - Self::Date(_, tz) => tz.to_owned(), - } - } -} - -impl From for DateTime { - fn from(value: CalDateTime) -> Self { - value.utc() - } -} - -impl Datelike for CalDateTime { - fn year(&self) -> i32 { - match &self { - Self::DateTime(datetime) => datetime.year(), - Self::Date(date, _) => date.year(), - } - } - fn month(&self) -> u32 { - match &self { - Self::DateTime(datetime) => datetime.month(), - Self::Date(date, _) => date.month(), - } - } - - fn month0(&self) -> u32 { - match &self { - Self::DateTime(datetime) => datetime.month0(), - Self::Date(date, _) => date.month0(), - } - } - fn day(&self) -> u32 { - match &self { - Self::DateTime(datetime) => datetime.day(), - Self::Date(date, _) => date.day(), - } - } - fn day0(&self) -> u32 { - match &self { - Self::DateTime(datetime) => datetime.day0(), - Self::Date(date, _) => date.day0(), - } - } - fn ordinal(&self) -> u32 { - match &self { - Self::DateTime(datetime) => datetime.ordinal(), - Self::Date(date, _) => date.ordinal(), - } - } - fn ordinal0(&self) -> u32 { - match &self { - Self::DateTime(datetime) => datetime.ordinal0(), - Self::Date(date, _) => date.ordinal0(), - } - } - fn weekday(&self) -> chrono::Weekday { - match &self { - Self::DateTime(datetime) => datetime.weekday(), - Self::Date(date, _) => date.weekday(), - } - } - fn iso_week(&self) -> chrono::IsoWeek { - match &self { - Self::DateTime(datetime) => datetime.iso_week(), - Self::Date(date, _) => date.iso_week(), - } - } - fn with_year(&self, year: i32) -> Option { - match &self { - Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_year(year)?)), - Self::Date(date, tz) => Some(Self::Date(date.with_year(year)?, tz.to_owned())), - } - } - fn with_month(&self, month: u32) -> Option { - match &self { - Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_month(month)?)), - Self::Date(date, tz) => Some(Self::Date(date.with_month(month)?, tz.to_owned())), - } - } - fn with_month0(&self, month0: u32) -> Option { - match &self { - Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_month0(month0)?)), - Self::Date(date, tz) => Some(Self::Date(date.with_month0(month0)?, tz.to_owned())), - } - } - fn with_day(&self, day: u32) -> Option { - match &self { - Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_day(day)?)), - Self::Date(date, tz) => Some(Self::Date(date.with_day(day)?, tz.to_owned())), - } - } - fn with_day0(&self, day0: u32) -> Option { - match &self { - Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_day0(day0)?)), - Self::Date(date, tz) => Some(Self::Date(date.with_day0(day0)?, tz.to_owned())), - } - } - fn with_ordinal(&self, ordinal: u32) -> Option { - match &self { - Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_ordinal(ordinal)?)), - Self::Date(date, tz) => Some(Self::Date(date.with_ordinal(ordinal)?, tz.to_owned())), - } - } - fn with_ordinal0(&self, ordinal0: u32) -> Option { - match &self { - Self::DateTime(datetime) => Some(Self::DateTime(datetime.with_ordinal0(ordinal0)?)), - Self::Date(date, tz) => Some(Self::Date(date.with_ordinal0(ordinal0)?, tz.to_owned())), - } - } -} - -#[cfg(test)] -mod tests { - use crate::CalDateTime; - use chrono::NaiveDate; - - #[test] - fn test_vcard_date() { - assert_eq!( - CalDateTime::parse_vcard("19850412").unwrap(), - ( - CalDateTime::Date( - NaiveDate::from_ymd_opt(1985, 4, 12).unwrap(), - crate::ICalTimezone::Local - ), - true - ) - ); - assert_eq!( - CalDateTime::parse_vcard("1985-04-12").unwrap(), - ( - CalDateTime::Date( - NaiveDate::from_ymd_opt(1985, 4, 12).unwrap(), - crate::ICalTimezone::Local - ), - true - ) - ); - assert_eq!( - CalDateTime::parse_vcard("--0412").unwrap(), - ( - CalDateTime::Date( - NaiveDate::from_ymd_opt(1972, 4, 12).unwrap(), - crate::ICalTimezone::Local - ), - false - ) - ); - } -} diff --git a/crates/ical/src/timezone.rs b/crates/ical/src/timezone.rs deleted file mode 100644 index aea5c9e..0000000 --- a/crates/ical/src/timezone.rs +++ /dev/null @@ -1,92 +0,0 @@ -use chrono::{Local, NaiveDate, NaiveDateTime, TimeZone}; -use chrono_tz::Tz; -use derive_more::{Display, From}; - -#[derive(Debug, Clone, From, PartialEq, Eq)] -pub enum ICalTimezone { - Local, - Olson(Tz), -} - -impl From for rrule::Tz { - fn from(value: ICalTimezone) -> Self { - match value { - ICalTimezone::Local => Self::LOCAL, - ICalTimezone::Olson(tz) => Self::Tz(tz), - } - } -} - -impl From for ICalTimezone { - fn from(value: rrule::Tz) -> Self { - match value { - rrule::Tz::Local(_) => Self::Local, - rrule::Tz::Tz(tz) => Self::Olson(tz), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Display)] -pub enum CalTimezoneOffset { - Local(chrono::FixedOffset), - Olson(chrono_tz::TzOffset), -} - -impl chrono::Offset for CalTimezoneOffset { - fn fix(&self) -> chrono::FixedOffset { - match self { - Self::Local(local) => local.fix(), - Self::Olson(olson) => olson.fix(), - } - } -} - -impl TimeZone for ICalTimezone { - type Offset = CalTimezoneOffset; - - fn from_offset(offset: &Self::Offset) -> Self { - match offset { - CalTimezoneOffset::Local(_) => Self::Local, - CalTimezoneOffset::Olson(offset) => Self::Olson(Tz::from_offset(offset)), - } - } - - fn offset_from_local_date(&self, local: &NaiveDate) -> chrono::MappedLocalTime { - match self { - Self::Local => Local - .offset_from_local_date(local) - .map(CalTimezoneOffset::Local), - Self::Olson(tz) => tz - .offset_from_local_date(local) - .map(CalTimezoneOffset::Olson), - } - } - - fn offset_from_local_datetime( - &self, - local: &NaiveDateTime, - ) -> chrono::MappedLocalTime { - match self { - Self::Local => Local - .offset_from_local_datetime(local) - .map(CalTimezoneOffset::Local), - 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::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::Olson(tz) => CalTimezoneOffset::Olson(tz.offset_from_utc_date(utc)), - } - } -} diff --git a/crates/store_sqlite/Cargo.toml b/crates/store_sqlite/Cargo.toml index ae7a6d0..8c3b7fc 100644 --- a/crates/store_sqlite/Cargo.toml +++ b/crates/store_sqlite/Cargo.toml @@ -20,6 +20,7 @@ rstest.workspace = true criterion.workspace = true [dependencies] +ical.workspace = true tokio.workspace = true rustical_store.workspace = true async-trait.workspace = true diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index 5f1b130..d6bae17 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -3,8 +3,9 @@ use crate::BEGIN_IMMEDIATE; use async_trait::async_trait; use chrono::TimeDelta; use derive_more::derive::Constructor; +use ical::types::CalDateTime; use regex::Regex; -use rustical_ical::{CalDateTime, CalendarObject, CalendarObjectType}; +use rustical_ical::{CalendarObject, CalendarObjectType}; use rustical_store::calendar_store::CalendarQuery; use rustical_store::synctoken::format_synctoken; use rustical_store::{Calendar, CalendarMetadata, CalendarStore, CollectionMetadata, Error}; @@ -537,16 +538,12 @@ impl SqliteCalendarStore { let first_occurence = object .get_first_occurence() - .ok() - .flatten() .as_ref() - .map(CalDateTime::date); + .map(CalDateTime::date_floor); let last_occurence = object .get_last_occurence() - .ok() - .flatten() .as_ref() - .map(CalDateTime::date); + .map(CalDateTime::date_ceil); let etag = object.get_etag(); let object_type = object.get_object_type() as u8; From bb880aa4034337190fd767e229bbf55dde390496 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:44:55 +0100 Subject: [PATCH 02/26] incorporate get_first_occurenec --- crates/ical/src/icalendar/object.rs | 10 ---------- crates/store_sqlite/src/calendar_store.rs | 13 +++++++++---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/crates/ical/src/icalendar/object.rs b/crates/ical/src/icalendar/object.rs index f959d1a..3ad7d0e 100644 --- a/crates/ical/src/icalendar/object.rs +++ b/crates/ical/src/icalendar/object.rs @@ -126,16 +126,6 @@ impl CalendarObject { (&self.inner).into() } - pub fn get_first_occurence(&self) -> Option { - // TODO: Implement - None - } - - pub fn get_last_occurence(&self) -> Option { - // TODO: Implement - None - } - pub fn expand_recurrence( &self, start: Option>, diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index d6bae17..cb2c582 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -537,13 +537,18 @@ impl SqliteCalendarStore { let (object_id, uid, ics) = (object.get_id(), object.get_uid(), object.get_ics()); let first_occurence = object + .get_inner() + .get_inner() .get_first_occurence() .as_ref() .map(CalDateTime::date_floor); - let last_occurence = object - .get_last_occurence() - .as_ref() - .map(CalDateTime::date_ceil); + let last_occurence: Option = todo!(); + // let last_occurence = object + // .get_inner() + // .get_inner() + // .get_last_occurence() + // .as_ref() + // .map(CalDateTime::date_ceil); let etag = object.get_etag(); let object_type = object.get_object_type() as u8; From 896e934c0a7724d3636b6ca2108fc92802bec12a Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:46:28 +0100 Subject: [PATCH 03/26] Decrease folder nesting --- crates/ical/src/calendar_object.rs | 136 ++++++++++++++++++ .../src/{icalendar/object.rs => icalendar.rs} | 0 crates/ical/src/icalendar/mod.rs | 3 - crates/ical/src/lib.rs | 4 +- 4 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 crates/ical/src/calendar_object.rs rename crates/ical/src/{icalendar/object.rs => icalendar.rs} (100%) delete mode 100644 crates/ical/src/icalendar/mod.rs diff --git a/crates/ical/src/calendar_object.rs b/crates/ical/src/calendar_object.rs new file mode 100644 index 0000000..7035e49 --- /dev/null +++ b/crates/ical/src/calendar_object.rs @@ -0,0 +1,136 @@ +use crate::Error; +use chrono::DateTime; +use chrono::Utc; +use derive_more::Display; +use ical::component::CalendarInnerData; +use ical::component::IcalCalendarObject; +use ical::parser::ComponentParser; +use serde::Deserialize; +use serde::Serialize; +use sha2::{Digest, Sha256}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Display)] +// specified in https://datatracker.ietf.org/doc/html/rfc5545#section-3.6 +pub enum CalendarObjectType { + #[serde(rename = "VEVENT")] + Event = 0, + #[serde(rename = "VTODO")] + Todo = 1, + #[serde(rename = "VJOURNAL")] + Journal = 2, +} + +impl From<&IcalCalendarObject> for CalendarObjectType { + fn from(value: &IcalCalendarObject) -> Self { + match value.get_inner() { + CalendarInnerData::Event(_, _) => Self::Event, + CalendarInnerData::Todo(_, _) => Self::Todo, + CalendarInnerData::Journal(_, _) => Self::Journal, + } + } +} + +impl CalendarObjectType { + #[must_use] + pub const fn as_str(&self) -> &'static str { + match self { + Self::Event => "VEVENT", + Self::Todo => "VTODO", + Self::Journal => "VJOURNAL", + } + } +} + +impl rustical_xml::ValueSerialize for CalendarObjectType { + fn serialize(&self) -> String { + self.as_str().to_owned() + } +} + +impl rustical_xml::ValueDeserialize for CalendarObjectType { + fn deserialize(val: &str) -> std::result::Result { + match ::deserialize(val)?.as_str() { + "VEVENT" => Ok(Self::Event), + "VTODO" => Ok(Self::Todo), + "VJOURNAL" => Ok(Self::Journal), + _ => Err(rustical_xml::XmlError::InvalidValue( + rustical_xml::ParseValueError::Other(format!( + "Invalid value '{val}', must be VEVENT, VTODO, or VJOURNAL" + )), + )), + } + } +} + +#[derive(Debug, Clone)] +pub struct CalendarObject { + id: String, + inner: IcalCalendarObject, + ics: String, +} + +impl CalendarObject { + pub fn from_ics(ics: String, id: Option) -> Result { + let mut parser: ComponentParser<_, IcalCalendarObject> = + ComponentParser::new(ics.as_bytes()); + let inner = parser.next().ok_or(Error::MissingCalendar)??; + if parser.next().is_some() { + return Err(Error::InvalidData( + "multiple calendars, only one allowed".to_owned(), + )); + } + + Ok(Self { + id: id.unwrap_or_else(|| inner.get_uid().to_owned()), + inner, + ics, + }) + } + + #[must_use] + pub const fn get_inner(&self) -> &IcalCalendarObject { + &self.inner + } + + #[must_use] + pub fn get_uid(&self) -> &str { + self.inner.get_uid() + } + + #[must_use] + pub fn get_id(&self) -> &str { + &self.id + } + + #[must_use] + pub fn get_etag(&self) -> String { + let mut hasher = Sha256::new(); + hasher.update(self.get_uid()); + hasher.update(self.get_ics()); + format!("\"{:x}\"", hasher.finalize()) + } + + #[must_use] + pub fn get_ics(&self) -> &str { + &self.ics + } + + #[must_use] + pub fn get_component_name(&self) -> &str { + self.get_object_type().as_str() + } + + #[must_use] + pub fn get_object_type(&self) -> CalendarObjectType { + (&self.inner).into() + } + + pub fn expand_recurrence( + &self, + start: Option>, + end: Option>, + ) -> Result { + // Ok(self.inner.expand_recurrence(start, end)?) + todo!() + } +} diff --git a/crates/ical/src/icalendar/object.rs b/crates/ical/src/icalendar.rs similarity index 100% rename from crates/ical/src/icalendar/object.rs rename to crates/ical/src/icalendar.rs diff --git a/crates/ical/src/icalendar/mod.rs b/crates/ical/src/icalendar/mod.rs deleted file mode 100644 index 75ce6bc..0000000 --- a/crates/ical/src/icalendar/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod object; - -pub use object::*; diff --git a/crates/ical/src/lib.rs b/crates/ical/src/lib.rs index 42f646f..7e1aa43 100644 --- a/crates/ical/src/lib.rs +++ b/crates/ical/src/lib.rs @@ -3,8 +3,8 @@ mod timestamp; pub use timestamp::*; -mod icalendar; -pub use icalendar::*; +mod calendar_object; +pub use calendar_object::*; mod error; pub use error::Error; From a9f3833a327393f3a6f4e6c00df7c7c1477b7098 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:58:02 +0100 Subject: [PATCH 04/26] small fixes --- Cargo.lock | 5566 ----------------- .../report/calendar_query/comp_filter.rs | 23 +- .../methods/report/calendar_query/tests.rs | 2 +- crates/caldav/src/calendar_object/resource.rs | 9 +- crates/ical/src/address_object.rs | 15 - crates/ical/src/calendar_object.rs | 16 - crates/ical/src/timestamp.rs | 2 - crates/ical/tests/test_cal_object.rs | 2 +- 8 files changed, 23 insertions(+), 5612 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 90cd229..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,5566 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloca" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" -dependencies = [ - "cc", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" -dependencies = [ - "backtrace", -] - -[[package]] -name = "argon2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" -dependencies = [ - "base64ct", - "blake2", - "cpufeatures", - "password-hash", -] - -[[package]] -name = "askama" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427" -dependencies = [ - "askama_macros", - "itoa", - "percent-encoding", - "serde", - "serde_json", -] - -[[package]] -name = "askama_derive" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994" -dependencies = [ - "askama_parser", - "basic-toml", - "memchr", - "proc-macro2", - "quote", - "rustc-hash", - "serde", - "serde_derive", - "syn 2.0.114", -] - -[[package]] -name = "askama_macros" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46" -dependencies = [ - "askama_derive", -] - -[[package]] -name = "askama_parser" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e" -dependencies = [ - "rustc-hash", - "serde", - "serde_derive", - "unicode-ident", - "winnow", -] - -[[package]] -name = "askama_web" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0d6576f8e59513752a3e2673ca602fb403be7d0d0aacba5cd8b219838ab58fe" -dependencies = [ - "askama", - "askama_web_derive", - "axum-core", - "bytes", - "http", -] - -[[package]] -name = "askama_web_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9767c17d33a63daf6da5872ffaf2ab0c289cd73ce7ed4f41d5ddf9149c004873" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "async-attributes" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener 5.4.1", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-std" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" -dependencies = [ - "async-attributes", - "async-channel 1.9.0", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atomic" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "axum" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" -dependencies = [ - "axum-core", - "bytes", - "form_urlencoded", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit 0.8.4", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde_core", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-extra" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef252edff26ddba56bbcdf2ee3307b8129acb86f5749b68990c168a6fcc9c76" -dependencies = [ - "axum", - "axum-core", - "bytes", - "futures-core", - "futures-util", - "headers", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "basic-toml" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" -dependencies = [ - "serde_core", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel 2.5.0", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - -[[package]] -name = "bstr" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.2.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "chrono-humanize" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" -dependencies = [ - "chrono", -] - -[[package]] -name = "chrono-tz" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" -dependencies = [ - "chrono", - "phf 0.12.1", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "clap" -version = "4.5.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.5.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.5.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "clap_lex" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" - -[[package]] -name = "colorchoice" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "console" -version = "0.15.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "windows-sys 0.59.0", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "cookie" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "criterion" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d883447757bb0ee46f233e9dc22eb84d93a9508c9b868687b274fc431d886bf" -dependencies = [ - "alloca", - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "itertools 0.13.0", - "num-traits", - "oorandom", - "page_size", - "plotters", - "rayon", - "regex", - "serde", - "serde_json", - "tinytemplate", - "tokio", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed943f81ea2faa8dcecbbfa50164acf95d555afec96a27871663b300e387b2e4" -dependencies = [ - "cast", - "itertools 0.13.0", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "darling" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" -dependencies = [ - "darling_core 0.21.3", - "darling_macro 0.21.3", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core 0.23.0", - "darling_macro 0.23.0", -] - -[[package]] -name = "darling_core" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.114", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.114", -] - -[[package]] -name = "darling_macro" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" -dependencies = [ - "darling_core 0.21.3", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core 0.23.0", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" -dependencies = [ - "powerfmt", - "serde_core", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version", - "syn 2.0.114", - "unicode-xid", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ece" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ea1d2f2cc974957a4e2575d8e5bb494549bab66338d6320c2789abcfff5746" -dependencies = [ - "base64 0.21.7", - "byteorder", - "hex", - "hkdf", - "lazy_static", - "once_cell", - "openssl", - "sha2", - "thiserror 1.0.69", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2", - "subtle", - "zeroize", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -dependencies = [ - "serde", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener 5.4.1", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "figment" -version = "0.10.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" -dependencies = [ - "atomic", - "pear", - "serde", - "toml 0.8.23", - "uncased", - "version_check", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap 2.13.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "hashlink" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64 0.22.1", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-timeout" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" -dependencies = [ - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ical" -version = "0.12.0-dev" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#5e61c25646c3785448d349e7d18b2833fc483c53" -dependencies = [ - "chrono", - "chrono-tz", - "derive_more", - "itertools 0.14.0", - "lazy_static", - "phf 0.13.1", - "regex", - "rrule", - "thiserror 2.0.17", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", -] - -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - -[[package]] -name = "insta" -version = "1.46.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" -dependencies = [ - "console", - "once_cell", - "regex", - "similar", - "tempfile", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[package]] -name = "js-sys" -version = "0.3.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.180" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "libredox" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" -dependencies = [ - "bitflags", - "libc", - "redox_syscall 0.7.0", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", - "serde", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -dependencies = [ - "value-bag", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matchit" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" - -[[package]] -name = "matchit" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3eede3bdf92f3b4f9dc04072a9ce5ab557d5ec9038773bf9ffcd5588b3cc05b" - -[[package]] -name = "matchit-serde" -version = "0.1.0" -source = "git+https://github.com/lennart-k/matchit-serde?rev=e18e65d7#e18e65d75cb20ab5f6a193c84a87ee2db8e6ae0b" -dependencies = [ - "derive_more", - "matchit 0.9.1", - "percent-encoding", - "serde", - "thiserror 2.0.17", -] - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" -dependencies = [ - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "oauth2" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" -dependencies = [ - "base64 0.22.1", - "chrono", - "getrandom 0.2.17", - "http", - "rand 0.8.5", - "reqwest", - "serde", - "serde_json", - "serde_path_to_error", - "sha2", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "openidconnect" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c6709ba2ea764bbed26bce1adf3c10517113ddea6f2d4196e4851757ef2b2" -dependencies = [ - "base64 0.21.7", - "chrono", - "dyn-clone", - "ed25519-dalek", - "hmac", - "http", - "itertools 0.10.5", - "log", - "oauth2", - "p256", - "p384", - "rand 0.8.5", - "rsa", - "serde", - "serde-value", - "serde_json", - "serde_path_to_error", - "serde_plain", - "serde_with", - "sha2", - "subtle", - "thiserror 1.0.69", - "url", -] - -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "openssl-src" -version = "300.5.4+3.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "opentelemetry" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 2.0.17", - "tracing", -] - -[[package]] -name = "opentelemetry-http" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" -dependencies = [ - "async-trait", - "bytes", - "http", - "opentelemetry", - "reqwest", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" -dependencies = [ - "http", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", - "prost", - "reqwest", - "thiserror 2.0.17", - "tokio", - "tonic", - "tracing", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" -dependencies = [ - "opentelemetry", - "opentelemetry_sdk", - "prost", - "tonic", - "tonic-prost", -] - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" - -[[package]] -name = "opentelemetry_sdk" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" -dependencies = [ - "futures-channel", - "futures-executor", - "futures-util", - "opentelemetry", - "percent-encoding", - "rand 0.9.2", - "thiserror 2.0.17", - "tokio", - "tokio-stream", -] - -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "page_size" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", -] - -[[package]] -name = "password-auth" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a2a4764cc1f8d961d802af27193c6f4f0124bd0e76e8393cf818e18880f0524" -dependencies = [ - "argon2", - "getrandom 0.2.17", - "password-hash", - "pbkdf2", - "rand_core 0.6.4", -] - -[[package]] -name = "password-hash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - -[[package]] -name = "pear" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "phf" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" -dependencies = [ - "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]] -name = "phf_codegen" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61" -dependencies = [ - "phf_generator 0.12.1", - "phf_shared 0.12.1", -] - -[[package]] -name = "phf_generator" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" -dependencies = [ - "fastrand", - "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.114", -] - -[[package]] -name = "phf_shared" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "piper" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "polling" -version = "3.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" -dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proc-macro2-diagnostics" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", - "version_check", - "yansi", -] - -[[package]] -name = "prost" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "quick-xml" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" -dependencies = [ - "memchr", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.17", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.17", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_syscall" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "relative-path" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64 0.22.1", - "bytes", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rpassword" -version = "7.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" -dependencies = [ - "libc", - "rtoolbox", - "windows-sys 0.59.0", -] - -[[package]] -name = "rrule" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "720acfb4980b9d8a6a430f6d7a11933e701ebbeba5eee39cc9d8c5f932aaff74" -dependencies = [ - "chrono", - "chrono-tz", - "log", - "regex", - "thiserror 2.0.17", -] - -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rstest" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" -dependencies = [ - "futures-timer", - "futures-util", - "rstest_macros", -] - -[[package]] -name = "rstest_macros" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" -dependencies = [ - "cfg-if", - "glob", - "proc-macro-crate", - "proc-macro2", - "quote", - "regex", - "relative-path", - "rustc_version", - "syn 2.0.114", - "unicode-ident", -] - -[[package]] -name = "rstest_reuse" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a8fb4672e840a587a66fc577a5491375df51ddb88f2a2c2a792598c326fe14" -dependencies = [ - "quote", - "rand 0.8.5", - "syn 2.0.114", -] - -[[package]] -name = "rtoolbox" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "rust-embed" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.114", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" -dependencies = [ - "sha2", - "walkdir", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustical" -version = "0.11.17" -dependencies = [ - "anyhow", - "argon2", - "async-trait", - "axum", - "axum-extra", - "clap", - "figment", - "headers", - "http", - "insta", - "opentelemetry", - "opentelemetry-otlp", - "opentelemetry-semantic-conventions", - "opentelemetry_sdk", - "password-hash", - "pbkdf2", - "quick-xml", - "reqwest", - "rpassword", - "rstest", - "rustical_caldav", - "rustical_carddav", - "rustical_dav", - "rustical_dav_push", - "rustical_frontend", - "rustical_oidc", - "rustical_store", - "rustical_store_sqlite", - "serde", - "sqlx", - "tokio", - "toml 0.9.11+spec-1.1.0", - "tower", - "tower-http", - "tower-sessions", - "tracing", - "tracing-opentelemetry", - "tracing-subscriber", - "uuid", -] - -[[package]] -name = "rustical_caldav" -version = "0.11.17" -dependencies = [ - "async-std", - "async-trait", - "axum", - "axum-extra", - "base64 0.22.1", - "chrono", - "chrono-tz", - "derive_more", - "futures-util", - "headers", - "http", - "ical", - "insta", - "percent-encoding", - "quick-xml", - "rstest", - "rustical_dav", - "rustical_dav_push", - "rustical_ical", - "rustical_store", - "rustical_store_sqlite", - "rustical_xml", - "serde", - "serde_json", - "sha2", - "similar-asserts", - "strum", - "strum_macros", - "thiserror 2.0.17", - "tokio", - "tower", - "tower-http", - "tracing", - "url", - "uuid", - "vtimezones-rs", -] - -[[package]] -name = "rustical_carddav" -version = "0.11.17" -dependencies = [ - "async-trait", - "axum", - "axum-extra", - "base64 0.22.1", - "chrono", - "derive_more", - "futures-util", - "http", - "ical", - "insta", - "percent-encoding", - "quick-xml", - "rstest", - "rustical_dav", - "rustical_dav_push", - "rustical_ical", - "rustical_store", - "rustical_xml", - "serde", - "strum", - "strum_macros", - "thiserror 2.0.17", - "tokio", - "tower", - "tower-http", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "rustical_dav" -version = "0.11.17" -dependencies = [ - "async-trait", - "axum", - "axum-extra", - "derive_more", - "futures-util", - "headers", - "http", - "ical", - "itertools 0.14.0", - "log", - "matchit 0.9.1", - "matchit-serde", - "quick-xml", - "rustical_xml", - "serde", - "strum", - "thiserror 2.0.17", - "tokio", - "tower", - "tracing", -] - -[[package]] -name = "rustical_dav_push" -version = "0.11.17" -dependencies = [ - "async-trait", - "axum", - "base64 0.22.1", - "derive_more", - "ece", - "futures-util", - "http", - "itertools 0.14.0", - "log", - "openssl", - "quick-xml", - "reqwest", - "rustical_dav", - "rustical_store", - "rustical_xml", - "serde", - "thiserror 2.0.17", - "tokio", - "tracing", -] - -[[package]] -name = "rustical_frontend" -version = "0.11.17" -dependencies = [ - "askama", - "askama_web", - "async-trait", - "axum", - "axum-extra", - "chrono", - "chrono-humanize", - "futures-core", - "headers", - "hex", - "http", - "itertools 0.14.0", - "mime_guess", - "percent-encoding", - "rand 0.9.2", - "rust-embed", - "rustical_ical", - "rustical_oidc", - "rustical_store", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tower", - "tower-http", - "tower-sessions", - "tracing", - "url", - "uuid", - "vtimezones-rs", -] - -[[package]] -name = "rustical_ical" -version = "0.11.17" -dependencies = [ - "axum", - "chrono", - "chrono-tz", - "derive_more", - "ical", - "regex", - "rrule", - "rstest", - "rustical_xml", - "serde", - "sha2", - "similar-asserts", - "thiserror 2.0.17", -] - -[[package]] -name = "rustical_oidc" -version = "0.11.17" -dependencies = [ - "async-trait", - "axum", - "axum-extra", - "headers", - "openidconnect", - "reqwest", - "serde", - "thiserror 2.0.17", - "tower-sessions", - "tracing", -] - -[[package]] -name = "rustical_store" -version = "0.11.17" -dependencies = [ - "anyhow", - "async-trait", - "axum", - "chrono", - "chrono-tz", - "clap", - "derive_more", - "futures-core", - "headers", - "http", - "ical", - "regex", - "rrule", - "rstest", - "rstest_reuse", - "rustical_dav", - "rustical_ical", - "rustical_store_sqlite", - "rustical_xml", - "serde", - "sha2", - "thiserror 2.0.17", - "tokio", - "tower", - "tower-sessions", - "tracing", - "vtimezones-rs", -] - -[[package]] -name = "rustical_store_sqlite" -version = "0.11.17" -dependencies = [ - "async-trait", - "chrono", - "criterion", - "derive_more", - "ical", - "password-auth", - "password-hash", - "pbkdf2", - "regex", - "rstest", - "rustical_ical", - "rustical_store", - "serde", - "sha2", - "sqlx", - "thiserror 2.0.17", - "tokio", - "tracing", - "uuid", -] - -[[package]] -name = "rustical_xml" -version = "0.11.17" -dependencies = [ - "quick-xml", - "thiserror 2.0.17", - "xml_derive", -] - -[[package]] -name = "rustix" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4910321ebe4151be888e35fe062169554e74aad01beafed60410131420ceffbc" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_plain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_spanned" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" -dependencies = [ - "base64 0.22.1", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.13.0", - "schemars 0.9.0", - "schemars 1.2.0", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" -dependencies = [ - "darling 0.21.3", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest", - "rand_core 0.6.4", -] - -[[package]] -name = "similar" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" -dependencies = [ - "bstr", - "unicode-segmentation", -] - -[[package]] -name = "similar-asserts" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" -dependencies = [ - "console", - "similar", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlx" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" -dependencies = [ - "base64 0.22.1", - "bytes", - "chrono", - "crc", - "crossbeam-queue", - "either", - "event-listener 5.4.1", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.15.5", - "hashlink", - "indexmap 2.13.0", - "log", - "memchr", - "once_cell", - "percent-encoding", - "serde", - "serde_json", - "sha2", - "smallvec", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "sqlx-macros" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 2.0.114", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" -dependencies = [ - "dotenvy", - "either", - "heck", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 2.0.114", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" -dependencies = [ - "atoi", - "base64 0.22.1", - "bitflags", - "byteorder", - "bytes", - "chrono", - "crc", - "digest", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array", - "hex", - "hkdf", - "hmac", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "percent-encoding", - "rand 0.8.5", - "rsa", - "serde", - "sha1", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror 2.0.17", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" -dependencies = [ - "atoi", - "base64 0.22.1", - "bitflags", - "byteorder", - "chrono", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "rand 0.8.5", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror 2.0.17", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" -dependencies = [ - "atoi", - "chrono", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "serde_urlencoded", - "sqlx-core", - "thiserror 2.0.17", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "unicode-properties", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "tempfile" -version = "3.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.3.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" - -[[package]] -name = "time-macros" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "tracing", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - -[[package]] -name = "toml" -version = "0.9.11+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" -dependencies = [ - "indexmap 2.13.0", - "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.13.0", - "serde", - "serde_spanned 0.6.9", - "toml_datetime 0.6.11", - "toml_write", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.23.10+spec-1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" -dependencies = [ - "indexmap 2.13.0", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.0.6+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" -dependencies = [ - "winnow", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "toml_writer" -version = "1.0.6+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" - -[[package]] -name = "tonic" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" -dependencies = [ - "async-trait", - "base64 0.22.1", - "bytes", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "sync_wrapper", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic-prost" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" -dependencies = [ - "bytes", - "prost", - "tonic", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 2.13.0", - "pin-project-lite", - "slab", - "sync_wrapper", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-cookies" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36" -dependencies = [ - "axum-core", - "cookie", - "futures-util", - "http", - "parking_lot", - "pin-project-lite", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "http-range-header", - "httpdate", - "iri-string", - "mime", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "tokio", - "tokio-util", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tower-sessions" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a05911f23e8fae446005fe9b7b97e66d95b6db589dc1c4d59f6a2d4d4927d3" -dependencies = [ - "async-trait", - "http", - "time", - "tokio", - "tower-cookies", - "tower-layer", - "tower-service", - "tower-sessions-core", - "tower-sessions-memory-store", - "tracing", -] - -[[package]] -name = "tower-sessions-core" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8cce604865576b7751b7a6bc3058f754569a60d689328bb74c52b1d87e355b" -dependencies = [ - "async-trait", - "axum-core", - "base64 0.22.1", - "futures", - "http", - "parking_lot", - "rand 0.8.5", - "serde", - "serde_json", - "thiserror 2.0.17", - "time", - "tokio", - "tracing", -] - -[[package]] -name = "tower-sessions-memory-store" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb05909f2e1420135a831dd5df9f5596d69196d0a64c3499ca474c4bd3d33242" -dependencies = [ - "async-trait", - "time", - "tokio", - "tower-sessions-core", -] - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-opentelemetry" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" -dependencies = [ - "js-sys", - "opentelemetry", - "smallvec", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", - "web-time", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", - "serde_derive", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "rand 0.9.2", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vtimezones-rs" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6728de8767c8dea44f41b88115a205ed23adc3302e1b4342be59d922934dae5" -dependencies = [ - "glob", - "phf 0.12.1", - "phf_codegen", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" -dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.114", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.85" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "whoami" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" -dependencies = [ - "libredox", - "wasite", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "xml_derive" -version = "0.11.17" -dependencies = [ - "darling 0.23.0", - "heck", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.114", -] - -[[package]] -name = "zmij" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs index 7e773c8..27f10cc 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs @@ -139,7 +139,10 @@ END:VCALENDAR"; prop_filter: vec![], comp_filter: vec![], }; - assert!(!object.matches(&comp_filter), "filter: wants no VCALENDAR"); + assert!( + !object.get_inner().matches(&comp_filter), + "filter: wants no VCALENDAR" + ); let comp_filter = CompFilterElement { is_not_defined: None, @@ -154,7 +157,10 @@ END:VCALENDAR"; comp_filter: vec![], }], }; - assert!(!object.matches(&comp_filter), "filter matches VTODO"); + assert!( + !object.get_inner().matches(&comp_filter), + "filter matches VTODO" + ); let comp_filter = CompFilterElement { is_not_defined: None, @@ -169,7 +175,10 @@ END:VCALENDAR"; comp_filter: vec![], }], }; - assert!(object.matches(&comp_filter), "filter matches VEVENT"); + assert!( + object.get_inner().matches(&comp_filter), + "filter matches VEVENT" + ); let comp_filter = CompFilterElement { is_not_defined: None, @@ -216,7 +225,7 @@ END:VCALENDAR"; }], }; assert!( - object.matches(&comp_filter), + object.get_inner().matches(&comp_filter), "Some prop filters on VCALENDAR and VEVENT" ); } @@ -245,7 +254,7 @@ END:VCALENDAR"; }], }; assert!( - object.matches(&comp_filter), + object.get_inner().matches(&comp_filter), "event should lie in time range" ); @@ -270,7 +279,7 @@ END:VCALENDAR"; }], }; assert!( - !object.matches(&comp_filter), + !object.get_inner().matches(&comp_filter), "event should not lie in time range" ); } @@ -304,7 +313,7 @@ END:VCALENDAR"; }], }; assert!( - object.matches(&comp_filter), + object.get_inner().matches(&comp_filter), "Timezone should be Europe/Berlin" ); } diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs b/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs index 9652c10..0163158 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs @@ -79,5 +79,5 @@ const FILTER_2: &str = r#" fn yeet(#[case] ics: &str, #[case] filter: &str, #[case] matches: bool) { let obj = CalendarObject::from_ics(ics.to_owned(), None).unwrap(); let filter = FilterElement::parse_str(filter).unwrap(); - assert_eq!(matches, filter.matches(&obj)); + assert_eq!(matches, filter.matches(obj.get_inner())); } diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index 475f1fa..995b537 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -54,10 +54,11 @@ impl Resource for CalendarObjectResource { } CalendarObjectPropName::CalendarData(CalendarData { expand, .. }) => { CalendarObjectProp::CalendarData(if let Some(expand) = expand.as_ref() { - self.object.expand_recurrence( - Some(expand.start.to_utc()), - Some(expand.end.to_utc()), - )? + todo!() + // self.object.get_inner().expand_recurrence( + // Some(expand.start.to_utc()), + // Some(expand.end.to_utc()), + // ) } else { self.object.get_ics().to_owned() }) diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index 9f63fb6..79c68ec 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -4,7 +4,6 @@ use ical::parser::{ Component, vcard::{self, component::VcardContact}, }; -use ical::types::CalDateTime; use sha2::{Digest, Sha256}; use std::{collections::HashMap, io::BufReader}; @@ -62,20 +61,6 @@ impl AddressObject { &self.vcf } - #[must_use] - pub fn get_anniversary(&self) -> Option<(CalDateTime, bool)> { - // let prop = self.vcard.get_property("ANNIVERSARY")?.value.as_deref()?; - // CalDateTime::parse_vcard(prop).ok() - todo!() - } - - #[must_use] - pub fn get_birthday(&self) -> Option<(CalDateTime, bool)> { - todo!() - // let prop = self.vcard.get_property("BDAY")?.value.as_deref()?; - // CalDateTime::parse_vcard(prop).ok() - } - #[must_use] pub fn get_full_name(&self) -> Option<&str> { let prop = self.vcard.get_property("FN")?; diff --git a/crates/ical/src/calendar_object.rs b/crates/ical/src/calendar_object.rs index 7035e49..59bea86 100644 --- a/crates/ical/src/calendar_object.rs +++ b/crates/ical/src/calendar_object.rs @@ -1,6 +1,4 @@ use crate::Error; -use chrono::DateTime; -use chrono::Utc; use derive_more::Display; use ical::component::CalendarInnerData; use ical::component::IcalCalendarObject; @@ -115,22 +113,8 @@ impl CalendarObject { &self.ics } - #[must_use] - pub fn get_component_name(&self) -> &str { - self.get_object_type().as_str() - } - #[must_use] pub fn get_object_type(&self) -> CalendarObjectType { (&self.inner).into() } - - pub fn expand_recurrence( - &self, - start: Option>, - end: Option>, - ) -> Result { - // Ok(self.inner.expand_recurrence(start, end)?) - todo!() - } } diff --git a/crates/ical/src/timestamp.rs b/crates/ical/src/timestamp.rs index bf053f1..1e31191 100644 --- a/crates/ical/src/timestamp.rs +++ b/crates/ical/src/timestamp.rs @@ -2,9 +2,7 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use derive_more::derive::Deref; use rustical_xml::{ValueDeserialize, ValueSerialize}; -const LOCAL_DATE_TIME: &str = "%Y%m%dT%H%M%S"; const UTC_DATE_TIME: &str = "%Y%m%dT%H%M%SZ"; -pub const LOCAL_DATE: &str = "%Y%m%d"; #[derive(Debug, thiserror::Error, PartialEq, Eq)] pub enum CalDateTimeError { diff --git a/crates/ical/tests/test_cal_object.rs b/crates/ical/tests/test_cal_object.rs index 9fc4ffb..50b5a9f 100644 --- a/crates/ical/tests/test_cal_object.rs +++ b/crates/ical/tests/test_cal_object.rs @@ -26,5 +26,5 @@ END:VCALENDAR #[test] fn parse_calendar_object() { let object = CalendarObject::from_ics(MULTI_VEVENT.to_string(), None).unwrap(); - object.expand_recurrence(None, None).unwrap(); + object.get_inner().expand_recurrence(None, None); } From 758793a11a908cc63ae29d2bf35997295ffcb65f Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:19:30 +0100 Subject: [PATCH 05/26] Make AddressObject object_id an extrinsic property --- crates/carddav/src/address_object/methods.rs | 4 +- crates/carddav/src/address_object/resource.rs | 3 +- crates/carddav/src/address_object/service.rs | 1 + crates/carddav/src/addressbook/methods/get.rs | 3 +- .../carddav/src/addressbook/methods/import.rs | 5 +- .../methods/report/addressbook_multiget.rs | 9 +-- .../methods/report/addressbook_query/mod.rs | 4 +- .../methods/report/addressbook_query/tests.rs | 2 +- .../src/addressbook/methods/report/mod.rs | 7 ++- .../methods/report/sync_collection.rs | 5 +- crates/carddav/src/addressbook/service.rs | 5 +- crates/ical/src/address_object.rs | 27 ++------- crates/store/src/addressbook_store.rs | 15 ++--- .../addressbook_store/birthday_calendar.rs | 47 ++++++++-------- .../store_sqlite/src/addressbook_store/mod.rs | 55 ++++++++++++------- 15 files changed, 98 insertions(+), 94 deletions(-) diff --git a/crates/carddav/src/address_object/methods.rs b/crates/carddav/src/address_object/methods.rs index a93c78d..ceabfd3 100644 --- a/crates/carddav/src/address_object/methods.rs +++ b/crates/carddav/src/address_object/methods.rs @@ -103,10 +103,10 @@ pub async fn put_object( true }; - let object = AddressObject::from_vcf(object_id, body)?; + let object = AddressObject::from_vcf(body)?; let etag = object.get_etag(); addr_store - .put_object(principal, addressbook_id, object, overwrite) + .put_object(&principal, &addressbook_id, &object_id, object, overwrite) .await?; let mut headers = HeaderMap::new(); diff --git a/crates/carddav/src/address_object/resource.rs b/crates/carddav/src/address_object/resource.rs index cddcee3..ed5f7f0 100644 --- a/crates/carddav/src/address_object/resource.rs +++ b/crates/carddav/src/address_object/resource.rs @@ -21,11 +21,12 @@ use rustical_store::auth::Principal; pub struct AddressObjectResource { pub object: AddressObject, pub principal: String, + pub object_id: String, } impl ResourceName for AddressObjectResource { fn get_name(&self) -> Cow<'_, str> { - Cow::from(format!("{}.vcf", self.object.get_id())) + Cow::from(format!("{}.vcf", self.object_id)) } } diff --git a/crates/carddav/src/address_object/service.rs b/crates/carddav/src/address_object/service.rs index 1c6efdc..0070a2d 100644 --- a/crates/carddav/src/address_object/service.rs +++ b/crates/carddav/src/address_object/service.rs @@ -57,6 +57,7 @@ impl ResourceService for AddressObjectResourceService .await?; Ok(AddressObjectResource { object, + object_id: object_id.to_owned(), principal: principal.to_owned(), }) } diff --git a/crates/carddav/src/addressbook/methods/get.rs b/crates/carddav/src/addressbook/methods/get.rs index 3159243..8bd4d6e 100644 --- a/crates/carddav/src/addressbook/methods/get.rs +++ b/crates/carddav/src/addressbook/methods/get.rs @@ -9,7 +9,6 @@ use http::{HeaderValue, Method, StatusCode, header}; use percent_encoding::{CONTROLS, utf8_percent_encode}; use rustical_dav::privileges::UserPrivilege; use rustical_dav::resource::Resource; -use rustical_ical::AddressObject; use rustical_store::auth::Principal; use rustical_store::{AddressbookStore, SubscriptionStore}; use std::str::FromStr; @@ -40,7 +39,7 @@ pub async fn route_get( let objects = addr_store.get_objects(&principal, &addressbook_id).await?; let vcf = objects .iter() - .map(AddressObject::get_vcf) + .map(|(_id, obj)| obj.get_vcf()) .collect::>() .join("\r\n"); diff --git a/crates/carddav/src/addressbook/methods/import.rs b/crates/carddav/src/addressbook/methods/import.rs index 3f4bc2a..82709e9 100644 --- a/crates/carddav/src/addressbook/methods/import.rs +++ b/crates/carddav/src/addressbook/methods/import.rs @@ -40,8 +40,9 @@ pub async fn route_import( }); card = card_mut.build(&HashMap::new()).unwrap(); } - - objects.push(card.try_into().unwrap()); + // TODO: Make nicer + let uid = card.get_uid().unwrap(); + objects.push((uid.to_owned(), card.into())); } if objects.is_empty() { diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs index 53fff13..03fb11b 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs @@ -29,7 +29,7 @@ pub async fn get_objects_addressbook_multiget( principal: &str, addressbook_id: &str, store: &AS, -) -> Result<(Vec, Vec), Error> { +) -> Result<(Vec<(String, AddressObject)>, Vec), Error> { let mut result = vec![]; let mut not_found = vec![]; @@ -43,7 +43,7 @@ pub async fn get_objects_addressbook_multiget( .get_object(principal, addressbook_id, object_id, false) .await { - Ok(object) => result.push(object), + Ok(object) => result.push((object_id.to_owned(), object)), Err(rustical_store::Error::NotFound) => not_found.push(href.to_string()), Err(err) => return Err(err.into()), } @@ -74,11 +74,12 @@ pub async fn handle_addressbook_multiget( .await?; let mut responses = Vec::new(); - for object in objects { - let path = format!("{}/{}.vcf", path, object.get_id()); + for (object_id, object) in objects { + let path = format!("{path}/{object_id}.vcf"); responses.push( AddressObjectResource { object, + object_id, principal: principal.to_owned(), } .propfind(&path, prop, None, puri, user)?, diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_query/mod.rs b/crates/carddav/src/addressbook/methods/report/addressbook_query/mod.rs index 1e463fe..6302584 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_query/mod.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_query/mod.rs @@ -15,8 +15,8 @@ pub async fn get_objects_addressbook_query( principal: &str, addressbook_id: &str, store: &AS, -) -> Result, Error> { +) -> Result, Error> { let mut objects = store.get_objects(principal, addressbook_id).await?; - objects.retain(|object| addr_query.filter.matches(object)); + objects.retain(|(_id, object)| addr_query.filter.matches(object)); Ok(objects) } diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_query/tests.rs b/crates/carddav/src/addressbook/methods/report/addressbook_query/tests.rs index 5c84224..f292562 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_query/tests.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_query/tests.rs @@ -64,7 +64,7 @@ const FILTER_2: &str = r#" #[case(VCF_2, FILTER_2, true)] fn test_filter(#[case] vcf: &str, #[case] filter: &str, #[case] matches: bool) { dbg!(vcf); - let obj = AddressObject::from_vcf(String::new(), vcf.to_owned()).unwrap(); + let obj = AddressObject::from_vcf(vcf.to_owned()).unwrap(); let filter = FilterElement::parse_str(filter).unwrap(); assert_eq!(matches, filter.matches(&obj)); } diff --git a/crates/carddav/src/addressbook/methods/report/mod.rs b/crates/carddav/src/addressbook/methods/report/mod.rs index dfba5e0..8191b1a 100644 --- a/crates/carddav/src/addressbook/methods/report/mod.rs +++ b/crates/carddav/src/addressbook/methods/report/mod.rs @@ -55,7 +55,7 @@ impl ReportRequest { } fn objects_response( - objects: Vec, + objects: Vec<(String, AddressObject)>, not_found: Vec, path: &str, principal: &str, @@ -64,11 +64,12 @@ fn objects_response( prop: &PropfindType, ) -> Result, Error> { let mut responses = Vec::new(); - for object in objects { - let path = format!("{}/{}.vcf", path, object.get_id()); + for (object_id, object) in objects { + let path = format!("{}/{}.vcf", path, &object_id); responses.push( AddressObjectResource { object, + object_id, principal: principal.to_owned(), } .propfind(&path, prop, None, puri, user)?, diff --git a/crates/carddav/src/addressbook/methods/report/sync_collection.rs b/crates/carddav/src/addressbook/methods/report/sync_collection.rs index f27837b..e1ac852 100644 --- a/crates/carddav/src/addressbook/methods/report/sync_collection.rs +++ b/crates/carddav/src/addressbook/methods/report/sync_collection.rs @@ -32,11 +32,12 @@ pub async fn handle_sync_collection( .await?; let mut responses = Vec::new(); - for object in new_objects { - let path = format!("{}/{}.vcf", path.trim_end_matches('/'), object.get_id()); + for (object_id, object) in new_objects { + let path = format!("{}/{}.vcf", path.trim_end_matches('/'), object_id); responses.push( AddressObjectResource { object, + object_id, principal: principal.to_owned(), } .propfind(&path, &sync_collection.prop, None, puri, user)?, diff --git a/crates/carddav/src/addressbook/service.rs b/crates/carddav/src/addressbook/service.rs index 4ae19d6..0e838f1 100644 --- a/crates/carddav/src/addressbook/service.rs +++ b/crates/carddav/src/addressbook/service.rs @@ -78,7 +78,8 @@ impl ResourceService .get_objects(principal, addressbook_id) .await? .into_iter() - .map(|object| AddressObjectResource { + .map(|(object_id, object)| AddressObjectResource { + object_id, object, principal: principal.to_owned(), }) @@ -91,7 +92,7 @@ impl ResourceService file: Self::Resource, ) -> Result<(), Self::Error> { self.addr_store - .update_addressbook(principal.to_owned(), addressbook_id.to_owned(), file.into()) + .update_addressbook(principal, addressbook_id, file.into()) .await?; Ok(()) } diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index 79c68ec..e631fec 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -9,30 +9,19 @@ use std::{collections::HashMap, io::BufReader}; #[derive(Debug, Clone)] pub struct AddressObject { - id: String, vcf: String, vcard: VcardContact, } -impl TryFrom for AddressObject { - type Error = Error; - - fn try_from(vcard: VcardContact) -> Result { - let uid = vcard - .get_uid() - .ok_or_else(|| Error::InvalidData("missing UID".to_owned()))? - .to_owned(); +impl From for AddressObject { + fn from(vcard: VcardContact) -> Self { let vcf = vcard.generate(); - Ok(Self { - vcf, - vcard, - id: uid, - }) + Self { vcf, vcard } } } impl AddressObject { - pub fn from_vcf(id: String, vcf: String) -> Result { + pub fn from_vcf(vcf: String) -> Result { let mut parser = vcard::VcardParser::new(BufReader::new(vcf.as_bytes())); let vcard = parser.next().ok_or(Error::MissingContact)??; if parser.next().is_some() { @@ -40,18 +29,12 @@ impl AddressObject { "multiple vcards, only one allowed".to_owned(), )); } - Ok(Self { id, vcf, vcard }) - } - - #[must_use] - pub fn get_id(&self) -> &str { - &self.id + Ok(Self { vcf, vcard }) } #[must_use] pub fn get_etag(&self) -> String { let mut hasher = Sha256::new(); - hasher.update(self.get_id()); hasher.update(self.get_vcf()); format!("\"{:x}\"", hasher.finalize()) } diff --git a/crates/store/src/addressbook_store.rs b/crates/store/src/addressbook_store.rs index 325071b..c5bf2d6 100644 --- a/crates/store/src/addressbook_store.rs +++ b/crates/store/src/addressbook_store.rs @@ -15,8 +15,8 @@ pub trait AddressbookStore: Send + Sync + 'static { async fn update_addressbook( &self, - principal: String, - id: String, + principal: &str, + id: &str, addressbook: Addressbook, ) -> Result<(), Error>; async fn insert_addressbook(&self, addressbook: Addressbook) -> Result<(), Error>; @@ -33,7 +33,7 @@ pub trait AddressbookStore: Send + Sync + 'static { principal: &str, addressbook_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error>; + ) -> Result<(Vec<(String, AddressObject)>, Vec, i64), Error>; async fn addressbook_metadata( &self, @@ -45,7 +45,7 @@ pub trait AddressbookStore: Send + Sync + 'static { &self, principal: &str, addressbook_id: &str, - ) -> Result, Error>; + ) -> Result, Error>; async fn get_object( &self, principal: &str, @@ -55,8 +55,9 @@ pub trait AddressbookStore: Send + Sync + 'static { ) -> Result; async fn put_object( &self, - principal: String, - addressbook_id: String, + principal: &str, + addressbook_id: &str, + object_id: &str, object: AddressObject, overwrite: bool, ) -> Result<(), Error>; @@ -77,7 +78,7 @@ pub trait AddressbookStore: Send + Sync + 'static { async fn import_addressbook( &self, addressbook: Addressbook, - objects: Vec, + objects: Vec<(String, AddressObject)>, merge_existing: bool, ) -> Result<(), Error>; } diff --git a/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs b/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs index 8a73824..a79d50b 100644 --- a/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs +++ b/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs @@ -8,7 +8,6 @@ use rustical_store::{ }; use sha2::{Digest, Sha256}; use sqlx::{Executor, Sqlite}; -use std::collections::HashMap; use tracing::instrument; pub const BIRTHDAYS_PREFIX: &str = "_birthdays_"; @@ -330,13 +329,14 @@ impl CalendarStore for SqliteAddressbookStore { .ok_or(Error::NotFound)?; let (objects, deleted_objects, new_synctoken) = AddressbookStore::sync_changes(self, principal, cal_id, synctoken).await?; - let objects: Result>, rustical_ical::Error> = objects - .iter() - .map(AddressObject::get_birthday_object) - .collect(); - let objects = objects?.into_iter().flatten().collect(); - - Ok((objects, deleted_objects, new_synctoken)) + todo!(); + // let objects: Result>, rustical_ical::Error> = objects + // .iter() + // .map(AddressObject::get_birthday_object) + // .collect(); + // let objects = objects?.into_iter().flatten().collect(); + // + // Ok((objects, deleted_objects, new_synctoken)) } #[instrument] @@ -357,21 +357,22 @@ impl CalendarStore for SqliteAddressbookStore { principal: &str, cal_id: &str, ) -> Result, Error> { - let cal_id = cal_id - .strip_prefix(BIRTHDAYS_PREFIX) - .ok_or(Error::NotFound)?; - let objects: Result>, rustical_ical::Error> = - AddressbookStore::get_objects(self, principal, cal_id) - .await? - .iter() - .map(AddressObject::get_significant_dates) - .collect(); - let objects = objects? - .into_iter() - .flat_map(HashMap::into_values) - .collect(); - - Ok(objects) + todo!() + // let cal_id = cal_id + // .strip_prefix(BIRTHDAYS_PREFIX) + // .ok_or(Error::NotFound)?; + // let objects: Result>, rustical_ical::Error> = + // AddressbookStore::get_objects(self, principal, cal_id) + // .await? + // .iter() + // .map(AddressObject::get_significant_dates) + // .collect(); + // let objects = objects? + // .into_iter() + // .flat_map(HashMap::into_values) + // .collect(); + // + // Ok(objects) } #[instrument] diff --git a/crates/store_sqlite/src/addressbook_store/mod.rs b/crates/store_sqlite/src/addressbook_store/mod.rs index b026c3d..196c241 100644 --- a/crates/store_sqlite/src/addressbook_store/mod.rs +++ b/crates/store_sqlite/src/addressbook_store/mod.rs @@ -19,11 +19,11 @@ struct AddressObjectRow { vcf: String, } -impl TryFrom for AddressObject { +impl TryFrom for (String, AddressObject) { type Error = rustical_store::Error; fn try_from(value: AddressObjectRow) -> Result { - Ok(Self::from_vcf(value.id, value.vcf)?) + Ok((value.id, AddressObject::from_vcf(value.vcf)?)) } } @@ -290,7 +290,7 @@ impl SqliteAddressbookStore { principal: &str, addressbook_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), rustical_store::Error> { + ) -> Result<(Vec<(String, AddressObject)>, Vec, i64), rustical_store::Error> { struct Row { object_id: String, synctoken: i64, @@ -318,7 +318,7 @@ impl SqliteAddressbookStore { for Row { object_id, .. } in changes { match Self::_get_object(&mut *conn, principal, addressbook_id, &object_id, false).await { - Ok(object) => objects.push(object), + Ok(object) => objects.push((object_id, object)), Err(rustical_store::Error::NotFound) => deleted_objects.push(object_id), Err(err) => return Err(err), } @@ -353,7 +353,7 @@ impl SqliteAddressbookStore { executor: E, principal: &str, addressbook_id: &str, - ) -> Result, rustical_store::Error> { + ) -> Result, rustical_store::Error> { sqlx::query_as!( AddressObjectRow, "SELECT id, vcf FROM addressobjects WHERE principal = ? AND addressbook_id = ? AND deleted_at IS NULL", @@ -374,7 +374,7 @@ impl SqliteAddressbookStore { object_id: &str, show_deleted: bool, ) -> Result { - sqlx::query_as!( + let (id, object) = sqlx::query_as!( AddressObjectRow, "SELECT id, vcf FROM addressobjects WHERE (principal, addressbook_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)", principal, @@ -385,17 +385,20 @@ impl SqliteAddressbookStore { .fetch_one(executor) .await .map_err(crate::Error::from)? - .try_into() + .try_into()?; + assert_eq!(id, object_id); + Ok(object) } async fn _put_object<'e, E: Executor<'e, Database = Sqlite>>( executor: E, principal: &str, addressbook_id: &str, + object_id: &str, object: &AddressObject, overwrite: bool, ) -> Result<(), rustical_store::Error> { - let (object_id, vcf) = (object.get_id(), object.get_vcf()); + let vcf = object.get_vcf(); (if overwrite { sqlx::query!( @@ -500,10 +503,12 @@ impl AddressbookStore for SqliteAddressbookStore { #[instrument] async fn update_addressbook( &self, - principal: String, - id: String, + principal: &str, + id: &str, addressbook: Addressbook, ) -> Result<(), rustical_store::Error> { + assert_eq!(principal, &addressbook.principal); + assert_eq!(id, &addressbook.id); Self::_update_addressbook(&self.db, &principal, &id, &addressbook).await } @@ -569,7 +574,7 @@ impl AddressbookStore for SqliteAddressbookStore { principal: &str, addressbook_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), rustical_store::Error> { + ) -> Result<(Vec<(String, AddressObject)>, Vec, i64), rustical_store::Error> { Self::_sync_changes(&self.db, principal, addressbook_id, synctoken).await } @@ -601,7 +606,7 @@ impl AddressbookStore for SqliteAddressbookStore { &self, principal: &str, addressbook_id: &str, - ) -> Result, rustical_store::Error> { + ) -> Result, rustical_store::Error> { Self::_get_objects(&self.db, principal, addressbook_id).await } @@ -619,8 +624,9 @@ impl AddressbookStore for SqliteAddressbookStore { #[instrument] async fn put_object( &self, - principal: String, - addressbook_id: String, + principal: &str, + addressbook_id: &str, + object_id: &str, object: AddressObject, overwrite: bool, ) -> Result<(), rustical_store::Error> { @@ -630,9 +636,15 @@ impl AddressbookStore for SqliteAddressbookStore { .await .map_err(crate::Error::from)?; - let object_id = object.get_id().to_owned(); - - Self::_put_object(&mut *tx, &principal, &addressbook_id, &object, overwrite).await?; + Self::_put_object( + &mut *tx, + principal, + addressbook_id, + object_id, + &object, + overwrite, + ) + .await?; let sync_token = Self::log_object_operation( &mut tx, @@ -648,7 +660,7 @@ impl AddressbookStore for SqliteAddressbookStore { self.send_push_notification( CollectionOperationInfo::Content { sync_token }, - self.get_addressbook(&principal, &addressbook_id, false) + self.get_addressbook(principal, addressbook_id, false) .await? .push_topic, ); @@ -733,7 +745,7 @@ impl AddressbookStore for SqliteAddressbookStore { async fn import_addressbook( &self, addressbook: Addressbook, - objects: Vec, + objects: Vec<(String, AddressObject)>, merge_existing: bool, ) -> Result<(), Error> { let mut tx = self @@ -758,11 +770,12 @@ impl AddressbookStore for SqliteAddressbookStore { } let mut sync_token = None; - for object in objects { + for (object_id, object) in objects { Self::_put_object( &mut *tx, &addressbook.principal, &addressbook.id, + &object_id, &object, false, ) @@ -773,7 +786,7 @@ impl AddressbookStore for SqliteAddressbookStore { &mut tx, &addressbook.principal, &addressbook.id, - object.get_id(), + &object_id, ChangeOperation::Add, ) .await?, From a2255bc7f185bbf2c6d690e7f733937cda692ab6 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:14:50 +0100 Subject: [PATCH 06/26] make calendar object id extrinsic --- .../methods/report/calendar_multiget.rs | 4 +- .../report/calendar_query/comp_filter.rs | 6 +- .../methods/report/calendar_query/mod.rs | 4 +- .../methods/report/calendar_query/tests.rs | 2 +- .../caldav/src/calendar/methods/report/mod.rs | 7 +- .../methods/report/sync_collection.rs | 5 +- crates/caldav/src/calendar/service.rs | 5 +- crates/caldav/src/calendar_object/methods.rs | 4 +- crates/caldav/src/calendar_object/resource.rs | 6 +- crates/caldav/src/calendar_object/service.rs | 1 + crates/ical/src/calendar_object.rs | 14 +--- crates/ical/tests/test_cal_object.rs | 2 +- crates/store/src/calendar_store.rs | 30 +++++---- crates/store/src/combined_calendar_store.rs | 29 +++++---- .../benches/insert_calendar_object.rs | 11 ++-- .../addressbook_store/birthday_calendar.rs | 17 ++--- crates/store_sqlite/src/calendar_store.rs | 65 ++++++++++++------- 17 files changed, 116 insertions(+), 96 deletions(-) diff --git a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs index 2cf61ff..a16c6fa 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs @@ -21,7 +21,7 @@ pub async fn get_objects_calendar_multiget( principal: &str, cal_id: &str, store: &C, -) -> Result<(Vec, Vec), Error> { +) -> Result<(Vec<(String, CalendarObject)>, Vec), Error> { let mut result = vec![]; let mut not_found = vec![]; @@ -32,7 +32,7 @@ pub async fn get_objects_calendar_multiget( let filename = filename.trim_start_matches('/'); if let Some(object_id) = filename.strip_suffix(".ics") { match store.get_object(principal, cal_id, object_id, false).await { - Ok(object) => result.push(object), + Ok(object) => result.push((object_id.to_owned(), object)), Err(rustical_store::Error::NotFound) => not_found.push(href.to_string()), Err(err) => return Err(err.into()), } diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs index 27f10cc..517e486 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs @@ -130,7 +130,7 @@ END:VCALENDAR"; #[test] fn test_comp_filter_matching() { - let object = CalendarObject::from_ics(ICS.to_string(), None).unwrap(); + let object = CalendarObject::from_ics(ICS.to_string()).unwrap(); let comp_filter = CompFilterElement { is_not_defined: Some(()), @@ -231,7 +231,7 @@ END:VCALENDAR"; } #[test] fn test_comp_filter_time_range() { - let object = CalendarObject::from_ics(ICS.to_string(), None).unwrap(); + let object = CalendarObject::from_ics(ICS.to_string()).unwrap(); let comp_filter = CompFilterElement { is_not_defined: None, @@ -286,7 +286,7 @@ END:VCALENDAR"; #[test] fn test_match_timezone() { - let object = CalendarObject::from_ics(ICS.to_string(), None).unwrap(); + let object = CalendarObject::from_ics(ICS.to_string()).unwrap(); let comp_filter = CompFilterElement { is_not_defined: None, diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs index b674142..038b03c 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/mod.rs @@ -18,12 +18,12 @@ pub async fn get_objects_calendar_query( principal: &str, cal_id: &str, store: &C, -) -> Result, Error> { +) -> Result, Error> { let mut objects = store .calendar_query(principal, cal_id, cal_query.into()) .await?; if let Some(filter) = &cal_query.filter { - objects.retain(|object| filter.matches(object.get_inner())); + objects.retain(|(_id, object)| filter.matches(object.get_inner())); } Ok(objects) } diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs b/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs index 0163158..7f8119b 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/tests.rs @@ -77,7 +77,7 @@ const FILTER_2: &str = r#" #[case(ICS_1, FILTER_1, true)] #[case(ICS_1, FILTER_2, false)] fn yeet(#[case] ics: &str, #[case] filter: &str, #[case] matches: bool) { - let obj = CalendarObject::from_ics(ics.to_owned(), None).unwrap(); + let obj = CalendarObject::from_ics(ics.to_owned()).unwrap(); let filter = FilterElement::parse_str(filter).unwrap(); assert_eq!(matches, filter.matches(obj.get_inner())); } diff --git a/crates/caldav/src/calendar/methods/report/mod.rs b/crates/caldav/src/calendar/methods/report/mod.rs index b6b4bce..8d4e1da 100644 --- a/crates/caldav/src/calendar/methods/report/mod.rs +++ b/crates/caldav/src/calendar/methods/report/mod.rs @@ -51,7 +51,7 @@ impl ReportRequest { } fn objects_response( - objects: Vec, + objects: Vec<(String, CalendarObject)>, not_found: Vec, path: &str, principal: &str, @@ -60,11 +60,12 @@ fn objects_response( prop: &PropfindType, ) -> Result, Error> { let mut responses = Vec::new(); - for object in objects { - let path = format!("{}/{}.ics", path, object.get_id()); + for (object_id, object) in objects { + let path = format!("{path}/{object_id}.ics"); responses.push( CalendarObjectResource { object, + object_id, principal: principal.to_owned(), } .propfind(&path, prop, None, puri, user)?, diff --git a/crates/caldav/src/calendar/methods/report/sync_collection.rs b/crates/caldav/src/calendar/methods/report/sync_collection.rs index 7b70f20..97b6806 100644 --- a/crates/caldav/src/calendar/methods/report/sync_collection.rs +++ b/crates/caldav/src/calendar/methods/report/sync_collection.rs @@ -32,11 +32,12 @@ pub async fn handle_sync_collection( .await?; let mut responses = Vec::new(); - for object in new_objects { - let path = format!("{}/{}.ics", path, object.get_id()); + for (object_id, object) in new_objects { + let path = format!("{}/{}.ics", path, &object_id); responses.push( CalendarObjectResource { object, + object_id, principal: principal.to_owned(), } .propfind(&path, &sync_collection.prop, None, puri, user)?, diff --git a/crates/caldav/src/calendar/service.rs b/crates/caldav/src/calendar/service.rs index ec6384d..2f35acc 100644 --- a/crates/caldav/src/calendar/service.rs +++ b/crates/caldav/src/calendar/service.rs @@ -78,8 +78,9 @@ impl ResourceService for CalendarResourc .get_objects(principal, cal_id) .await? .into_iter() - .map(|object| CalendarObjectResource { + .map(|(object_id, object)| CalendarObjectResource { object, + object_id, principal: principal.to_owned(), }) .collect()) @@ -91,7 +92,7 @@ impl ResourceService for CalendarResourc file: Self::Resource, ) -> Result<(), Self::Error> { self.cal_store - .update_calendar(principal.to_owned(), cal_id.to_owned(), file.into()) + .update_calendar(principal, cal_id, file.into()) .await?; Ok(()) } diff --git a/crates/caldav/src/calendar_object/methods.rs b/crates/caldav/src/calendar_object/methods.rs index 7e475ac..2d7fc3f 100644 --- a/crates/caldav/src/calendar_object/methods.rs +++ b/crates/caldav/src/calendar_object/methods.rs @@ -94,13 +94,13 @@ pub async fn put_event( true }; - let Ok(object) = CalendarObject::from_ics(body.clone(), Some(object_id)) else { + let Ok(object) = CalendarObject::from_ics(body.clone()) else { debug!("invalid calendar data:\n{body}"); return Err(Error::PreconditionFailed(Precondition::ValidCalendarData)); }; let etag = object.get_etag(); cal_store - .put_object(principal, calendar_id, object, overwrite) + .put_object(&principal, &calendar_id, &object_id, object, overwrite) .await?; let mut headers = HeaderMap::new(); diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index 995b537..61c1571 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -1,5 +1,3 @@ -use std::borrow::Cow; - use super::prop::{ CalendarData, CalendarObjectProp, CalendarObjectPropName, CalendarObjectPropWrapper, CalendarObjectPropWrapperName, @@ -14,16 +12,18 @@ use rustical_dav::{ }; use rustical_ical::CalendarObject; use rustical_store::auth::Principal; +use std::borrow::Cow; #[derive(Clone, From, Into)] pub struct CalendarObjectResource { pub object: CalendarObject, + pub object_id: String, pub principal: String, } impl ResourceName for CalendarObjectResource { fn get_name(&self) -> Cow<'_, str> { - Cow::from(format!("{}.ics", self.object.get_id())) + Cow::from(format!("{}.ics", self.object_id)) } } diff --git a/crates/caldav/src/calendar_object/service.rs b/crates/caldav/src/calendar_object/service.rs index 8a63e12..97f1516 100644 --- a/crates/caldav/src/calendar_object/service.rs +++ b/crates/caldav/src/calendar_object/service.rs @@ -66,6 +66,7 @@ impl ResourceService for CalendarObjectResourceService { .await?; Ok(CalendarObjectResource { object, + object_id: object_id.to_owned(), principal: principal.to_owned(), }) } diff --git a/crates/ical/src/calendar_object.rs b/crates/ical/src/calendar_object.rs index 59bea86..4ddaa19 100644 --- a/crates/ical/src/calendar_object.rs +++ b/crates/ical/src/calendar_object.rs @@ -62,13 +62,12 @@ impl rustical_xml::ValueDeserialize for CalendarObjectType { #[derive(Debug, Clone)] pub struct CalendarObject { - id: String, inner: IcalCalendarObject, ics: String, } impl CalendarObject { - pub fn from_ics(ics: String, id: Option) -> Result { + pub fn from_ics(ics: String) -> Result { let mut parser: ComponentParser<_, IcalCalendarObject> = ComponentParser::new(ics.as_bytes()); let inner = parser.next().ok_or(Error::MissingCalendar)??; @@ -78,11 +77,7 @@ impl CalendarObject { )); } - Ok(Self { - id: id.unwrap_or_else(|| inner.get_uid().to_owned()), - inner, - ics, - }) + Ok(Self { inner, ics }) } #[must_use] @@ -95,11 +90,6 @@ impl CalendarObject { self.inner.get_uid() } - #[must_use] - pub fn get_id(&self) -> &str { - &self.id - } - #[must_use] pub fn get_etag(&self) -> String { let mut hasher = Sha256::new(); diff --git a/crates/ical/tests/test_cal_object.rs b/crates/ical/tests/test_cal_object.rs index 50b5a9f..392affd 100644 --- a/crates/ical/tests/test_cal_object.rs +++ b/crates/ical/tests/test_cal_object.rs @@ -25,6 +25,6 @@ END:VCALENDAR #[test] fn parse_calendar_object() { - let object = CalendarObject::from_ics(MULTI_VEVENT.to_string(), None).unwrap(); + let object = CalendarObject::from_ics(MULTI_VEVENT.to_string()).unwrap(); object.get_inner().expand_recurrence(None, None); } diff --git a/crates/store/src/calendar_store.rs b/crates/store/src/calendar_store.rs index 0c51909..ac9b311 100644 --- a/crates/store/src/calendar_store.rs +++ b/crates/store/src/calendar_store.rs @@ -22,8 +22,8 @@ pub trait CalendarStore: Send + Sync + 'static { async fn update_calendar( &self, - principal: String, - id: String, + principal: &str, + id: &str, calendar: Calendar, ) -> Result<(), Error>; async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error>; @@ -46,7 +46,7 @@ pub trait CalendarStore: Send + Sync + 'static { principal: &str, cal_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error>; + ) -> Result<(Vec<(String, CalendarObject)>, Vec, i64), Error>; /// Since the rules are rather complex this function /// is only meant to do some prefiltering @@ -55,7 +55,7 @@ pub trait CalendarStore: Send + Sync + 'static { principal: &str, cal_id: &str, _query: CalendarQuery, - ) -> Result, Error> { + ) -> Result, Error> { self.get_objects(principal, cal_id).await } @@ -69,7 +69,7 @@ pub trait CalendarStore: Send + Sync + 'static { &self, principal: &str, cal_id: &str, - ) -> Result, Error>; + ) -> Result, Error>; async fn get_object( &self, principal: &str, @@ -79,20 +79,26 @@ pub trait CalendarStore: Send + Sync + 'static { ) -> Result; async fn put_objects( &self, - principal: String, - cal_id: String, - objects: Vec, + principal: &str, + cal_id: &str, + objects: Vec<(String, CalendarObject)>, overwrite: bool, ) -> Result<(), Error>; async fn put_object( &self, - principal: String, - cal_id: String, + principal: &str, + cal_id: &str, + object_id: &str, object: CalendarObject, overwrite: bool, ) -> Result<(), Error> { - self.put_objects(principal, cal_id, vec![object], overwrite) - .await + self.put_objects( + principal, + cal_id, + vec![(object_id.to_owned(), object)], + overwrite, + ) + .await } async fn delete_object( &self, diff --git a/crates/store/src/combined_calendar_store.rs b/crates/store/src/combined_calendar_store.rs index a83b7e4..e987918 100644 --- a/crates/store/src/combined_calendar_store.rs +++ b/crates/store/src/combined_calendar_store.rs @@ -1,5 +1,6 @@ -use crate::CalendarStore; +use crate::{Calendar, CalendarStore, calendar_store::CalendarQuery}; use async_trait::async_trait; +use rustical_ical::CalendarObject; use std::{collections::HashMap, sync::Arc}; pub trait PrefixedCalendarStore: CalendarStore { @@ -51,11 +52,11 @@ impl CalendarStore for CombinedCalendarStore { async fn update_calendar( &self, - principal: String, - id: String, - calendar: crate::Calendar, + principal: &str, + id: &str, + calendar: Calendar, ) -> Result<(), crate::Error> { - self.store_for_id(&id) + self.store_for_id(id) .update_calendar(principal, id, calendar) .await } @@ -88,7 +89,7 @@ impl CalendarStore for CombinedCalendarStore { principal: &str, cal_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), crate::Error> { + ) -> Result<(Vec<(String, CalendarObject)>, Vec, i64), crate::Error> { self.store_for_id(cal_id) .sync_changes(principal, cal_id, synctoken) .await @@ -97,7 +98,7 @@ impl CalendarStore for CombinedCalendarStore { async fn import_calendar( &self, calendar: crate::Calendar, - objects: Vec, + objects: Vec, merge_existing: bool, ) -> Result<(), crate::Error> { self.store_for_id(&calendar.id) @@ -109,8 +110,8 @@ impl CalendarStore for CombinedCalendarStore { &self, principal: &str, cal_id: &str, - query: crate::calendar_store::CalendarQuery, - ) -> Result, crate::Error> { + query: CalendarQuery, + ) -> Result, crate::Error> { self.store_for_id(cal_id) .calendar_query(principal, cal_id, query) .await @@ -141,7 +142,7 @@ impl CalendarStore for CombinedCalendarStore { &self, principal: &str, cal_id: &str, - ) -> Result, crate::Error> { + ) -> Result, crate::Error> { self.store_for_id(cal_id) .get_objects(principal, cal_id) .await @@ -149,12 +150,12 @@ impl CalendarStore for CombinedCalendarStore { async fn put_objects( &self, - principal: String, - cal_id: String, - objects: Vec, + principal: &str, + cal_id: &str, + objects: Vec<(String, CalendarObject)>, overwrite: bool, ) -> Result<(), crate::Error> { - self.store_for_id(&cal_id) + self.store_for_id(cal_id) .put_objects(principal, cal_id, objects, overwrite) .await } diff --git a/crates/store_sqlite/benches/insert_calendar_object.rs b/crates/store_sqlite/benches/insert_calendar_object.rs index 02f0d42..4330720 100644 --- a/crates/store_sqlite/benches/insert_calendar_object.rs +++ b/crates/store_sqlite/benches/insert_calendar_object.rs @@ -34,16 +34,19 @@ fn benchmark(c: &mut Criterion) { cal_store }); - let object = CalendarObject::from_ics(include_str!("ical_event.ics").to_owned(), None).unwrap(); + let row = ( + "asd".to_owned(), + CalendarObject::from_ics(include_str!("ical_event.ics").to_owned()).unwrap(), + ); let batch_size = 1000; - let objects: Vec<_> = std::iter::repeat_n(object.clone(), batch_size).collect(); + let objects: Vec<_> = std::iter::repeat_n(row.clone(), batch_size).collect(); c.bench_function("put_batch", |b| { b.to_async(&runtime).iter(async || { // yeet cal_store - .put_objects("user".to_owned(), "okwow".to_owned(), objects.clone(), true) + .put_objects("user", "okwow", objects.clone(), true) .await .unwrap(); }); @@ -54,7 +57,7 @@ fn benchmark(c: &mut Criterion) { // yeet for _ in 0..1000 { cal_store - .put_object("user".to_owned(), "okwow".to_owned(), object.clone(), true) + .put_object("user", "okwow", &row.0, row.1.clone(), true) .await .unwrap(); } diff --git a/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs b/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs index a79d50b..8f49cf3 100644 --- a/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs +++ b/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs @@ -1,7 +1,7 @@ use crate::addressbook_store::SqliteAddressbookStore; use async_trait::async_trait; use chrono::NaiveDateTime; -use rustical_ical::{AddressObject, CalendarObject, CalendarObjectType}; +use rustical_ical::{CalendarObject, CalendarObjectType}; use rustical_store::{ Addressbook, AddressbookStore, Calendar, CalendarMetadata, CalendarStore, CollectionMetadata, Error, PrefixedCalendarStore, @@ -268,10 +268,11 @@ impl CalendarStore for SqliteAddressbookStore { #[instrument] async fn update_calendar( &self, - principal: String, - id: String, + principal: &str, + id: &str, mut calendar: Calendar, ) -> Result<(), Error> { + assert_eq!(principal, calendar.principal); assert_eq!(id, calendar.id); calendar.id = calendar .id @@ -323,7 +324,7 @@ impl CalendarStore for SqliteAddressbookStore { principal: &str, cal_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error> { + ) -> Result<(Vec<(String, CalendarObject)>, Vec, i64), Error> { let cal_id = cal_id .strip_prefix(BIRTHDAYS_PREFIX) .ok_or(Error::NotFound)?; @@ -356,7 +357,7 @@ impl CalendarStore for SqliteAddressbookStore { &self, principal: &str, cal_id: &str, - ) -> Result, Error> { + ) -> Result, Error> { todo!() // let cal_id = cal_id // .strip_prefix(BIRTHDAYS_PREFIX) @@ -397,9 +398,9 @@ impl CalendarStore for SqliteAddressbookStore { #[instrument] async fn put_objects( &self, - _principal: String, - _cal_id: String, - _objects: Vec, + _principal: &str, + _cal_id: &str, + _objects: Vec<(String, CalendarObject)>, _overwrite: bool, ) -> Result<(), Error> { Err(Error::ReadOnly) diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index cb2c582..6fdc61e 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -22,11 +22,11 @@ struct CalendarObjectRow { uid: String, } -impl TryFrom for CalendarObject { +impl TryFrom for (String, CalendarObject) { type Error = rustical_store::Error; fn try_from(value: CalendarObjectRow) -> Result { - let object = Self::from_ics(value.ics, Some(value.id))?; + let object = CalendarObject::from_ics(value.ics)?; if object.get_uid() != value.uid { return Err(rustical_store::Error::IcalError( rustical_ical::Error::InvalidData(format!( @@ -36,7 +36,7 @@ impl TryFrom for CalendarObject { )), )); } - Ok(object) + Ok((value.id, object)) } } @@ -358,8 +358,8 @@ impl SqliteCalendarStore { async fn _update_calendar<'e, E: Executor<'e, Database = Sqlite>>( executor: E, - principal: String, - id: String, + principal: &str, + id: &str, calendar: Calendar, ) -> Result<(), Error> { let comp_event = calendar.components.contains(&CalendarObjectType::Event); @@ -457,7 +457,7 @@ impl SqliteCalendarStore { executor: E, principal: &str, cal_id: &str, - ) -> Result, Error> { + ) -> Result, Error> { sqlx::query_as!( CalendarObjectRow, "SELECT id, uid, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL", @@ -476,7 +476,7 @@ impl SqliteCalendarStore { principal: &str, cal_id: &str, query: CalendarQuery, - ) -> Result, Error> { + ) -> Result, Error> { // We extend our query interval by one day in each direction since we really don't want to // miss any objects because of timezone differences // I've previously tried NaiveDate::MIN,MAX, but it seems like sqlite cannot handle these @@ -512,7 +512,7 @@ impl SqliteCalendarStore { object_id: &str, show_deleted: bool, ) -> Result { - sqlx::query_as!( + let (row_id, object) = sqlx::query_as!( CalendarObjectRow, "SELECT id, uid, ics FROM calendarobjects WHERE (principal, cal_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)", principal, @@ -523,7 +523,9 @@ impl SqliteCalendarStore { .fetch_one(executor) .await .map_err(crate::Error::from)? - .try_into() + .try_into()?; + assert_eq!(object_id, row_id); + Ok(object) } #[instrument] @@ -531,10 +533,11 @@ impl SqliteCalendarStore { executor: E, principal: &str, cal_id: &str, + object_id: &str, object: &CalendarObject, overwrite: bool, ) -> Result<(), Error> { - let (object_id, uid, ics) = (object.get_id(), object.get_uid(), object.get_ics()); + let (uid, ics) = (object.get_uid(), object.get_ics()); let first_occurence = object .get_inner() @@ -640,7 +643,7 @@ impl SqliteCalendarStore { principal: &str, cal_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error> { + ) -> Result<(Vec<(String, CalendarObject)>, Vec, i64), Error> { struct Row { object_id: String, synctoken: i64, @@ -667,7 +670,7 @@ impl SqliteCalendarStore { for Row { object_id, .. } in changes { match Self::_get_object(&mut *conn, principal, cal_id, &object_id, false).await { - Ok(object) => objects.push(object), + Ok(object) => objects.push((object_id, object)), Err(rustical_store::Error::NotFound) => deleted_objects.push(object_id), Err(err) => return Err(err), } @@ -707,8 +710,8 @@ impl CalendarStore for SqliteCalendarStore { #[instrument] async fn update_calendar( &self, - principal: String, - id: String, + principal: &str, + id: &str, calendar: Calendar, ) -> Result<(), Error> { Self::_update_calendar(&self.db, principal, id, calendar).await @@ -776,14 +779,23 @@ impl CalendarStore for SqliteCalendarStore { let mut sync_token = None; for object in objects { - Self::_put_object(&mut *tx, &calendar.principal, &calendar.id, &object, false).await?; + let object_id = object.get_uid(); + Self::_put_object( + &mut *tx, + &calendar.principal, + &calendar.id, + object_id, + &object, + false, + ) + .await?; sync_token = Some( Self::log_object_operation( &mut tx, &calendar.principal, &calendar.id, - object.get_id(), + object_id, ChangeOperation::Add, ) .await?, @@ -809,7 +821,7 @@ impl CalendarStore for SqliteCalendarStore { principal: &str, cal_id: &str, query: CalendarQuery, - ) -> Result, Error> { + ) -> Result, Error> { Self::_calendar_query(&self.db, principal, cal_id, query).await } @@ -840,7 +852,7 @@ impl CalendarStore for SqliteCalendarStore { &self, principal: &str, cal_id: &str, - ) -> Result, Error> { + ) -> Result, Error> { Self::_get_objects(&self.db, principal, cal_id).await } @@ -858,9 +870,9 @@ impl CalendarStore for SqliteCalendarStore { #[instrument] async fn put_objects( &self, - principal: String, - cal_id: String, - objects: Vec, + principal: &str, + cal_id: &str, + objects: Vec<(String, CalendarObject)>, overwrite: bool, ) -> Result<(), Error> { let mut tx = self @@ -876,18 +888,21 @@ impl CalendarStore for SqliteCalendarStore { } let mut sync_token = None; - for object in objects { + for (object_id, object) in objects { sync_token = Some( Self::log_object_operation( &mut tx, &principal, &cal_id, - object.get_id(), + &object_id, ChangeOperation::Add, ) .await?, ); - Self::_put_object(&mut *tx, &principal, &cal_id, &object, overwrite).await?; + Self::_put_object( + &mut *tx, &principal, &cal_id, &object_id, &object, overwrite, + ) + .await?; } tx.commit().await.map_err(crate::Error::from)?; @@ -965,7 +980,7 @@ impl CalendarStore for SqliteCalendarStore { principal: &str, cal_id: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error> { + ) -> Result<(Vec<(String, CalendarObject)>, Vec, i64), Error> { Self::_sync_changes(&self.db, principal, cal_id, synctoken).await } From 5639127782569c117a774ca6adf1acf4fb5b0d41 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Thu, 8 Jan 2026 14:31:28 +0100 Subject: [PATCH 07/26] clean up ical-related stuff --- Cargo.lock | 5567 +++++++++++++++++ crates/carddav/src/address_object/resource.rs | 3 +- crates/ical/src/address_object.rs | 20 +- crates/ical/src/calendar_object.rs | 10 +- crates/ical/src/icalendar.rs | 137 - 5 files changed, 5574 insertions(+), 163 deletions(-) create mode 100644 Cargo.lock delete mode 100644 crates/ical/src/icalendar.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..ff12d52 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,5567 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +dependencies = [ + "backtrace", +] + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "askama" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427" +dependencies = [ + "askama_macros", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama_derive" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994" +dependencies = [ + "askama_parser", + "basic-toml", + "memchr", + "proc-macro2", + "quote", + "rustc-hash", + "serde", + "serde_derive", + "syn 2.0.114", +] + +[[package]] +name = "askama_macros" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46" +dependencies = [ + "askama_derive", +] + +[[package]] +name = "askama_parser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e" +dependencies = [ + "rustc-hash", + "serde", + "serde_derive", + "unicode-ident", + "winnow", +] + +[[package]] +name = "askama_web" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d6576f8e59513752a3e2673ca602fb403be7d0d0aacba5cd8b219838ab58fe" +dependencies = [ + "askama", + "askama_web_derive", + "axum-core", + "bytes", + "http", +] + +[[package]] +name = "askama_web_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9767c17d33a63daf6da5872ffaf2ab0c289cd73ce7ed4f41d5ddf9149c004873" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.5.0", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener 5.4.1", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-std" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" +dependencies = [ + "async-attributes", + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef252edff26ddba56bbcdf2ee3307b8129acb86f5749b68990c168a6fcc9c76" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-core", + "futures-util", + "headers", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82" + +[[package]] +name = "basic-toml" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +dependencies = [ + "serde_core", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel 2.5.0", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", +] + +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf 0.12.1", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "criterion" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d883447757bb0ee46f233e9dc22eb84d93a9508c9b868687b274fc431d886bf" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed943f81ea2faa8dcecbbfa50164acf95d555afec96a27871663b300e387b2e4" +dependencies = [ + "cast", + "itertools 0.13.0", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.114", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.114", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ece" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ea1d2f2cc974957a4e2575d8e5bb494549bab66338d6320c2789abcfff5746" +dependencies = [ + "base64 0.21.7", + "byteorder", + "hex", + "hkdf", + "lazy_static", + "once_cell", + "openssl", + "sha2", + "thiserror 1.0.69", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "pear", + "serde", + "toml 0.8.23", + "uncased", + "version_check", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.13.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64 0.22.1", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ical" +version = "0.11.0" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#0a50f3998b8ae104642cceb9d974e99b78838b14" +dependencies = [ + "chrono", + "chrono-tz", + "derive_more", + "itertools 0.14.0", + "lazy_static", + "phf 0.13.1", + "regex", + "rrule", + "thiserror 2.0.17", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "insta" +version = "1.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b66886d14d18d420ab5052cbff544fc5d34d0b2cdd35eb5976aaa10a4a472e5" +dependencies = [ + "console", + "once_cell", + "regex", + "similar", + "tempfile", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags", + "libc", + "redox_syscall 0.7.0", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", + "serde", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "matchit" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3eede3bdf92f3b4f9dc04072a9ce5ab557d5ec9038773bf9ffcd5588b3cc05b" + +[[package]] +name = "matchit-serde" +version = "0.1.0" +source = "git+https://github.com/lennart-k/matchit-serde?rev=e18e65d7#e18e65d75cb20ab5f6a193c84a87ee2db8e6ae0b" +dependencies = [ + "derive_more", + "matchit 0.9.1", + "percent-encoding", + "serde", + "thiserror 2.0.17", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom 0.2.16", + "http", + "rand 0.8.5", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "openidconnect" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c6709ba2ea764bbed26bce1adf3c10517113ddea6f2d4196e4851757ef2b2" +dependencies = [ + "base64 0.21.7", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http", + "itertools 0.10.5", + "log", + "oauth2", + "p256", + "p384", + "rand 0.8.5", + "rsa", + "serde", + "serde-value", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "openssl-src" +version = "300.5.4+3.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "opentelemetry" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.17", + "tracing", +] + +[[package]] +name = "opentelemetry-http" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" +dependencies = [ + "http", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "thiserror 2.0.17", + "tokio", + "tonic", + "tracing", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" +dependencies = [ + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", + "tonic-prost", +] + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" + +[[package]] +name = "opentelemetry_sdk" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "opentelemetry", + "percent-encoding", + "rand 0.9.2", + "thiserror 2.0.17", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "password-auth" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2a4764cc1f8d961d802af27193c6f4f0124bd0e76e8393cf818e18880f0524" +dependencies = [ + "argon2", + "getrandom 0.2.16", + "password-hash", + "pbkdf2", + "rand_core 0.6.4", +] + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "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]] +name = "phf_codegen" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61" +dependencies = [ + "phf_generator 0.12.1", + "phf_shared 0.12.1", +] + +[[package]] +name = "phf_generator" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" +dependencies = [ + "fastrand", + "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.114", +] + +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", + "version_check", + "yansi", +] + +[[package]] +name = "prost" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_syscall" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rpassword" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.59.0", +] + +[[package]] +name = "rrule" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "720acfb4980b9d8a6a430f6d7a11933e701ebbeba5eee39cc9d8c5f932aaff74" +dependencies = [ + "chrono", + "chrono-tz", + "log", + "regex", + "thiserror 2.0.17", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +dependencies = [ + "futures-timer", + "futures-util", + "rstest_macros", +] + +[[package]] +name = "rstest_macros" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.114", + "unicode-ident", +] + +[[package]] +name = "rstest_reuse" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a8fb4672e840a587a66fc577a5491375df51ddb88f2a2c2a792598c326fe14" +dependencies = [ + "quote", + "rand 0.8.5", + "syn 2.0.114", +] + +[[package]] +name = "rtoolbox" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "rust-embed" +version = "8.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.114", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustical" +version = "0.11.10" +dependencies = [ + "anyhow", + "argon2", + "async-trait", + "axum", + "axum-extra", + "clap", + "figment", + "headers", + "http", + "insta", + "opentelemetry", + "opentelemetry-otlp", + "opentelemetry-semantic-conventions", + "opentelemetry_sdk", + "password-hash", + "pbkdf2", + "quick-xml", + "reqwest", + "rpassword", + "rstest", + "rustical_caldav", + "rustical_carddav", + "rustical_dav", + "rustical_dav_push", + "rustical_frontend", + "rustical_oidc", + "rustical_store", + "rustical_store_sqlite", + "serde", + "sqlx", + "tokio", + "toml 0.9.10+spec-1.1.0", + "tower", + "tower-http", + "tower-sessions", + "tracing", + "tracing-opentelemetry", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "rustical_caldav" +version = "0.11.10" +dependencies = [ + "async-std", + "async-trait", + "axum", + "axum-extra", + "base64 0.22.1", + "chrono", + "chrono-tz", + "derive_more", + "futures-util", + "headers", + "http", + "ical", + "insta", + "percent-encoding", + "quick-xml", + "rstest", + "rustical_dav", + "rustical_dav_push", + "rustical_ical", + "rustical_store", + "rustical_store_sqlite", + "rustical_xml", + "serde", + "serde_json", + "sha2", + "similar-asserts", + "strum", + "strum_macros", + "thiserror 2.0.17", + "tokio", + "tower", + "tower-http", + "tracing", + "url", + "uuid", + "vtimezones-rs", +] + +[[package]] +name = "rustical_carddav" +version = "0.11.10" +dependencies = [ + "async-trait", + "axum", + "axum-extra", + "base64 0.22.1", + "chrono", + "derive_more", + "futures-util", + "http", + "ical", + "insta", + "percent-encoding", + "quick-xml", + "rstest", + "rustical_dav", + "rustical_dav_push", + "rustical_ical", + "rustical_store", + "rustical_xml", + "serde", + "strum", + "strum_macros", + "thiserror 2.0.17", + "tokio", + "tower", + "tower-http", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "rustical_dav" +version = "0.11.10" +dependencies = [ + "async-trait", + "axum", + "axum-extra", + "derive_more", + "futures-util", + "headers", + "http", + "ical", + "itertools 0.14.0", + "log", + "matchit 0.9.1", + "matchit-serde", + "quick-xml", + "rustical_xml", + "serde", + "strum", + "thiserror 2.0.17", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "rustical_dav_push" +version = "0.11.10" +dependencies = [ + "async-trait", + "axum", + "base64 0.22.1", + "derive_more", + "ece", + "futures-util", + "http", + "itertools 0.14.0", + "log", + "openssl", + "quick-xml", + "reqwest", + "rustical_dav", + "rustical_store", + "rustical_xml", + "serde", + "thiserror 2.0.17", + "tokio", + "tracing", +] + +[[package]] +name = "rustical_frontend" +version = "0.11.10" +dependencies = [ + "askama", + "askama_web", + "async-trait", + "axum", + "axum-extra", + "chrono", + "chrono-humanize", + "futures-core", + "headers", + "hex", + "http", + "itertools 0.14.0", + "mime_guess", + "percent-encoding", + "rand 0.9.2", + "rust-embed", + "rustical_ical", + "rustical_oidc", + "rustical_store", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tower", + "tower-http", + "tower-sessions", + "tracing", + "url", + "uuid", + "vtimezones-rs", +] + +[[package]] +name = "rustical_ical" +version = "0.11.10" +dependencies = [ + "axum", + "chrono", + "chrono-tz", + "derive_more", + "ical", + "regex", + "rrule", + "rstest", + "rustical_xml", + "serde", + "sha2", + "similar-asserts", + "thiserror 2.0.17", +] + +[[package]] +name = "rustical_oidc" +version = "0.11.10" +dependencies = [ + "async-trait", + "axum", + "axum-extra", + "headers", + "openidconnect", + "reqwest", + "serde", + "thiserror 2.0.17", + "tower-sessions", + "tracing", +] + +[[package]] +name = "rustical_store" +version = "0.11.10" +dependencies = [ + "anyhow", + "async-trait", + "axum", + "chrono", + "chrono-tz", + "clap", + "derive_more", + "futures-core", + "headers", + "http", + "ical", + "regex", + "rrule", + "rstest", + "rstest_reuse", + "rustical_dav", + "rustical_ical", + "rustical_store_sqlite", + "rustical_xml", + "serde", + "sha2", + "thiserror 2.0.17", + "tokio", + "tower", + "tower-sessions", + "tracing", + "vtimezones-rs", +] + +[[package]] +name = "rustical_store_sqlite" +version = "0.11.10" +dependencies = [ + "async-trait", + "chrono", + "criterion", + "derive_more", + "ical", + "password-auth", + "password-hash", + "pbkdf2", + "rstest", + "rustical_ical", + "rustical_store", + "serde", + "sha2", + "sqlx", + "thiserror 2.0.17", + "tokio", + "tracing", + "uuid", +] + +[[package]] +name = "rustical_xml" +version = "0.11.10" +dependencies = [ + "quick-xml", + "thiserror 2.0.17", + "xml_derive", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.13.0", + "schemars 0.9.0", + "schemars 1.2.0", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +dependencies = [ + "bstr", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" +dependencies = [ + "console", + "similar", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64 0.22.1", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener 5.4.1", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.5", + "hashlink", + "indexmap 2.13.0", + "log", + "memchr", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.114", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.114", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.17", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.5", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.17", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.17", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "tempfile" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "0.9.10+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +dependencies = [ + "indexmap 2.13.0", + "serde_core", + "serde_spanned 1.0.4", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.13.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + +[[package]] +name = "tonic" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-prost" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 2.13.0", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-cookies" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151b5a3e3c45df17466454bb74e9ecedecc955269bdedbf4d150dfa393b55a36" +dependencies = [ + "axum-core", + "cookie", + "futures-util", + "http", + "parking_lot", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tower-sessions" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a05911f23e8fae446005fe9b7b97e66d95b6db589dc1c4d59f6a2d4d4927d3" +dependencies = [ + "async-trait", + "http", + "time", + "tokio", + "tower-cookies", + "tower-layer", + "tower-service", + "tower-sessions-core", + "tower-sessions-memory-store", + "tracing", +] + +[[package]] +name = "tower-sessions-core" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce8cce604865576b7751b7a6bc3058f754569a60d689328bb74c52b1d87e355b" +dependencies = [ + "async-trait", + "axum-core", + "base64 0.22.1", + "futures", + "http", + "parking_lot", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror 2.0.17", + "time", + "tokio", + "tracing", +] + +[[package]] +name = "tower-sessions-memory-store" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb05909f2e1420135a831dd5df9f5596d69196d0a64c3499ca474c4bd3d33242" +dependencies = [ + "async-trait", + "time", + "tokio", + "tower-sessions-core", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6e5658463dd88089aba75c7791e1d3120633b1bfde22478b28f625a9bb1b8e" +dependencies = [ + "js-sys", + "opentelemetry", + "opentelemetry_sdk", + "rustversion", + "smallvec", + "thiserror 2.0.17", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "getrandom 0.3.4", + "js-sys", + "rand 0.9.2", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "value-bag" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vtimezones-rs" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6728de8767c8dea44f41b88115a205ed23adc3302e1b4342be59d922934dae5" +dependencies = [ + "glob", + "phf 0.12.1", + "phf_codegen", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.114", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "xml_derive" +version = "0.11.10" +dependencies = [ + "darling 0.23.0", + "heck", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "zmij" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" diff --git a/crates/carddav/src/address_object/resource.rs b/crates/carddav/src/address_object/resource.rs index ed5f7f0..75a5ac7 100644 --- a/crates/carddav/src/address_object/resource.rs +++ b/crates/carddav/src/address_object/resource.rs @@ -70,7 +70,8 @@ impl Resource for AddressObjectResource { } fn get_displayname(&self) -> Option<&str> { - self.object.get_full_name() + todo!() + // self.object.get_full_name() } fn get_owner(&self) -> Option<&str> { diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index e631fec..52c9db8 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -1,9 +1,6 @@ use crate::{CalendarObject, Error}; use ical::generator::Emitter; -use ical::parser::{ - Component, - vcard::{self, component::VcardContact}, -}; +use ical::parser::vcard::{self, component::VcardContact}; use sha2::{Digest, Sha256}; use std::{collections::HashMap, io::BufReader}; @@ -22,13 +19,8 @@ impl From for AddressObject { impl AddressObject { pub fn from_vcf(vcf: String) -> Result { - let mut parser = vcard::VcardParser::new(BufReader::new(vcf.as_bytes())); - let vcard = parser.next().ok_or(Error::MissingContact)??; - if parser.next().is_some() { - return Err(Error::InvalidData( - "multiple vcards, only one allowed".to_owned(), - )); - } + let parser = vcard::VcardParser::new(BufReader::new(vcf.as_bytes())); + let vcard = parser.expect_one()?; Ok(Self { vcf, vcard }) } @@ -44,12 +36,6 @@ impl AddressObject { &self.vcf } - #[must_use] - pub fn get_full_name(&self) -> Option<&str> { - let prop = self.vcard.get_property("FN")?; - prop.value.as_deref() - } - pub fn get_anniversary_object(&self) -> Result, Error> { todo!(); } diff --git a/crates/ical/src/calendar_object.rs b/crates/ical/src/calendar_object.rs index 4ddaa19..ead3cf0 100644 --- a/crates/ical/src/calendar_object.rs +++ b/crates/ical/src/calendar_object.rs @@ -68,14 +68,8 @@ pub struct CalendarObject { impl CalendarObject { pub fn from_ics(ics: String) -> Result { - let mut parser: ComponentParser<_, IcalCalendarObject> = - ComponentParser::new(ics.as_bytes()); - let inner = parser.next().ok_or(Error::MissingCalendar)??; - if parser.next().is_some() { - return Err(Error::InvalidData( - "multiple calendars, only one allowed".to_owned(), - )); - } + let parser: ComponentParser<_, IcalCalendarObject> = ComponentParser::new(ics.as_bytes()); + let inner = parser.expect_one()?; Ok(Self { inner, ics }) } diff --git a/crates/ical/src/icalendar.rs b/crates/ical/src/icalendar.rs deleted file mode 100644 index 3ad7d0e..0000000 --- a/crates/ical/src/icalendar.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::Error; -use chrono::DateTime; -use chrono::Utc; -use derive_more::Display; -use ical::component::CalendarInnerData; -use ical::component::IcalCalendarObject; -use ical::parser::ComponentParser; -use ical::types::CalDateTime; -use serde::Deserialize; -use serde::Serialize; -use sha2::{Digest, Sha256}; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Display)] -// specified in https://datatracker.ietf.org/doc/html/rfc5545#section-3.6 -pub enum CalendarObjectType { - #[serde(rename = "VEVENT")] - Event = 0, - #[serde(rename = "VTODO")] - Todo = 1, - #[serde(rename = "VJOURNAL")] - Journal = 2, -} - -impl From<&IcalCalendarObject> for CalendarObjectType { - fn from(value: &IcalCalendarObject) -> Self { - match value.get_inner() { - CalendarInnerData::Event(_, _) => Self::Event, - CalendarInnerData::Todo(_, _) => Self::Todo, - CalendarInnerData::Journal(_, _) => Self::Journal, - } - } -} - -impl CalendarObjectType { - #[must_use] - pub const fn as_str(&self) -> &'static str { - match self { - Self::Event => "VEVENT", - Self::Todo => "VTODO", - Self::Journal => "VJOURNAL", - } - } -} - -impl rustical_xml::ValueSerialize for CalendarObjectType { - fn serialize(&self) -> String { - self.as_str().to_owned() - } -} - -impl rustical_xml::ValueDeserialize for CalendarObjectType { - fn deserialize(val: &str) -> std::result::Result { - match ::deserialize(val)?.as_str() { - "VEVENT" => Ok(Self::Event), - "VTODO" => Ok(Self::Todo), - "VJOURNAL" => Ok(Self::Journal), - _ => Err(rustical_xml::XmlError::InvalidValue( - rustical_xml::ParseValueError::Other(format!( - "Invalid value '{val}', must be VEVENT, VTODO, or VJOURNAL" - )), - )), - } - } -} - -#[derive(Debug, Clone)] -pub struct CalendarObject { - id: String, - inner: IcalCalendarObject, - ics: String, -} - -impl CalendarObject { - pub fn from_ics(ics: String, id: Option) -> Result { - let mut parser: ComponentParser<_, IcalCalendarObject> = - ComponentParser::new(ics.as_bytes()); - let inner = parser.next().ok_or(Error::MissingCalendar)??; - if parser.next().is_some() { - return Err(Error::InvalidData( - "multiple calendars, only one allowed".to_owned(), - )); - } - - Ok(Self { - id: id.unwrap_or_else(|| inner.get_uid().to_owned()), - inner, - ics, - }) - } - - #[must_use] - pub const fn get_inner(&self) -> &IcalCalendarObject { - &self.inner - } - - #[must_use] - pub fn get_uid(&self) -> &str { - self.inner.get_uid() - } - - #[must_use] - pub fn get_id(&self) -> &str { - &self.id - } - - #[must_use] - pub fn get_etag(&self) -> String { - let mut hasher = Sha256::new(); - hasher.update(self.get_uid()); - hasher.update(self.get_ics()); - format!("\"{:x}\"", hasher.finalize()) - } - - #[must_use] - pub fn get_ics(&self) -> &str { - &self.ics - } - - #[must_use] - pub fn get_component_name(&self) -> &str { - self.get_object_type().as_str() - } - - #[must_use] - pub fn get_object_type(&self) -> CalendarObjectType { - (&self.inner).into() - } - - pub fn expand_recurrence( - &self, - start: Option>, - end: Option>, - ) -> Result { - // Ok(self.inner.expand_recurrence(start, end)?) - todo!() - } -} From 977fd75500813ae53eb000786bf5e7e8cf4a6d0a Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:36:02 +0100 Subject: [PATCH 08/26] Re-implement calendar export --- Cargo.lock | 2 +- crates/caldav/src/calendar/methods/get.rs | 131 ++++++++---------- crates/caldav/src/calendar/methods/import.rs | 3 + .../caldav/src/calendar/methods/mkcalendar.rs | 2 +- crates/caldav/src/calendar/resource.rs | 2 +- crates/caldav/src/calendar_object/resource.rs | 22 +-- crates/ical/src/calendar_object.rs | 6 + 7 files changed, 82 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ff12d52..f692c80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#0a50f3998b8ae104642cceb9d974e99b78838b14" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#8732224dd1514f6dcbccdcf63268a3f223d360e9" dependencies = [ "chrono", "chrono-tz", diff --git a/crates/caldav/src/calendar/methods/get.rs b/crates/caldav/src/calendar/methods/get.rs index 368870b..c2c1b76 100644 --- a/crates/caldav/src/calendar/methods/get.rs +++ b/crates/caldav/src/calendar/methods/get.rs @@ -5,11 +5,11 @@ use axum::extract::State; use axum::{extract::Path, response::Response}; 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::collections::HashMap; use std::str::FromStr; use tracing::instrument; @@ -31,79 +31,62 @@ pub async fn route_get( return Err(crate::Error::Unauthorized); } - // let mut vtimezones = HashMap::new(); - // let objects = cal_store.get_objects(&principal, &calendar_id).await?; + let objects = cal_store + .get_objects(&principal, &calendar_id) + .await? + .into_iter() + .map(|(_, object)| object.into()) + .collect(); - todo!() + let mut props = vec![]; - // let mut ical_calendar_builder = IcalCalendarBuilder::version("2.0") - // .gregorian() - // .prodid("RustiCal"); - // if let Some(displayname) = calendar.meta.displayname { - // ical_calendar_builder = ical_calendar_builder.set(ContentLine { - // name: "X-WR-CALNAME".to_owned(), - // value: Some(displayname), - // params: vec![].into(), - // }); - // } - // if let Some(description) = calendar.meta.description { - // ical_calendar_builder = ical_calendar_builder.set(ContentLine { - // name: "X-WR-CALDESC".to_owned(), - // value: Some(description), - // params: vec![].into(), - // }); - // } - // if let Some(timezone_id) = calendar.timezone_id { - // ical_calendar_builder = ical_calendar_builder.set(ContentLine { - // name: "X-WR-TIMEZONE".to_owned(), - // value: Some(timezone_id), - // params: vec![].into(), - // }); - // } - // - // for object in &objects { - // vtimezones.extend(object.get_vtimezones()); - // match object.get_data() { - // CalendarObjectComponent::Event(EventObject { event, .. }, overrides) => { - // ical_calendar_builder = ical_calendar_builder - // .add_event(event.clone()) - // .add_events(overrides.iter().map(|ev| ev.event.clone())); - // } - // CalendarObjectComponent::Todo(todo, overrides) => { - // ical_calendar_builder = ical_calendar_builder - // .add_todo(todo.clone()) - // .add_todos(overrides.iter().cloned()); - // } - // CalendarObjectComponent::Journal(journal, overrides) => { - // ical_calendar_builder = ical_calendar_builder - // .add_journal(journal.clone()) - // .add_journals(overrides.iter().cloned()); - // } - // } - // } - // - // ical_calendar_builder = ical_calendar_builder.add_timezones(vtimezones.into_values().cloned()); - // - // let ical_calendar = ical_calendar_builder - // .build() - // .map_err(|parser_error| Error::IcalError(parser_error.into()))?; - // - // let mut resp = Response::builder().status(StatusCode::OK); - // let hdrs = resp.headers_mut().unwrap(); - // hdrs.typed_insert(ContentType::from_str("text/calendar; charset=utf-8").unwrap()); - // - // let filename = format!("{}_{}.ics", calendar.principal, calendar.id); - // let filename = utf8_percent_encode(&filename, CONTROLS); - // hdrs.insert( - // header::CONTENT_DISPOSITION, - // HeaderValue::from_str(&format!( - // "attachement; filename*=UTF-8''{filename}; filename={filename}", - // )) - // .unwrap(), - // ); - // if matches!(method, Method::HEAD) { - // Ok(resp.body(Body::empty()).unwrap()) - // } else { - // Ok(resp.body(Body::new(ical_calendar.generate())).unwrap()) - // } + if let Some(displayname) = calendar.meta.displayname { + props.push(ContentLine { + name: "X-WR-CALNAME".to_owned(), + value: Some(displayname), + params: vec![].into(), + }); + } + if let Some(description) = calendar.meta.description { + props.push(ContentLine { + name: "X-WR-CALDESC".to_owned(), + value: Some(description), + params: vec![].into(), + }); + } + if let Some(color) = calendar.meta.color { + props.push(ContentLine { + name: "X-WR-CALCOLOR".to_owned(), + value: Some(color), + params: vec![].into(), + }); + } + if let Some(timezone_id) = calendar.timezone_id { + props.push(ContentLine { + name: "X-WR-TIMEZONE".to_owned(), + value: Some(timezone_id), + params: vec![].into(), + }); + } + + let export_calendar = IcalCalendar::from_objects(objects, props); + + let mut resp = Response::builder().status(StatusCode::OK); + let hdrs = resp.headers_mut().unwrap(); + hdrs.typed_insert(ContentType::from_str("text/calendar; charset=utf-8").unwrap()); + + let filename = format!("{}_{}.ics", calendar.principal, calendar.id); + let filename = utf8_percent_encode(&filename, CONTROLS); + hdrs.insert( + header::CONTENT_DISPOSITION, + HeaderValue::from_str(&format!( + "attachement; filename*=UTF-8''{filename}; filename={filename}", + )) + .unwrap(), + ); + if matches!(method, Method::HEAD) { + Ok(resp.body(Body::empty()).unwrap()) + } else { + Ok(resp.body(Body::new(export_calendar.generate())).unwrap()) + } } diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index fe4f153..03746fc 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -46,6 +46,9 @@ pub async fn route_import( let description = cal .get_property("X-WR-CALDESC") .and_then(|prop| prop.value.clone()); + let color = cal + .get_property("X-WR-CALCOLOR") + .and_then(|prop| prop.value.clone()); let timezone_id = cal .get_property("X-WR-TIMEZONE") .and_then(|prop| prop.value.clone()); diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs index 71a8a09..f85ca76 100644 --- a/crates/caldav/src/calendar/methods/mkcalendar.rs +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -92,7 +92,7 @@ pub async fn route_mkcalendar( .ok_or_else(|| rustical_dav::Error::BadRequest("No timezone data provided".to_owned()))? .map_err(|_| rustical_dav::Error::BadRequest("Error parsing timezone".to_owned()))?; - let timezone = calendar.vtimezones.first().ok_or_else(|| { + let timezone = calendar.vtimezones.values().next().ok_or_else(|| { rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) })?; let timezone: Option = timezone.into(); diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index cb37d71..f80a4d4 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -215,7 +215,7 @@ impl Resource for CalendarResource { ) })?; - let timezone = calendar.vtimezones.first().ok_or_else(|| { + let timezone = calendar.vtimezones.values().next().ok_or_else(|| { rustical_dav::Error::BadRequest("No timezone data provided".to_owned()) })?; let timezone: Option = timezone.into(); diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index 61c1571..0bb74d7 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -4,6 +4,7 @@ use super::prop::{ }; use crate::Error; use derive_more::derive::{From, Into}; +use ical::generator::Emitter; use rustical_dav::{ extensions::CommonPropertiesExtension, privileges::UserPrivilegeSet, @@ -53,15 +54,18 @@ impl Resource for CalendarObjectResource { CalendarObjectProp::Getetag(self.object.get_etag()) } CalendarObjectPropName::CalendarData(CalendarData { expand, .. }) => { - CalendarObjectProp::CalendarData(if let Some(expand) = expand.as_ref() { - todo!() - // self.object.get_inner().expand_recurrence( - // Some(expand.start.to_utc()), - // Some(expand.end.to_utc()), - // ) - } else { - self.object.get_ics().to_owned() - }) + CalendarObjectProp::CalendarData(expand.as_ref().map_or_else( + || self.object.get_ics().to_owned(), + |expand| { + self.object + .get_inner() + .expand_recurrence( + Some(expand.start.to_utc()), + Some(expand.end.to_utc()), + ) + .generate() + }, + )) } CalendarObjectPropName::Getcontenttype => { CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8") diff --git a/crates/ical/src/calendar_object.rs b/crates/ical/src/calendar_object.rs index ead3cf0..c7518a8 100644 --- a/crates/ical/src/calendar_object.rs +++ b/crates/ical/src/calendar_object.rs @@ -102,3 +102,9 @@ impl CalendarObject { (&self.inner).into() } } + +impl From for IcalCalendarObject { + fn from(value: CalendarObject) -> Self { + value.inner + } +} From 43fff63008da91e3cc7cc29794d3577979070a89 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:17:39 +0100 Subject: [PATCH 09/26] Calendar export: Fix PRODID --- Cargo.lock | 2 +- crates/caldav/src/calendar/methods/get.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f692c80..3d59a39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#8732224dd1514f6dcbccdcf63268a3f223d360e9" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#dd310bbb9866dd2b51d3e83e2b3572e4a3c7f7c9" dependencies = [ "chrono", "chrono-tz", diff --git a/crates/caldav/src/calendar/methods/get.rs b/crates/caldav/src/calendar/methods/get.rs index c2c1b76..c00cf72 100644 --- a/crates/caldav/src/calendar/methods/get.rs +++ b/crates/caldav/src/calendar/methods/get.rs @@ -69,7 +69,7 @@ pub async fn route_get( }); } - let export_calendar = IcalCalendar::from_objects(objects, props); + let export_calendar = IcalCalendar::from_objects("RustiCal Export".to_owned(), objects, props); let mut resp = Response::builder().status(StatusCode::OK); let hdrs = resp.headers_mut().unwrap(); From 276fdcacf51f4da080999213d2b60a13d89893f9 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Thu, 8 Jan 2026 23:17:39 +0100 Subject: [PATCH 10/26] Re-implement calendar imports --- Cargo.lock | 2 +- crates/caldav/src/calendar/methods/import.rs | 126 +++++++++---------- crates/ical/src/calendar_object.rs | 10 ++ 3 files changed, 70 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d59a39..d0194e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#dd310bbb9866dd2b51d3e83e2b3572e4a3c7f7c9" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#a64b2f6b2920c238f0aee6dd02bafcda5ca76040" dependencies = [ "chrono", "chrono-tz", diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index 03746fc..054b7ef 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -5,13 +5,13 @@ use axum::{ response::{IntoResponse, Response}, }; use http::StatusCode; -use ical::{generator::Emitter, parser::Component}; +use ical::parser::{Component, ComponentMut}; use rustical_dav::header::Overwrite; -use rustical_ical::{CalendarObject, CalendarObjectType}; +use rustical_ical::CalendarObjectType; use rustical_store::{ Calendar, CalendarMetadata, CalendarStore, SubscriptionStore, auth::Principal, }; -use std::io::BufReader; +use std::{collections::HashMap, io::BufReader}; use tracing::instrument; #[instrument(skip(resource_service))] @@ -26,18 +26,11 @@ pub async fn route_import( return Err(Error::Unauthorized); } - let mut parser = ical::IcalParser::new(BufReader::new(body.as_bytes())); + let parser = ical::IcalParser::new(BufReader::new(body.as_bytes())); let mut cal = parser - .next() - .expect("input must contain calendar") - .unwrap() + .expect_one() + .map_err(rustical_ical::Error::ParserError)? .mutable(); - if parser.next().is_some() { - return Err(rustical_ical::Error::InvalidData( - "multiple calendars, only one allowed".to_owned(), - ) - .into()); - } // Extract calendar metadata let displayname = cal @@ -53,59 +46,58 @@ pub async fn route_import( .get_property("X-WR-TIMEZONE") .and_then(|prop| prop.value.clone()); // These properties should not appear in the expanded calendar objects - todo!(); - // cal.remove_property("X-WR-CALNAME"); - // cal.remove_property("X-WR-CALDESC"); - // cal.remove_property("X-WR-TIMEZONE"); - // let cal = cal.verify().unwrap(); - // // Make sure timezone is valid - // if let Some(timezone_id) = timezone_id.as_ref() { - // assert!( - // vtimezones_rs::VTIMEZONES.contains_key(timezone_id), - // "Invalid calendar timezone id" - // ); - // } - // + cal.remove_property("X-WR-CALNAME"); + cal.remove_property("X-WR-CALDESC"); + cal.remove_property("X-WR-CALCOLOR"); + cal.remove_property("X-WR-TIMEZONE"); + let cal = cal.build(&HashMap::new()).unwrap(); + + // Make sure timezone is valid + if let Some(timezone_id) = timezone_id.as_ref() { + assert!( + vtimezones_rs::VTIMEZONES.contains_key(timezone_id), + "Invalid calendar timezone id" + ); + } // // Extract necessary component types - // let mut cal_components = vec![]; - // if !cal.events.is_empty() { - // cal_components.push(CalendarObjectType::Event); - // } - // if !cal.journals.is_empty() { - // cal_components.push(CalendarObjectType::Journal); - // } - // if !cal.todos.is_empty() { - // cal_components.push(CalendarObjectType::Todo); - // } - // - // let expanded_cals = cal.expand_calendar(); - // // Janky way to convert between IcalCalendar and CalendarObject - // let objects = expanded_cals - // .into_iter() - // .map(|cal| cal.generate()) - // .map(|ics| CalendarObject::from_ics(ics, None)) - // .collect::, _>>()?; - // let new_cal = Calendar { - // principal, - // id: cal_id, - // meta: CalendarMetadata { - // displayname, - // order: 0, - // description, - // color: None, - // }, - // timezone_id, - // deleted_at: None, - // synctoken: 0, - // subscription_url: None, - // push_topic: uuid::Uuid::new_v4().to_string(), - // components: cal_components, - // }; - // - // let cal_store = resource_service.cal_store; - // cal_store - // .import_calendar(new_cal, objects, overwrite) - // .await?; - // - // Ok(StatusCode::OK.into_response()) + let mut cal_components = vec![]; + if !cal.events.is_empty() { + cal_components.push(CalendarObjectType::Event); + } + if !cal.journals.is_empty() { + cal_components.push(CalendarObjectType::Journal); + } + if !cal.todos.is_empty() { + cal_components.push(CalendarObjectType::Todo); + } + + let objects = cal + .into_objects() + .map_err(rustical_ical::Error::ParserError)? + .into_iter() + .map(Into::into) + .collect(); + let new_cal = Calendar { + principal, + id: cal_id, + meta: CalendarMetadata { + displayname, + order: 0, + description, + color, + }, + timezone_id, + deleted_at: None, + synctoken: 0, + subscription_url: None, + push_topic: uuid::Uuid::new_v4().to_string(), + components: cal_components, + }; + + let cal_store = resource_service.cal_store; + cal_store + .import_calendar(new_cal, objects, overwrite) + .await?; + + Ok(StatusCode::OK.into_response()) } diff --git a/crates/ical/src/calendar_object.rs b/crates/ical/src/calendar_object.rs index c7518a8..dcecf56 100644 --- a/crates/ical/src/calendar_object.rs +++ b/crates/ical/src/calendar_object.rs @@ -2,6 +2,7 @@ use crate::Error; use derive_more::Display; use ical::component::CalendarInnerData; use ical::component::IcalCalendarObject; +use ical::generator::Emitter; use ical::parser::ComponentParser; use serde::Deserialize; use serde::Serialize; @@ -108,3 +109,12 @@ impl From for IcalCalendarObject { value.inner } } + +impl From for CalendarObject { + fn from(value: IcalCalendarObject) -> Self { + Self { + ics: value.generate(), + inner: value, + } + } +} From c77b59dcb0eb7e3f901b936dce0726c548d59083 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Thu, 8 Jan 2026 23:24:47 +0100 Subject: [PATCH 11/26] Remove unused code --- crates/ical/src/error.rs | 10 +----- crates/ical/src/timestamp.rs | 18 ---------- ...tests__caldav__calendar__get_body.snap.new | 14 ++++++++ ...rddav__addressbook__multiget_body.snap.new | 36 +++++++++++++++++++ 4 files changed, 51 insertions(+), 27 deletions(-) create mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new create mode 100644 src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new diff --git a/crates/ical/src/error.rs b/crates/ical/src/error.rs index 94b41fe..0054467 100644 --- a/crates/ical/src/error.rs +++ b/crates/ical/src/error.rs @@ -1,7 +1,5 @@ use axum::{http::StatusCode, response::IntoResponse}; -use crate::CalDateTimeError; - #[derive(Debug, thiserror::Error, PartialEq, Eq)] pub enum Error { #[error("Invalid ics/vcf input: {0}")] @@ -15,12 +13,6 @@ pub enum Error { #[error(transparent)] ParserError(#[from] ical::parser::ParserError), - - #[error(transparent)] - CalDateTimeError(#[from] CalDateTimeError), - - #[error(transparent)] - RRuleError(#[from] rrule::RRuleError), } impl Error { @@ -30,7 +22,7 @@ impl Error { Self::InvalidData(_) | Self::MissingCalendar | Self::MissingContact => { StatusCode::BAD_REQUEST } - _ => StatusCode::INTERNAL_SERVER_ERROR, + Self::ParserError(_) => StatusCode::INTERNAL_SERVER_ERROR, } } } diff --git a/crates/ical/src/timestamp.rs b/crates/ical/src/timestamp.rs index 1e31191..4fd5eb7 100644 --- a/crates/ical/src/timestamp.rs +++ b/crates/ical/src/timestamp.rs @@ -4,24 +4,6 @@ use rustical_xml::{ValueDeserialize, ValueSerialize}; const UTC_DATE_TIME: &str = "%Y%m%dT%H%M%SZ"; -#[derive(Debug, thiserror::Error, PartialEq, Eq)] -pub enum CalDateTimeError { - #[error( - "Timezone has X-LIC-LOCATION property to specify a timezone from the Olson database, however its value {0} is invalid" - )] - InvalidOlson(String), - #[error("TZID {0} does not refer to a valid timezone")] - InvalidTZID(String), - #[error("Timestamp doesn't exist because of gap in local time")] - LocalTimeGap, - #[error("Datetime string {0} has an invalid format")] - InvalidDatetimeFormat(String), - #[error("Could not parse datetime {0}")] - ParseError(String), - #[error("Duration string {0} has an invalid format")] - InvalidDurationFormat(String), -} - #[derive(Debug, Clone, Deref, PartialEq, Eq, Hash)] pub struct UtcDateTime(pub DateTime); diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new new file mode 100644 index 0000000..ebd639d --- /dev/null +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new @@ -0,0 +1,14 @@ +--- +source: src/integration_tests/caldav/calendar.rs +assertion_line: 145 +expression: body +--- +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:RustiCal Export +CALSCALE:GREGORIAN +X-WR-CALNAME:Calendar +X-WR-CALDESC:Description +X-WR-CALCOLOR:#00FF00 +X-WR-TIMEZONE:US/Eastern +END:VCALENDAR diff --git a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new new file mode 100644 index 0000000..c509f45 --- /dev/null +++ b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new @@ -0,0 +1,36 @@ +--- +source: src/integration_tests/carddav/addressbook.rs +assertion_line: 446 +expression: body +--- + + + + /carddav/principal/user/contacts/newcard.vcf + + + "ea0bf4a2ce7ef84606a4cf9235776dbc11b3e7ce351ddf35f27cbc0088acca7e" + BEGIN:VCARD +VERSION:3.0 +FN:Cyrus Daboo +N:Daboo;Cyrus +ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA +EMAIL;TYPE=INTERNET,PREF:cyrus@example.com +NICKNAME:me +NOTE:Example VCard. +ORG:Self Employed +TEL;TYPE=WORK,VOICE:412 605 0499 +TEL;TYPE=FAX:412 605 0705 +URL:http://www.example.com +UID:1234-5678-9000-1 +END:VCARD + + + HTTP/1.1 200 OK + + + + /home/bernard/addressbook/vcf1.vcf + HTTP/1.1 404 Not Found + + From 5f68a5ae5c38abd0f335ff09de0e7bd646038cae Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:32:50 +0100 Subject: [PATCH 12/26] Re-add get_last_occurence for sqlite store --- Cargo.lock | 10 +++++----- crates/store_sqlite/src/calendar_store.rs | 13 ++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0194e6..677f60b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,9 +573,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.51" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ "find-msvc-tools", "shlex", @@ -1241,9 +1241,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "flume" @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#a64b2f6b2920c238f0aee6dd02bafcda5ca76040" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#64c342e7258ba445dc91b47cd8e20e0ac8ffc417" dependencies = [ "chrono", "chrono-tz", diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index 6fdc61e..8cbe113 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -545,13 +545,12 @@ impl SqliteCalendarStore { .get_first_occurence() .as_ref() .map(CalDateTime::date_floor); - let last_occurence: Option = todo!(); - // let last_occurence = object - // .get_inner() - // .get_inner() - // .get_last_occurence() - // .as_ref() - // .map(CalDateTime::date_ceil); + let last_occurence = object + .get_inner() + .get_inner() + .get_last_occurence() + .as_ref() + .map(CalDateTime::date_ceil); let etag = object.get_etag(); let object_type = object.get_object_type() as u8; From c165e761be74953911c41c229ce45c700b1f30eb Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:04:11 +0100 Subject: [PATCH 13/26] update ical-rs --- Cargo.lock | 43 +++++++++---------- crates/caldav/src/calendar/methods/import.rs | 4 +- .../report/calendar_query/prop_filter.rs | 9 ++-- .../carddav/src/addressbook/methods/import.rs | 5 +-- .../report/addressbook_query/prop_filter.rs | 10 ++--- ...ion_tests__caldav__calendar__get_body.snap | 3 +- ...__caldav__calendar_import__0_get_body.snap | 2 +- ...__caldav__calendar_import__1_get_body.snap | 4 +- ...__carddav__addressbook__multiget_body.snap | 2 +- 9 files changed, 39 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 677f60b..4f6ad18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,9 +477,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "basic-toml" @@ -1422,9 +1422,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#64c342e7258ba445dc91b47cd8e20e0ac8ffc417" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#28e982f928a73f5af13f1e59e28da419567bf93f" dependencies = [ "chrono", "chrono-tz", @@ -2242,7 +2242,7 @@ checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ "base64 0.22.1", "chrono", - "getrandom 0.2.16", + "getrandom 0.2.17", "http", "rand 0.8.5", "reqwest", @@ -2521,7 +2521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a2a4764cc1f8d961d802af27193c6f4f0124bd0e76e8393cf818e18880f0524" dependencies = [ "argon2", - "getrandom 0.2.16", + "getrandom 0.2.17", "password-hash", "pbkdf2", "rand_core 0.6.4", @@ -2847,9 +2847,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" dependencies = [ "bytes", "prost-derive", @@ -2857,9 +2857,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", "itertools 0.14.0", @@ -2994,7 +2994,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] @@ -3160,7 +3160,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -3350,7 +3350,7 @@ dependencies = [ "serde", "sqlx", "tokio", - "toml 0.9.10+spec-1.1.0", + "toml 0.9.11+spec-1.1.0", "tower", "tower-http", "tower-sessions", @@ -4494,9 +4494,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" dependencies = [ "indexmap 2.13.0", "serde_core", @@ -4781,16 +4781,13 @@ dependencies = [ [[package]] name = "tracing-opentelemetry" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6e5658463dd88089aba75c7791e1d3120633b1bfde22478b28f625a9bb1b8e" +checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" dependencies = [ "js-sys", "opentelemetry", - "opentelemetry_sdk", - "rustversion", "smallvec", - "thiserror 2.0.17", "tracing", "tracing-core", "tracing-log", @@ -5562,6 +5559,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" +checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec" diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index 054b7ef..2a96efd 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -11,7 +11,7 @@ use rustical_ical::CalendarObjectType; use rustical_store::{ Calendar, CalendarMetadata, CalendarStore, SubscriptionStore, auth::Principal, }; -use std::{collections::HashMap, io::BufReader}; +use std::io::BufReader; use tracing::instrument; #[instrument(skip(resource_service))] @@ -50,7 +50,7 @@ pub async fn route_import( cal.remove_property("X-WR-CALDESC"); cal.remove_property("X-WR-CALCOLOR"); cal.remove_property("X-WR-TIMEZONE"); - let cal = cal.build(&HashMap::new()).unwrap(); + let cal = cal.build(None).unwrap(); // Make sure timezone is valid if let Some(timezone_id) = timezone_id.as_ref() { diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs b/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs index 889d0ee..fe99f03 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs @@ -3,7 +3,6 @@ use ical::{parser::Component, property::ContentLine, types::CalDateTime}; use rustical_dav::xml::TextMatchElement; use rustical_ical::UtcDateTime; use rustical_xml::XmlDeserialize; -use std::collections::HashMap; #[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq)] #[allow(dead_code)] @@ -27,7 +26,7 @@ impl PropFilterElement { pub fn match_property(&self, property: &ContentLine) -> bool { if let Some(TimeRangeElement { start, end }) = &self.time_range { // TODO: Respect timezones - let Ok(timestamp) = CalDateTime::parse_prop(property, &HashMap::default()) else { + let Ok(timestamp) = CalDateTime::parse_prop(property, None) else { return false; }; let timestamp = timestamp.utc(); @@ -62,13 +61,13 @@ impl PropFilterElement { } pub fn match_component(&self, comp: &impl Component) -> bool { - let properties = comp.get_named_properties(&self.name); + let mut properties = comp.get_named_properties(&self.name); if self.is_not_defined.is_some() { - return properties.is_empty(); + return properties.next().is_none(); } // The filter matches when one property instance matches // Example where this matters: We have multiple attendees and want to match one - properties.iter().any(|prop| self.match_property(prop)) + properties.any(|prop| self.match_property(prop)) } } diff --git a/crates/carddav/src/addressbook/methods/import.rs b/crates/carddav/src/addressbook/methods/import.rs index 82709e9..da3bcb0 100644 --- a/crates/carddav/src/addressbook/methods/import.rs +++ b/crates/carddav/src/addressbook/methods/import.rs @@ -1,5 +1,3 @@ -use std::{collections::HashMap, io::BufReader}; - use crate::Error; use crate::addressbook::AddressbookResourceService; use axum::{ @@ -12,6 +10,7 @@ use ical::{ property::ContentLine, }; use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore, auth::Principal}; +use std::io::BufReader; use tracing::instrument; #[instrument(skip(resource_service))] @@ -38,7 +37,7 @@ pub async fn route_import( value: Some(uuid::Uuid::new_v4().to_string()), params: vec![].into(), }); - card = card_mut.build(&HashMap::new()).unwrap(); + card = card_mut.build(None).unwrap(); } // TODO: Make nicer let uid = card.get_uid().unwrap(); diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs b/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs index eec0e96..b10b03c 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_query/prop_filter.rs @@ -56,22 +56,22 @@ impl PropFilterElement { } pub fn match_component(&self, comp: &impl PropFilterable) -> bool { - let properties = comp.get_named_properties(&self.name); + let mut properties = comp.get_named_properties(&self.name); if self.is_not_defined.is_some() { - return properties.is_empty(); + return properties.next().is_none(); } // The filter matches when one property instance matches - properties.iter().any(|prop| self.match_property(prop)) + properties.any(|prop| self.match_property(prop)) } } pub trait PropFilterable { - fn get_named_properties(&self, name: &str) -> Vec<&ContentLine>; + fn get_named_properties<'a>(&'a self, name: &'a str) -> impl Iterator; } impl PropFilterable for AddressObject { - fn get_named_properties(&self, name: &str) -> Vec<&ContentLine> { + fn get_named_properties<'a>(&'a self, name: &'a str) -> impl Iterator { self.get_vcard().get_named_properties(name) } } diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap index d0fc17b..b336d9c 100644 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap @@ -4,9 +4,10 @@ expression: body --- BEGIN:VCALENDAR VERSION:2.0 +PRODID:RustiCal Export CALSCALE:GREGORIAN -PRODID:RustiCal X-WR-CALNAME:Calendar X-WR-CALDESC:Description +X-WR-CALCOLOR:#00FF00 X-WR-TIMEZONE:US/Eastern END:VCALENDAR diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__0_get_body.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__0_get_body.snap index 7dc2838..c031a57 100644 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__0_get_body.snap +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__0_get_body.snap @@ -4,8 +4,8 @@ expression: body --- BEGIN:VCALENDAR VERSION:2.0 +PRODID:RustiCal Export CALSCALE:GREGORIAN -PRODID:RustiCal BEGIN:VEVENT UID:[UID] SUMMARY:One-off Meeting diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap index 8b15580..2cdafb5 100644 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap @@ -4,8 +4,8 @@ expression: body --- BEGIN:VCALENDAR VERSION:2.0 +PRODID:RustiCal Export CALSCALE:GREGORIAN -PRODID:RustiCal BEGIN:VTIMEZONE LAST-MODIFIED:20040110T032845Z TZID:US/Eastern @@ -29,7 +29,7 @@ DTSTAMP:20060206T001102Z DTSTART;TZID=US/Eastern:20060102T100000 DURATION:PT1H SUMMARY:Event #1 -Description:Go Steelers! +DESCRIPTION:Go Steelers! UID:[UID] END:VEVENT BEGIN:VEVENT diff --git a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap index 147559f..11e19dd 100644 --- a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap +++ b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap @@ -8,7 +8,7 @@ expression: body /carddav/principal/user/contacts/newcard.vcf - "24835b6c11816c864f9edadd4c7c296234c643892afcbbc5fbf5c9b7ac935cf8" + "ea0bf4a2ce7ef84606a4cf9235776dbc11b3e7ce351ddf35f27cbc0088acca7e" BEGIN:VCARD VERSION:3.0 FN:Cyrus Daboo From 7eecd957578372b29c7ee6cb91863dbad3522710 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:06:23 +0100 Subject: [PATCH 14/26] Remove calendar-query integration test for now --- .gitignore | 2 ++ src/integration_tests/caldav/mod.rs | 2 +- ...tests__caldav__calendar__get_body.snap.new | 14 -------- ...rddav__addressbook__multiget_body.snap.new | 36 ------------------- 4 files changed, 3 insertions(+), 51 deletions(-) delete mode 100644 src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new delete mode 100644 src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new diff --git a/.gitignore b/.gitignore index dbab0f4..a5b5571 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ site # Frontend **/node_modules **/.vite + +**/*.snap.new diff --git a/src/integration_tests/caldav/mod.rs b/src/integration_tests/caldav/mod.rs index 4a91cd7..3378fbe 100644 --- a/src/integration_tests/caldav/mod.rs +++ b/src/integration_tests/caldav/mod.rs @@ -9,7 +9,7 @@ use tower::ServiceExt; mod calendar; mod calendar_import; -mod calendar_report; +// mod calendar_report; #[rstest] #[tokio::test] diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new deleted file mode 100644 index ebd639d..0000000 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar__get_body.snap.new +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: src/integration_tests/caldav/calendar.rs -assertion_line: 145 -expression: body ---- -BEGIN:VCALENDAR -VERSION:2.0 -PRODID:RustiCal Export -CALSCALE:GREGORIAN -X-WR-CALNAME:Calendar -X-WR-CALDESC:Description -X-WR-CALCOLOR:#00FF00 -X-WR-TIMEZONE:US/Eastern -END:VCALENDAR diff --git a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new b/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new deleted file mode 100644 index c509f45..0000000 --- a/src/integration_tests/carddav/snapshots/rustical__integration_tests__carddav__addressbook__multiget_body.snap.new +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: src/integration_tests/carddav/addressbook.rs -assertion_line: 446 -expression: body ---- - - - - /carddav/principal/user/contacts/newcard.vcf - - - "ea0bf4a2ce7ef84606a4cf9235776dbc11b3e7ce351ddf35f27cbc0088acca7e" - BEGIN:VCARD -VERSION:3.0 -FN:Cyrus Daboo -N:Daboo;Cyrus -ADR;TYPE=POSTAL:;2822 Email HQ;Suite 2821;RFCVille;PA;15213;USA -EMAIL;TYPE=INTERNET,PREF:cyrus@example.com -NICKNAME:me -NOTE:Example VCard. -ORG:Self Employed -TEL;TYPE=WORK,VOICE:412 605 0499 -TEL;TYPE=FAX:412 605 0705 -URL:http://www.example.com -UID:1234-5678-9000-1 -END:VCARD - - - HTTP/1.1 200 OK - - - - /home/bernard/addressbook/vcf1.vcf - HTTP/1.1 404 Not Found - - From 5ec2787ecf5a6a911dee882e66f1dac85a231a82 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Tue, 13 Jan 2026 12:41:03 +0100 Subject: [PATCH 15/26] build MVP for birthday calendar --- Cargo.lock | 2 +- crates/ical/src/address_object.rs | 130 ++++++++++++++++-- .../addressbook_store/birthday_calendar.rs | 75 ++++++---- .../store_sqlite/src/addressbook_store/mod.rs | 8 +- crates/store_sqlite/src/calendar_store.rs | 15 +- 5 files changed, 172 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f6ad18..faf1b2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#28e982f928a73f5af13f1e59e28da419567bf93f" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#626982a02647c3bee5c7d0828facd1b77df5722f" dependencies = [ "chrono", "chrono-tz", diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index 52c9db8..1cac936 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -1,7 +1,20 @@ use crate::{CalendarObject, Error}; +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::str::FromStr; use std::{collections::HashMap, io::BufReader}; #[derive(Debug, Clone)] @@ -36,24 +49,115 @@ impl AddressObject { &self.vcf } + fn get_significant_date_object( + &self, + date: &PartialDate, + summary_prefix: &str, + suffix: &str, + ) -> Result, Error> { + let Some(uid) = self.vcard.get_uid() else { + return Ok(None); + }; + let uid = format!("{uid}{suffix}"); + let year = date.get_year(); + let year_suffix = year.map(|year| format!(" {year}")).unwrap_or_default(); + let Some(month) = date.get_month() else { + return Ok(None); + }; + let Some(day) = date.get_day() else { + return Ok(None); + }; + 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 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); + }; + let Some(VcardFNProperty(fullname, _)) = self.vcard.full_name.first() else { + return Ok(None); + }; + let summary = format!("{summary_prefix} {fullname}{year_suffix}"); + + let event = IcalEventBuilder { + properties: vec![ + IcalDTSTAMPProperty(Utc::now().into(), vec![].into()).into(), + IcalDTSTARTProperty(start_date.into(), vec![].into()).into(), + IcalDTENDProperty(end_date.into(), vec![].into()).into(), + IcalUIDProperty(uid, vec![].into()).into(), + IcalRRULEProperty( + rrule::RRule::from_str("FREQ=YEARLY").unwrap(), + vec![].into(), + ) + .into(), + IcalSUMMARYProperty(summary.clone(), vec![].into()).into(), + ContentLine { + name: "TRANSP".to_owned(), + value: Some("TRANSPARENT".to_owned()), + ..Default::default() + }, + ], + alarms: vec![IcalAlarmBuilder { + properties: vec![ + ContentLine { + name: "TRIGGER".to_owned(), + value: Some("-PT0M".to_owned()), + params: vec![("VALUE".to_owned(), vec!["DURATION".to_owned()])].into(), + }, + ContentLine { + name: "ACTION".to_owned(), + value: Some("DISPLAY".to_owned()), + ..Default::default() + }, + ContentLine { + name: "DESCRIPTION".to_owned(), + value: Some(summary), + ..Default::default() + }, + ], + }], + }; + + Ok(Some( + IcalCalendarObjectBuilder { + properties: vec![ + IcalVERSIONProperty(IcalVersion::Version2_0, vec![].into()).into(), + IcalCALSCALEProperty(Calscale::Gregorian, vec![].into()).into(), + IcalPRODIDProperty( + "-//github.com/lennart-k/rustical birthday calendar//EN".to_owned(), + vec![].into(), + ) + .into(), + ], + inner: Some(CalendarInnerDataBuilder::Event(vec![event])), + vtimezones: HashMap::default(), + } + .build(None)? + .into(), + )) + } + pub fn get_anniversary_object(&self) -> Result, Error> { - todo!(); + let Some(VcardANNIVERSARYProperty(anniversary, _)) = &self.vcard.anniversary else { + return Ok(None); + }; + let Some(date) = &anniversary.date else { + return Ok(None); + }; + + self.get_significant_date_object(date, "💍", "-anniversary") } pub fn get_birthday_object(&self) -> Result, Error> { - todo!(); - } + let Some(VcardBDAYProperty(bday, _)) = &self.vcard.birthday else { + return Ok(None); + }; + let Some(date) = &bday.date else { + return Ok(None); + }; - /// Get significant dates associated with this address object - pub fn get_significant_dates(&self) -> Result, Error> { - let mut out = HashMap::new(); - if let Some(birthday) = self.get_birthday_object()? { - out.insert("birthday", birthday); - } - if let Some(anniversary) = self.get_anniversary_object()? { - out.insert("anniversary", anniversary); - } - Ok(out) + self.get_significant_date_object(date, "🎂", "-birthday") } #[must_use] diff --git a/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs b/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs index 8f49cf3..2161d2c 100644 --- a/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs +++ b/crates/store_sqlite/src/addressbook_store/birthday_calendar.rs @@ -279,7 +279,7 @@ impl CalendarStore for SqliteAddressbookStore { .strip_prefix(BIRTHDAYS_PREFIX) .ok_or(Error::NotFound)? .to_string(); - Self::_update_birthday_calendar(&self.db, &principal, &calendar).await + Self::_update_birthday_calendar(&self.db, principal, &calendar).await } #[instrument] @@ -330,14 +330,29 @@ impl CalendarStore for SqliteAddressbookStore { .ok_or(Error::NotFound)?; let (objects, deleted_objects, new_synctoken) = AddressbookStore::sync_changes(self, principal, cal_id, synctoken).await?; - todo!(); - // let objects: Result>, rustical_ical::Error> = objects - // .iter() - // .map(AddressObject::get_birthday_object) - // .collect(); - // let objects = objects?.into_iter().flatten().collect(); - // - // Ok((objects, deleted_objects, new_synctoken)) + + let mut out_objects = vec![]; + + for (object_id, object) in objects { + if let Some(birthday) = object.get_birthday_object()? { + out_objects.push((format!("{object_id}-birthday"), birthday)); + } + if let Some(anniversary) = object.get_anniversary_object()? { + out_objects.push((format!("{object_id}-anniversayr"), anniversary)); + } + } + + let deleted_objects = deleted_objects + .into_iter() + .flat_map(|object_id| { + [ + format!("{object_id}-birthday"), + format!("{object_id}-anniversary"), + ] + }) + .collect(); + + Ok((out_objects, deleted_objects, new_synctoken)) } #[instrument] @@ -358,22 +373,19 @@ impl CalendarStore for SqliteAddressbookStore { principal: &str, cal_id: &str, ) -> Result, Error> { - todo!() - // let cal_id = cal_id - // .strip_prefix(BIRTHDAYS_PREFIX) - // .ok_or(Error::NotFound)?; - // let objects: Result>, rustical_ical::Error> = - // AddressbookStore::get_objects(self, principal, cal_id) - // .await? - // .iter() - // .map(AddressObject::get_significant_dates) - // .collect(); - // let objects = objects? - // .into_iter() - // .flat_map(HashMap::into_values) - // .collect(); - // - // Ok(objects) + let mut objects = vec![]; + let cal_id = cal_id + .strip_prefix(BIRTHDAYS_PREFIX) + .ok_or(Error::NotFound)?; + for (object_id, object) in AddressbookStore::get_objects(self, principal, cal_id).await? { + if let Some(birthday) = object.get_birthday_object()? { + objects.push((format!("{object_id}-birthday"), birthday)); + } + if let Some(anniversary) = object.get_anniversary_object()? { + objects.push((format!("{object_id}-anniversayr"), anniversary)); + } + } + Ok(objects) } #[instrument] @@ -388,11 +400,14 @@ impl CalendarStore for SqliteAddressbookStore { .strip_prefix(BIRTHDAYS_PREFIX) .ok_or(Error::NotFound)?; let (addressobject_id, date_type) = object_id.rsplit_once('-').ok_or(Error::NotFound)?; - AddressbookStore::get_object(self, principal, cal_id, addressobject_id, show_deleted) - .await? - .get_significant_dates()? - .remove(date_type) - .ok_or(Error::NotFound) + let obj = + AddressbookStore::get_object(self, principal, cal_id, addressobject_id, show_deleted) + .await?; + match date_type { + "birthday" => Ok(obj.get_birthday_object()?.ok_or(Error::NotFound)?), + "anniversary" => Ok(obj.get_anniversary_object()?.ok_or(Error::NotFound)?), + _ => Err(Error::NotFound), + } } #[instrument] diff --git a/crates/store_sqlite/src/addressbook_store/mod.rs b/crates/store_sqlite/src/addressbook_store/mod.rs index 196c241..4d85ff8 100644 --- a/crates/store_sqlite/src/addressbook_store/mod.rs +++ b/crates/store_sqlite/src/addressbook_store/mod.rs @@ -509,7 +509,7 @@ impl AddressbookStore for SqliteAddressbookStore { ) -> Result<(), rustical_store::Error> { assert_eq!(principal, &addressbook.principal); assert_eq!(id, &addressbook.id); - Self::_update_addressbook(&self.db, &principal, &id, &addressbook).await + Self::_update_addressbook(&self.db, principal, id, &addressbook).await } #[instrument] @@ -648,9 +648,9 @@ impl AddressbookStore for SqliteAddressbookStore { let sync_token = Self::log_object_operation( &mut tx, - &principal, - &addressbook_id, - &object_id, + principal, + addressbook_id, + object_id, ChangeOperation::Add, ) .await diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index 8cbe113..36428e3 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -880,7 +880,7 @@ impl CalendarStore for SqliteCalendarStore { .await .map_err(crate::Error::from)?; - let calendar = Self::_get_calendar(&mut *tx, &principal, &cal_id, true).await?; + let calendar = Self::_get_calendar(&mut *tx, principal, cal_id, true).await?; if calendar.subscription_url.is_some() { // We cannot commit an object to a subscription calendar return Err(Error::ReadOnly); @@ -891,17 +891,14 @@ impl CalendarStore for SqliteCalendarStore { sync_token = Some( Self::log_object_operation( &mut tx, - &principal, - &cal_id, + principal, + cal_id, &object_id, ChangeOperation::Add, ) .await?, ); - Self::_put_object( - &mut *tx, &principal, &cal_id, &object_id, &object, overwrite, - ) - .await?; + Self::_put_object(&mut *tx, principal, cal_id, &object_id, &object, overwrite).await?; } tx.commit().await.map_err(crate::Error::from)?; @@ -909,9 +906,7 @@ impl CalendarStore for SqliteCalendarStore { if let Some(sync_token) = sync_token { self.send_push_notification( CollectionOperationInfo::Content { sync_token }, - self.get_calendar(&principal, &cal_id, true) - .await? - .push_topic, + self.get_calendar(principal, cal_id, true).await?.push_topic, ); } Ok(()) From 2c678903433a9090147baa53b4bd6213ab98facf Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Tue, 13 Jan 2026 16:01:59 +0100 Subject: [PATCH 16/26] Update ical-rs --- Cargo.lock | 36 +++++++++---------- crates/caldav/src/calendar/methods/import.rs | 3 +- .../caldav/src/calendar/methods/mkcalendar.rs | 2 +- crates/caldav/src/calendar/resource.rs | 2 +- .../carddav/src/addressbook/methods/import.rs | 3 +- crates/ical/src/address_object.rs | 4 +-- crates/ical/src/calendar_object.rs | 4 +-- 7 files changed, 26 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index faf1b2e..163449d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -689,9 +689,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "colorchoice" @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#626982a02647c3bee5c7d0828facd1b77df5722f" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#ece5b95ddc20f89d14e162aba3a49038f9989701" dependencies = [ "chrono", "chrono-tz", @@ -2965,7 +2965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2985,7 +2985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2999,9 +2999,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -4353,30 +4353,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" dependencies = [ "num-conv", "time-core", @@ -4611,9 +4611,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -5559,6 +5559,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index 2a96efd..94588c3 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -11,7 +11,6 @@ use rustical_ical::CalendarObjectType; use rustical_store::{ Calendar, CalendarMetadata, CalendarStore, SubscriptionStore, auth::Principal, }; -use std::io::BufReader; use tracing::instrument; #[instrument(skip(resource_service))] @@ -26,7 +25,7 @@ pub async fn route_import( return Err(Error::Unauthorized); } - let parser = ical::IcalParser::new(BufReader::new(body.as_bytes())); + let parser = ical::IcalParser::from_slice(body.as_bytes()); let mut cal = parser .expect_one() .map_err(rustical_ical::Error::ParserError)? diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs index f85ca76..d065231 100644 --- a/crates/caldav/src/calendar/methods/mkcalendar.rs +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -87,7 +87,7 @@ pub async fn route_mkcalendar( Some(tzid) } else if let Some(tz) = request.calendar_timezone { // TODO: Proper error (calendar-timezone precondition) - let calendar = IcalParser::new(tz.as_bytes()) + let calendar = IcalParser::from_slice(tz.as_bytes()) .next() .ok_or_else(|| rustical_dav::Error::BadRequest("No timezone data provided".to_owned()))? .map_err(|_| rustical_dav::Error::BadRequest("Error parsing timezone".to_owned()))?; diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index f80a4d4..5c8c54a 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -202,7 +202,7 @@ impl Resource for CalendarResource { CalendarProp::CalendarTimezone(timezone) => { if let Some(tz) = timezone { // TODO: Proper error (calendar-timezone precondition) - let calendar = IcalParser::new(tz.as_bytes()) + let calendar = IcalParser::from_slice(tz.as_bytes()) .next() .ok_or_else(|| { rustical_dav::Error::BadRequest( diff --git a/crates/carddav/src/addressbook/methods/import.rs b/crates/carddav/src/addressbook/methods/import.rs index da3bcb0..cb7bff2 100644 --- a/crates/carddav/src/addressbook/methods/import.rs +++ b/crates/carddav/src/addressbook/methods/import.rs @@ -10,7 +10,6 @@ use ical::{ property::ContentLine, }; use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore, auth::Principal}; -use std::io::BufReader; use tracing::instrument; #[instrument(skip(resource_service))] @@ -24,7 +23,7 @@ pub async fn route_import( return Err(Error::Unauthorized); } - let parser = vcard::VcardParser::new(BufReader::new(body.as_bytes())); + let parser = vcard::VcardParser::from_slice(body.as_bytes()); let mut objects = vec![]; for res in parser { diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index 1cac936..ad1e943 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -14,8 +14,8 @@ use ical::parser::{ use ical::property::ContentLine; use ical::types::{CalDate, PartialDate}; use sha2::{Digest, Sha256}; +use std::collections::HashMap; use std::str::FromStr; -use std::{collections::HashMap, io::BufReader}; #[derive(Debug, Clone)] pub struct AddressObject { @@ -32,7 +32,7 @@ impl From for AddressObject { impl AddressObject { pub fn from_vcf(vcf: String) -> Result { - let parser = vcard::VcardParser::new(BufReader::new(vcf.as_bytes())); + let parser = vcard::VcardParser::from_slice(vcf.as_bytes()); let vcard = parser.expect_one()?; Ok(Self { vcf, vcard }) } diff --git a/crates/ical/src/calendar_object.rs b/crates/ical/src/calendar_object.rs index dcecf56..e0114f7 100644 --- a/crates/ical/src/calendar_object.rs +++ b/crates/ical/src/calendar_object.rs @@ -1,9 +1,9 @@ use crate::Error; use derive_more::Display; +use ical::IcalObjectParser; use ical::component::CalendarInnerData; use ical::component::IcalCalendarObject; use ical::generator::Emitter; -use ical::parser::ComponentParser; use serde::Deserialize; use serde::Serialize; use sha2::{Digest, Sha256}; @@ -69,7 +69,7 @@ pub struct CalendarObject { impl CalendarObject { pub fn from_ics(ics: String) -> Result { - let parser: ComponentParser<_, IcalCalendarObject> = ComponentParser::new(ics.as_bytes()); + let parser = IcalObjectParser::from_slice(ics.as_bytes()); let inner = parser.expect_one()?; Ok(Self { inner, ics }) From 63373ad525dc418fac639725b52648aae904f511 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Fri, 16 Jan 2026 14:16:22 +0100 Subject: [PATCH 17/26] simplify handling of ical-related errors --- crates/caldav/src/calendar/methods/import.rs | 12 ++----- crates/caldav/src/error.rs | 3 +- crates/carddav/src/error.rs | 3 +- crates/ical/src/error.rs | 34 -------------------- crates/ical/src/lib.rs | 6 ++-- crates/store/src/error.rs | 5 +-- crates/store_sqlite/src/calendar_store.rs | 21 ++++++------ 7 files changed, 22 insertions(+), 62 deletions(-) delete mode 100644 crates/ical/src/error.rs diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index 94588c3..fd561e8 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -26,10 +26,7 @@ pub async fn route_import( } let parser = ical::IcalParser::from_slice(body.as_bytes()); - let mut cal = parser - .expect_one() - .map_err(rustical_ical::Error::ParserError)? - .mutable(); + let mut cal = parser.expect_one()?.mutable(); // Extract calendar metadata let displayname = cal @@ -70,12 +67,7 @@ pub async fn route_import( cal_components.push(CalendarObjectType::Todo); } - let objects = cal - .into_objects() - .map_err(rustical_ical::Error::ParserError)? - .into_iter() - .map(Into::into) - .collect(); + let objects = cal.into_objects()?.into_iter().map(Into::into).collect(); let new_cal = Calendar { principal, id: cal_id, diff --git a/crates/caldav/src/error.rs b/crates/caldav/src/error.rs index 833db6c..49161d7 100644 --- a/crates/caldav/src/error.rs +++ b/crates/caldav/src/error.rs @@ -75,7 +75,8 @@ impl Error { Self::XmlDecodeError(_) => StatusCode::BAD_REQUEST, Self::ChronoParseError(_) | Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, - Self::IcalError(err) => err.status_code(), + // TODO: Can also be Bad Request, if it's used input + Self::IcalError(_err) => StatusCode::INTERNAL_SERVER_ERROR, Self::PreconditionFailed(_err) => StatusCode::PRECONDITION_FAILED, } } diff --git a/crates/carddav/src/error.rs b/crates/carddav/src/error.rs index 66409b5..dbdff71 100644 --- a/crates/carddav/src/error.rs +++ b/crates/carddav/src/error.rs @@ -43,7 +43,8 @@ impl Error { Self::XmlDecodeError(_) => StatusCode::BAD_REQUEST, Self::ChronoParseError(_) | Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, - Self::IcalError(err) => err.status_code(), + // TODO: Can also be Bad Request, if it's used input + Self::IcalError(_err) => StatusCode::INTERNAL_SERVER_ERROR, } } } diff --git a/crates/ical/src/error.rs b/crates/ical/src/error.rs deleted file mode 100644 index 0054467..0000000 --- a/crates/ical/src/error.rs +++ /dev/null @@ -1,34 +0,0 @@ -use axum::{http::StatusCode, response::IntoResponse}; - -#[derive(Debug, thiserror::Error, PartialEq, Eq)] -pub enum Error { - #[error("Invalid ics/vcf input: {0}")] - InvalidData(String), - - #[error("Missing calendar")] - MissingCalendar, - - #[error("Missing contact")] - MissingContact, - - #[error(transparent)] - ParserError(#[from] ical::parser::ParserError), -} - -impl Error { - #[must_use] - pub const fn status_code(&self) -> StatusCode { - match self { - Self::InvalidData(_) | Self::MissingCalendar | Self::MissingContact => { - StatusCode::BAD_REQUEST - } - Self::ParserError(_) => StatusCode::INTERNAL_SERVER_ERROR, - } - } -} - -impl IntoResponse for Error { - fn into_response(self) -> axum::response::Response { - (self.status_code(), self.to_string()).into_response() - } -} diff --git a/crates/ical/src/lib.rs b/crates/ical/src/lib.rs index 7e1aa43..68782f3 100644 --- a/crates/ical/src/lib.rs +++ b/crates/ical/src/lib.rs @@ -1,13 +1,13 @@ #![warn(clippy::all, clippy::pedantic, clippy::nursery)] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)] mod timestamp; +use ical::parser::ParserError; pub use timestamp::*; mod calendar_object; pub use calendar_object::*; -mod error; -pub use error::Error; - mod address_object; pub use address_object::AddressObject; + +pub type Error = ParserError; diff --git a/crates/store/src/error.rs b/crates/store/src/error.rs index 9ace70f..259a9fa 100644 --- a/crates/store/src/error.rs +++ b/crates/store/src/error.rs @@ -26,7 +26,7 @@ pub enum Error { Other(#[from] anyhow::Error), #[error(transparent)] - IcalError(#[from] rustical_ical::Error), + IcalError(#[from] ical::parser::ParserError), } impl Error { @@ -36,7 +36,8 @@ impl Error { Self::NotFound => StatusCode::NOT_FOUND, Self::AlreadyExists => StatusCode::CONFLICT, Self::ReadOnly => StatusCode::FORBIDDEN, - Self::IcalError(err) => err.status_code(), + // TODO: Can also be Bad Request, depending on when this is raised + Self::IcalError(_err) => StatusCode::INTERNAL_SERVER_ERROR, Self::InvalidPrincipalType(_) => StatusCode::BAD_REQUEST, _ => StatusCode::INTERNAL_SERVER_ERROR, } diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index 36428e3..b06b085 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -25,18 +25,17 @@ struct CalendarObjectRow { impl TryFrom for (String, CalendarObject) { type Error = rustical_store::Error; - fn try_from(value: CalendarObjectRow) -> Result { - let object = CalendarObject::from_ics(value.ics)?; - if object.get_uid() != value.uid { - return Err(rustical_store::Error::IcalError( - rustical_ical::Error::InvalidData(format!( - "uid={} and UID={} don't match", - value.uid, - object.get_uid() - )), - )); + fn try_from(row: CalendarObjectRow) -> Result { + let object = CalendarObject::from_ics(row.ics)?; + if object.get_uid() != row.uid { + warn!( + "Calendar object {}.ics: UID={} and row uid={} do not match", + row.id, + object.get_uid(), + row.uid + ); } - Ok((value.id, object)) + Ok((row.id, object)) } } From 967d18de95038c5145473ba78caf28f48a49bd8d Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Fri, 16 Jan 2026 14:45:34 +0100 Subject: [PATCH 18/26] Fix comp-filter --- Cargo.lock | 69 +++++----- .../report/calendar_query/comp_filter.rs | 124 +++++++++++++++++- .../report/calendar_query/prop_filter.rs | 8 +- 3 files changed, 158 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 163449d..6e15871 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -595,9 +595,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ "iana-time-zone", "js-sys", @@ -1770,8 +1770,8 @@ dependencies = [ [[package]] name = "ical" -version = "0.11.0" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#ece5b95ddc20f89d14e162aba3a49038f9989701" +version = "0.12.0-dev" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#5e61c25646c3785448d349e7d18b2833fc483c53" dependencies = [ "chrono", "chrono-tz", @@ -1923,9 +1923,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" [[package]] name = "insta" -version = "1.46.0" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b66886d14d18d420ab5052cbff544fc5d34d0b2cdd35eb5976aaa10a4a472e5" +checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" dependencies = [ "console", "once_cell", @@ -1991,9 +1991,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -3262,9 +3262,9 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.9.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -3273,9 +3273,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.9.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" dependencies = [ "proc-macro2", "quote", @@ -3286,9 +3286,9 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "8.9.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" dependencies = [ "sha2", "walkdir", @@ -3296,9 +3296,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" @@ -3653,9 +3653,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "4910321ebe4151be888e35fe062169554e74aad01beafed60410131420ceffbc" dependencies = [ "web-time", "zeroize", @@ -4984,9 +4984,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] @@ -4999,9 +4999,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -5012,11 +5012,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -5025,9 +5026,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5035,9 +5036,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -5048,18 +5049,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -5426,9 +5427,9 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs index 517e486..57d08a6 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/comp_filter.rs @@ -1,8 +1,9 @@ use crate::calendar::methods::report::calendar_query::{ - TimeRangeElement, prop_filter::PropFilterElement, + TimeRangeElement, + prop_filter::{PropFilterElement, PropFilterable}, }; use ical::{ - component::IcalCalendarObject, + component::{CalendarInnerData, IcalAlarm, IcalCalendarObject, IcalEvent, IcalTodo}, parser::{Component, ical::component::IcalTimeZone}, }; use rustical_xml::XmlDeserialize; @@ -25,7 +26,9 @@ pub struct CompFilterElement { pub(crate) name: String, } -pub trait CompFilterable: Component + Sized { +pub trait CompFilterable: PropFilterable + Sized { + fn get_comp_name(&self) -> &'static str; + fn match_time_range(&self, time_range: &TimeRangeElement) -> bool; fn match_subcomponents(&self, comp_filter: &CompFilterElement) -> bool; @@ -67,7 +70,100 @@ pub trait CompFilterable: Component + Sized { } } +impl CompFilterable for CalendarInnerData { + fn get_comp_name(&self) -> &'static str { + match self { + Self::Event(main, _) => main.get_comp_name(), + Self::Journal(main, _) => main.get_comp_name(), + Self::Todo(main, _) => main.get_comp_name(), + } + } + + fn match_time_range(&self, time_range: &TimeRangeElement) -> bool { + if let Some(start) = &time_range.start + && let Some(last_end) = self.get_last_occurence() + && start.to_utc() > last_end.utc() + { + return false; + } + if let Some(end) = &time_range.end + && let Some(first_start) = self.get_first_occurence() + && end.to_utc() < first_start.utc() + { + return false; + } + true + } + + fn match_subcomponents(&self, comp_filter: &CompFilterElement) -> bool { + match self { + Self::Event(main, overrides) => std::iter::once(main) + .chain(overrides.iter()) + .flat_map(IcalEvent::get_alarms) + .any(|alarm| alarm.matches(comp_filter)), + Self::Todo(main, overrides) => std::iter::once(main) + .chain(overrides.iter()) + .flat_map(IcalTodo::get_alarms) + .any(|alarm| alarm.matches(comp_filter)), + // VJOURNAL has no subcomponents + Self::Journal(_, _) => comp_filter.is_not_defined.is_some(), + } + } +} + +impl PropFilterable for IcalAlarm { + fn get_named_properties<'a>( + &'a self, + name: &'a str, + ) -> impl Iterator { + Component::get_named_properties(self, name) + } +} + +impl CompFilterable for IcalAlarm { + fn get_comp_name(&self) -> &'static str { + Component::get_comp_name(self) + } + + fn match_time_range(&self, _time_range: &TimeRangeElement) -> bool { + true + } + + fn match_subcomponents(&self, comp_filter: &CompFilterElement) -> bool { + comp_filter.is_not_defined.is_some() + } +} + +impl PropFilterable for CalendarInnerData { + #[allow(refining_impl_trait)] + fn get_named_properties<'a>( + &'a self, + name: &'a str, + ) -> Box + 'a> { + // TODO: If we were pedantic, we would have to do recurrence expansion first + // and take into account the overrides :( + match self { + Self::Event(main, _) => Box::new(main.get_named_properties(name)), + Self::Todo(main, _) => Box::new(main.get_named_properties(name)), + Self::Journal(main, _) => Box::new(main.get_named_properties(name)), + } + } +} + +impl PropFilterable for IcalCalendarObject { + fn get_named_properties<'a>( + &'a self, + name: &'a str, + ) -> impl Iterator { + Component::get_named_properties(self, name) + } +} + impl CompFilterable for IcalCalendarObject { + fn get_comp_name(&self) -> &'static str { + Component::get_comp_name(self) + } + fn match_time_range(&self, _time_range: &TimeRangeElement) -> bool { // VCALENDAR has no concept of time range false @@ -78,23 +174,36 @@ impl CompFilterable for IcalCalendarObject { .get_vtimezones() .values() .map(|tz| tz.matches(comp_filter)) - .chain([self.matches(comp_filter)]); + .chain([self.get_inner().matches(comp_filter)]); if comp_filter.is_not_defined.is_some() { - matches.all(|x| x) + matches.all(|x| !x) } else { matches.any(|x| x) } } } +impl PropFilterable for IcalTimeZone { + fn get_named_properties<'a>( + &'a self, + name: &'a str, + ) -> impl Iterator { + Component::get_named_properties(self, name) + } +} + impl CompFilterable for IcalTimeZone { + fn get_comp_name(&self) -> &'static str { + Component::get_comp_name(self) + } fn match_time_range(&self, _time_range: &TimeRangeElement) -> bool { false } - fn match_subcomponents(&self, _comp_filter: &CompFilterElement) -> bool { - true + fn match_subcomponents(&self, comp_filter: &CompFilterElement) -> bool { + // VTIMEZONE has no subcomponents + comp_filter.is_not_defined.is_some() } } @@ -111,6 +220,7 @@ mod tests { const ICS: &str = r"BEGIN:VCALENDAR CALSCALE:GREGORIAN VERSION:2.0 +PRODID:me BEGIN:VTIMEZONE TZID:Europe/Berlin X-LIC-LOCATION:Europe/Berlin diff --git a/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs b/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs index fe99f03..cb7a7f0 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query/prop_filter.rs @@ -1,5 +1,5 @@ use super::{ParamFilterElement, TimeRangeElement}; -use ical::{parser::Component, property::ContentLine, types::CalDateTime}; +use ical::{property::ContentLine, types::CalDateTime}; use rustical_dav::xml::TextMatchElement; use rustical_ical::UtcDateTime; use rustical_xml::XmlDeserialize; @@ -21,6 +21,10 @@ pub struct PropFilterElement { pub(crate) name: String, } +pub trait PropFilterable { + fn get_named_properties<'a>(&'a self, name: &'a str) -> impl Iterator; +} + impl PropFilterElement { #[must_use] pub fn match_property(&self, property: &ContentLine) -> bool { @@ -60,7 +64,7 @@ impl PropFilterElement { true } - pub fn match_component(&self, comp: &impl Component) -> bool { + pub fn match_component(&self, comp: &impl PropFilterable) -> bool { let mut properties = comp.get_named_properties(&self.name); if self.is_not_defined.is_some() { return properties.next().is_none(); From 669d81aea07b5f676035766b4953fd7eecce8bb2 Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Fri, 16 Jan 2026 14:49:19 +0100 Subject: [PATCH 19/26] address_object resource: Implement displayname --- crates/carddav/src/address_object/resource.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/carddav/src/address_object/resource.rs b/crates/carddav/src/address_object/resource.rs index 75a5ac7..56f5b89 100644 --- a/crates/carddav/src/address_object/resource.rs +++ b/crates/carddav/src/address_object/resource.rs @@ -8,6 +8,7 @@ use crate::{ }, }; use derive_more::derive::{From, Into}; +use ical::parser::VcardFNProperty; use rustical_dav::{ extensions::CommonPropertiesExtension, privileges::UserPrivilegeSet, @@ -70,8 +71,11 @@ impl Resource for AddressObjectResource { } fn get_displayname(&self) -> Option<&str> { - todo!() - // self.object.get_full_name() + self.object + .get_vcard() + .full_name + .first() + .map(|VcardFNProperty(name, _)| name.as_str()) } fn get_owner(&self) -> Option<&str> { From 7c15976a1a58d4df344502820a83b9030943075e Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:41:39 +0100 Subject: [PATCH 20/26] rebase main --- Cargo.lock | 26 ++++++++++++++------------ Cargo.toml | 1 + src/migration_0_12.rs | 17 ++++++----------- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e15871..2833f16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3317,7 +3317,7 @@ dependencies = [ [[package]] name = "rustical" -version = "0.11.10" +version = "0.11.17" dependencies = [ "anyhow", "argon2", @@ -3328,6 +3328,7 @@ dependencies = [ "figment", "headers", "http", + "ical", "insta", "opentelemetry", "opentelemetry-otlp", @@ -3362,7 +3363,7 @@ dependencies = [ [[package]] name = "rustical_caldav" -version = "0.11.10" +version = "0.11.17" dependencies = [ "async-std", "async-trait", @@ -3404,7 +3405,7 @@ dependencies = [ [[package]] name = "rustical_carddav" -version = "0.11.10" +version = "0.11.17" dependencies = [ "async-trait", "axum", @@ -3438,7 +3439,7 @@ dependencies = [ [[package]] name = "rustical_dav" -version = "0.11.10" +version = "0.11.17" dependencies = [ "async-trait", "axum", @@ -3464,7 +3465,7 @@ dependencies = [ [[package]] name = "rustical_dav_push" -version = "0.11.10" +version = "0.11.17" dependencies = [ "async-trait", "axum", @@ -3489,7 +3490,7 @@ dependencies = [ [[package]] name = "rustical_frontend" -version = "0.11.10" +version = "0.11.17" dependencies = [ "askama", "askama_web", @@ -3525,7 +3526,7 @@ dependencies = [ [[package]] name = "rustical_ical" -version = "0.11.10" +version = "0.11.17" dependencies = [ "axum", "chrono", @@ -3544,7 +3545,7 @@ dependencies = [ [[package]] name = "rustical_oidc" -version = "0.11.10" +version = "0.11.17" dependencies = [ "async-trait", "axum", @@ -3560,7 +3561,7 @@ dependencies = [ [[package]] name = "rustical_store" -version = "0.11.10" +version = "0.11.17" dependencies = [ "anyhow", "async-trait", @@ -3593,7 +3594,7 @@ dependencies = [ [[package]] name = "rustical_store_sqlite" -version = "0.11.10" +version = "0.11.17" dependencies = [ "async-trait", "chrono", @@ -3603,6 +3604,7 @@ dependencies = [ "password-auth", "password-hash", "pbkdf2", + "regex", "rstest", "rustical_ical", "rustical_store", @@ -3617,7 +3619,7 @@ dependencies = [ [[package]] name = "rustical_xml" -version = "0.11.10" +version = "0.11.17" dependencies = [ "quick-xml", "thiserror 2.0.17", @@ -5439,7 +5441,7 @@ checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "xml_derive" -version = "0.11.10" +version = "0.11.17" dependencies = [ "darling 0.23.0", "heck", diff --git a/Cargo.toml b/Cargo.toml index ead46ae..c3a9c9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,6 +160,7 @@ rustical_store_sqlite.workspace = true rustical_caldav.workspace = true rustical_carddav.workspace = true rustical_frontend.workspace = true +ical.workspace = true toml.workspace = true serde.workspace = true tokio.workspace = true diff --git a/src/migration_0_12.rs b/src/migration_0_12.rs index 2130220..f661f8a 100644 --- a/src/migration_0_12.rs +++ b/src/migration_0_12.rs @@ -1,3 +1,4 @@ +use ical::parser::{ical::IcalObjectParser, vcard::VcardParser}; use rustical_store::{AddressbookStore, CalendarStore, auth::AuthenticationProvider}; use tracing::{error, info}; @@ -8,21 +9,18 @@ pub async fn validate_calendar_objects_0_12( 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 + for (object_id, object) in cal_store .get_objects(&calendar.principal, &calendar.id) .await? { - if let Err(err) = ical_dev::parser::ical::IcalObjectParser::from_slice( - object.get_ics().as_bytes(), - ) - .expect_one() + if let Err(err) = + IcalObjectParser::from_slice(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()); } @@ -48,20 +46,17 @@ pub async fn validate_address_objects_0_12( 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 + for (object_id, object) in addr_store .get_objects(&addressbook.principal, &addressbook.id) .await? { - if let Err(err) = - ical_dev::parser::vcard::VcardParser::from_slice(object.get_vcf().as_bytes()) - .expect_one() + if let Err(err) = VcardParser::from_slice(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()); } From f73658b32f78501e11e86407c65cf13dfc350a42 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:09:34 +0100 Subject: [PATCH 21/26] Re-enable calendar-query test and fix calendar expansion --- Cargo.lock | 79 +++++----- Cargo.toml | 1 + .../caldav/calendar_report.rs | 137 ++++++++++-------- src/integration_tests/caldav/mod.rs | 2 +- .../caldav/resources/rfc4791_appb.ics | 1 + ...__caldav__calendar_import__1_get_body.snap | 1 + ...aldav__calendar_report__0_report_body.snap | 3 +- ...aldav__calendar_report__1_report_body.snap | 1 + ...aldav__calendar_report__2_report_body.snap | 11 +- 9 files changed, 128 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83a59e7..b4a8517 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,9 +573,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.52" +version = "1.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" dependencies = [ "find-msvc-tools", "shlex", @@ -1241,9 +1241,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "flume" @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.12.0-dev" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#5e61c25646c3785448d349e7d18b2833fc483c53" +source = "git+https://github.com/lennart-k/ical-rs?branch=dev#8697656303f182ce173efdaf6aa7e842ffdb3f33" dependencies = [ "chrono", "chrono-tz", @@ -1781,7 +1781,7 @@ dependencies = [ "phf 0.13.1", "regex", "rrule", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2118,7 +2118,7 @@ dependencies = [ "matchit 0.9.1", "percent-encoding", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -2370,7 +2370,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] @@ -2400,7 +2400,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "reqwest", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tonic", "tracing", @@ -2437,7 +2437,7 @@ dependencies = [ "opentelemetry", "percent-encoding", "rand 0.9.2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", ] @@ -2891,7 +2891,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -2912,7 +2912,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -3187,7 +3187,7 @@ dependencies = [ "chrono-tz", "log", "regex", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3349,6 +3349,7 @@ dependencies = [ "rustical_store", "rustical_store_sqlite", "serde", + "similar-asserts", "sqlx", "tokio", "toml 0.9.11+spec-1.1.0", @@ -3393,7 +3394,7 @@ dependencies = [ "similar-asserts", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tower-http", @@ -3428,7 +3429,7 @@ dependencies = [ "serde", "strum", "strum_macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tower-http", @@ -3457,7 +3458,7 @@ dependencies = [ "rustical_xml", "serde", "strum", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tracing", @@ -3483,7 +3484,7 @@ dependencies = [ "rustical_store", "rustical_xml", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -3513,7 +3514,7 @@ dependencies = [ "rustical_store", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tower-http", @@ -3540,7 +3541,7 @@ dependencies = [ "serde", "sha2", "similar-asserts", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -3554,7 +3555,7 @@ dependencies = [ "openidconnect", "reqwest", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "tower-sessions", "tracing", ] @@ -3584,7 +3585,7 @@ dependencies = [ "rustical_xml", "serde", "sha2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tower", "tower-sessions", @@ -3611,7 +3612,7 @@ dependencies = [ "serde", "sha2", "sqlx", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "uuid", @@ -3622,7 +3623,7 @@ name = "rustical_xml" version = "0.11.17" dependencies = [ "quick-xml", - "thiserror 2.0.17", + "thiserror 2.0.18", "xml_derive", ] @@ -3655,9 +3656,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.3" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4910321ebe4151be888e35fe062169554e74aad01beafed60410131420ceffbc" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -3665,9 +3666,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "ring", "rustls-pki-types", @@ -4047,7 +4048,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tokio-stream", "tracing", @@ -4131,7 +4132,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "uuid", "whoami", @@ -4170,7 +4171,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "uuid", "whoami", @@ -4196,7 +4197,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "url", "uuid", @@ -4315,11 +4316,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -4335,9 +4336,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -4719,7 +4720,7 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "tokio", "tracing", @@ -5562,6 +5563,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" +checksum = "94f63c051f4fe3c1509da62131a678643c5b6fbdc9273b2b79d4378ebda003d2" diff --git a/Cargo.toml b/Cargo.toml index 488c0c1..c8ba8f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -153,6 +153,7 @@ criterion = { version = "0.8", features = ["async_tokio"] } rstest.workspace = true rustical_store_sqlite = { workspace = true, features = ["test"] } insta.workspace = true +similar-asserts.workspace = true [dependencies] rustical_store.workspace = true diff --git a/src/integration_tests/caldav/calendar_report.rs b/src/integration_tests/caldav/calendar_report.rs index 879e6f3..02974ff 100644 --- a/src/integration_tests/caldav/calendar_report.rs +++ b/src/integration_tests/caldav/calendar_report.rs @@ -87,70 +87,79 @@ const REPORT_7_8_3: &str = r#" "#; -const OUTPUT_7_8_3: &str = r#" - - http://cal.example.com/bernard/work/abcd2.ics - - - "fffff-abcd2" - BEGIN:VCALENDAR - VERSION:2.0 - PRODID:-//Example Corp.//CalDAV Client//EN - BEGIN:VEVENT - DTSTAMP:20060206T001121Z - DTSTART:20060103T170000 - DURATION:PT1H - RECURRENCE-ID:20060103T170000 - SUMMARY:Event #2 - UID:00959BC664CA650E933C892C@example.com - END:VEVENT - BEGIN:VEVENT - DTSTAMP:20060206T001121Z - DTSTART:20060104T190000 - DURATION:PT1H - RECURRENCE-ID:20060104T170000 - SUMMARY:Event #2 bis - UID:00959BC664CA650E933C892C@example.com - END:VEVENT - END:VCALENDAR - - - HTTP/1.1 200 OK - - - - http://cal.example.com/bernard/work/abcd3.ics - - - "fffff-abcd3" - BEGIN:VCALENDAR - VERSION:2.0 - PRODID:-//Example Corp.//CalDAV Client//EN - BEGIN:VEVENT - ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com - ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com - DTSTAMP:20060206T001220Z - DTSTART:20060104T150000 - DURATION:PT1H - LAST-MODIFIED:20060206T001330Z - ORGANIZER:mailto:cyrus@example.com - SEQUENCE:1 - STATUS:TENTATIVE - SUMMARY:Event #3 - UID:DC6C50A017428C5216A2F1CD@example.com - X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com - END:VEVENT - END:VCALENDAR - - - HTTP/1.1 200 OK - -"#; +// Adapted from Example 7.8.3 of RFC 4791 +// In the RFC the output is wrong since it returns DTSTART in UTC as local time, e.g. +// DTSTART:20060103T170000 +// instead of +// DTSTART:20060103T170000Z +// In https://datatracker.ietf.org/doc/html/rfc4791#section-9.6.5 +// it is clearly stated that times with timezone information MUST be returned in UTC. +// Also, the RECURRENCE-ID needs to include the TIMEZONE, which is fixed here by converting it to +// UTC +const OUTPUT_7_8_3: &str = r#" + + + /caldav/principal/user/calendar/abcd2.ics + + + BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Example Corp.//CalDAV Client//EN +BEGIN:VEVENT +DTSTAMP:20060206T001121Z +DTSTART:20060103T170000Z +DURATION:PT1H +SUMMARY:Event #2 +UID:abcd2 +RECURRENCE-ID:20060103T170000Z +END:VEVENT +BEGIN:VEVENT +DTSTAMP:20060206T001121Z +DTSTART:20060104T190000Z +DURATION:PT1H +RECURRENCE-ID:20060104T170000Z +SUMMARY:Event #2 bis +UID:abcd2 +END:VEVENT +END:VCALENDAR + + + HTTP/1.1 200 OK + + + + /caldav/principal/user/calendar/abcd3.ics + + + BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Example Corp.//CalDAV Client//EN +BEGIN:VEVENT +ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com +ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com +DTSTAMP:20060206T001220Z +DTSTART:20060104T150000Z +DURATION:PT1H +LAST-MODIFIED:20060206T001330Z +ORGANIZER:mailto:cyrus@example.com +SEQUENCE:1 +STATUS:TENTATIVE +SUMMARY:Event #3 +UID:abcd3 +X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com +END:VEVENT +END:VCALENDAR + + + HTTP/1.1 200 OK + + +"#; #[rstest] -#[case(0, ICS_1, REPORT_7_8_1)] -#[case(1, ICS_1, REPORT_7_8_2)] -#[case(2, ICS_1, REPORT_7_8_3)] +#[case(0, ICS_1, REPORT_7_8_1, None)] +#[case(1, ICS_1, REPORT_7_8_2, None)] +#[case(2, ICS_1, REPORT_7_8_3, Some(OUTPUT_7_8_3))] #[tokio::test] async fn test_report( #[from(test_store_context)] @@ -159,6 +168,7 @@ async fn test_report( #[case] case: usize, #[case] ics: &'static str, #[case] report: &'static str, + #[case] output: Option<&'static str>, ) { let context = context.await; let app = get_app(context.clone()); @@ -193,4 +203,7 @@ async fn test_report( assert_eq!(response.status(), StatusCode::MULTI_STATUS); let body = response.extract_string().await; insta::assert_snapshot!(format!("{case}_report_body"), body); + if let Some(output) = output { + similar_asserts::assert_eq!(output, body.replace('\r', "")); + } } diff --git a/src/integration_tests/caldav/mod.rs b/src/integration_tests/caldav/mod.rs index 3378fbe..4a91cd7 100644 --- a/src/integration_tests/caldav/mod.rs +++ b/src/integration_tests/caldav/mod.rs @@ -9,7 +9,7 @@ use tower::ServiceExt; mod calendar; mod calendar_import; -// mod calendar_report; +mod calendar_report; #[rstest] #[tokio::test] diff --git a/src/integration_tests/caldav/resources/rfc4791_appb.ics b/src/integration_tests/caldav/resources/rfc4791_appb.ics index ea74a96..4ab2a03 100644 --- a/src/integration_tests/caldav/resources/rfc4791_appb.ics +++ b/src/integration_tests/caldav/resources/rfc4791_appb.ics @@ -55,6 +55,7 @@ SEQUENCE:1 STATUS:TENTATIVE SUMMARY:Event #3 UID:abcd3 +X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com END:VEVENT BEGIN:VTODO DTSTAMP:20060205T235335Z diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap index 2cdafb5..059168d 100644 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_import__1_get_body.snap @@ -60,6 +60,7 @@ SEQUENCE:1 STATUS:TENTATIVE SUMMARY:Event #3 UID:[UID] +X-ABC-GUID:[UID] END:VEVENT BEGIN:VTODO DTSTAMP:20060205T235335Z diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__0_report_body.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__0_report_body.snap index b179127..79834cc 100644 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__0_report_body.snap +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__0_report_body.snap @@ -56,7 +56,7 @@ END:VCALENDAR /caldav/principal/user/calendar/abcd3.ics - "c6a5b1cf6985805686df99e7f2e1cf286567dcb3383fc6fa1b12ce42d3fbc01c" + "a84fd022dfc742bf8f17ac04fca3aad687e9ae724180185e8e0df11e432dae30" BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Example Corp.//CalDAV Client//EN @@ -90,6 +90,7 @@ SEQUENCE:1 STATUS:TENTATIVE SUMMARY:Event #3 UID:abcd3 +X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com END:VEVENT END:VCALENDAR diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__1_report_body.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__1_report_body.snap index f3e413b..09add03 100644 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__1_report_body.snap +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__1_report_body.snap @@ -88,6 +88,7 @@ SEQUENCE:1 STATUS:TENTATIVE SUMMARY:Event #3 UID:abcd3 +X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com END:VEVENT END:VCALENDAR diff --git a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__2_report_body.snap b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__2_report_body.snap index 9801e29..0ae78e9 100644 --- a/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__2_report_body.snap +++ b/src/integration_tests/caldav/snapshots/rustical__integration_tests__caldav__calendar_report__2_report_body.snap @@ -13,19 +13,19 @@ VERSION:2.0 PRODID:-//Example Corp.//CalDAV Client//EN BEGIN:VEVENT DTSTAMP:20060206T001121Z +DTSTART:20060103T170000Z DURATION:PT1H SUMMARY:Event #2 UID:abcd2 RECURRENCE-ID:20060103T170000Z -DTSTART:20060103T170000Z END:VEVENT BEGIN:VEVENT DTSTAMP:20060206T001121Z +DTSTART:20060104T190000Z DURATION:PT1H -SUMMARY:Event #2 -UID:abcd2 RECURRENCE-ID:20060104T170000Z -DTSTART:20060104T170000Z +SUMMARY:Event #2 bis +UID:abcd2 END:VEVENT END:VCALENDAR @@ -44,7 +44,7 @@ BEGIN:VEVENT ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:cyrus@example.com ATTENDEE;PARTSTAT=NEEDS-ACTION:mailto:lisa@example.com DTSTAMP:20060206T001220Z -DTSTART;TZID=US/Eastern:20060104T100000 +DTSTART:20060104T150000Z DURATION:PT1H LAST-MODIFIED:20060206T001330Z ORGANIZER:mailto:cyrus@example.com @@ -52,6 +52,7 @@ SEQUENCE:1 STATUS:TENTATIVE SUMMARY:Event #3 UID:abcd3 +X-ABC-GUID:E1CX5Dr-0007ym-Hz@example.com END:VEVENT END:VCALENDAR From 3460a2821e294cf565bb709928df63e84667f4b8 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:37:35 +0100 Subject: [PATCH 22/26] dav: Check Host matching for MV,COPY --- crates/dav/src/resource/methods/copy.rs | 10 +++++++++- crates/dav/src/resource/methods/mv.rs | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/dav/src/resource/methods/copy.rs b/crates/dav/src/resource/methods/copy.rs index bec1372..c0426c1 100644 --- a/crates/dav/src/resource/methods/copy.rs +++ b/crates/dav/src/resource/methods/copy.rs @@ -6,12 +6,15 @@ use axum::{ extract::{MatchedPath, Path, State}, response::{IntoResponse, Response}, }; +use axum_extra::TypedHeader; +use headers::Host; use http::{HeaderMap, StatusCode, Uri}; use matchit_serde::ParamsDeserializer; use serde::Deserialize; use tracing::instrument; #[instrument(skip(path, resource_service,))] +#[allow(clippy::too_many_arguments)] pub async fn axum_route_copy( Path(path): Path, State(resource_service): State, @@ -20,6 +23,7 @@ pub async fn axum_route_copy( Overwrite(overwrite): Overwrite, matched_path: MatchedPath, header_map: HeaderMap, + TypedHeader(host): TypedHeader, ) -> Result { let destination = header_map .get("Destination") @@ -27,7 +31,11 @@ pub async fn axum_route_copy( .to_str() .map_err(|_| crate::Error::Forbidden)?; let destination_uri: Uri = destination.parse().map_err(|_| crate::Error::Forbidden)?; - // TODO: Check that host also matches + if let Some(authority) = destination_uri.authority() + && host != authority.clone().into() + { + return Err(crate::Error::Forbidden.into()); + } let destination = destination_uri.path(); let mut router = matchit::Router::new(); diff --git a/crates/dav/src/resource/methods/mv.rs b/crates/dav/src/resource/methods/mv.rs index a10d5d8..8192565 100644 --- a/crates/dav/src/resource/methods/mv.rs +++ b/crates/dav/src/resource/methods/mv.rs @@ -6,12 +6,15 @@ use axum::{ extract::{MatchedPath, Path, State}, response::{IntoResponse, Response}, }; +use axum_extra::TypedHeader; +use headers::Host; use http::{HeaderMap, StatusCode, Uri}; use matchit_serde::ParamsDeserializer; use serde::Deserialize; use tracing::instrument; #[instrument(skip(path, resource_service,))] +#[allow(clippy::too_many_arguments)] pub async fn axum_route_move( Path(path): Path, State(resource_service): State, @@ -20,6 +23,7 @@ pub async fn axum_route_move( Overwrite(overwrite): Overwrite, matched_path: MatchedPath, header_map: HeaderMap, + TypedHeader(host): TypedHeader, ) -> Result { let destination = header_map .get("Destination") @@ -27,7 +31,11 @@ pub async fn axum_route_move( .to_str() .map_err(|_| crate::Error::Forbidden)?; let destination_uri: Uri = destination.parse().map_err(|_| crate::Error::Forbidden)?; - // TODO: Check that host also matches + if let Some(authority) = destination_uri.authority() + && host != authority.clone().into() + { + return Err(crate::Error::Forbidden.into()); + } let destination = destination_uri.path(); let mut router = matchit::Router::new(); From 303f9aff68c550e26ee5f05ce1eb2d4b51062d40 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Mon, 19 Jan 2026 12:51:51 +0100 Subject: [PATCH 23/26] Remove IcalError from caldav/carddav since it had an ambiguous status code --- crates/caldav/src/calendar/methods/import.rs | 10 ++++++++-- crates/caldav/src/error.rs | 5 ----- crates/carddav/src/address_object/methods.rs | 5 ++++- crates/carddav/src/error.rs | 5 ----- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index fd561e8..d6370a3 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -26,7 +26,10 @@ pub async fn route_import( } let parser = ical::IcalParser::from_slice(body.as_bytes()); - let mut cal = parser.expect_one()?.mutable(); + let mut cal = match parser.expect_one() { + Ok(cal) => cal.mutable(), + Err(err) => return Ok((StatusCode::BAD_REQUEST, err.to_string()).into_response()), + }; // Extract calendar metadata let displayname = cal @@ -67,7 +70,10 @@ pub async fn route_import( cal_components.push(CalendarObjectType::Todo); } - let objects = cal.into_objects()?.into_iter().map(Into::into).collect(); + let objects = match cal.into_objects() { + Ok(objects) => objects.into_iter().map(Into::into).collect(), + Err(err) => return Ok((StatusCode::BAD_REQUEST, err.to_string()).into_response()), + }; let new_cal = Calendar { principal, id: cal_id, diff --git a/crates/caldav/src/error.rs b/crates/caldav/src/error.rs index 49161d7..51e3da5 100644 --- a/crates/caldav/src/error.rs +++ b/crates/caldav/src/error.rs @@ -52,9 +52,6 @@ pub enum Error { #[error(transparent)] XmlDecodeError(#[from] rustical_xml::XmlError), - #[error(transparent)] - IcalError(#[from] rustical_ical::Error), - #[error(transparent)] PreconditionFailed(Precondition), } @@ -75,8 +72,6 @@ impl Error { Self::XmlDecodeError(_) => StatusCode::BAD_REQUEST, Self::ChronoParseError(_) | Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, - // TODO: Can also be Bad Request, if it's used input - Self::IcalError(_err) => StatusCode::INTERNAL_SERVER_ERROR, Self::PreconditionFailed(_err) => StatusCode::PRECONDITION_FAILED, } } diff --git a/crates/carddav/src/address_object/methods.rs b/crates/carddav/src/address_object/methods.rs index ceabfd3..9d3ab58 100644 --- a/crates/carddav/src/address_object/methods.rs +++ b/crates/carddav/src/address_object/methods.rs @@ -103,7 +103,10 @@ pub async fn put_object( true }; - let object = AddressObject::from_vcf(body)?; + let object = match AddressObject::from_vcf(body) { + Ok(object) => object, + Err(err) => return Ok((StatusCode::BAD_REQUEST, err.to_string()).into_response()), + }; let etag = object.get_etag(); addr_store .put_object(&principal, &addressbook_id, &object_id, object, overwrite) diff --git a/crates/carddav/src/error.rs b/crates/carddav/src/error.rs index dbdff71..5439529 100644 --- a/crates/carddav/src/error.rs +++ b/crates/carddav/src/error.rs @@ -23,9 +23,6 @@ pub enum Error { #[error(transparent)] XmlDecodeError(#[from] rustical_xml::XmlError), - - #[error(transparent)] - IcalError(#[from] rustical_ical::Error), } impl Error { @@ -43,8 +40,6 @@ impl Error { Self::XmlDecodeError(_) => StatusCode::BAD_REQUEST, Self::ChronoParseError(_) | Self::NotImplemented => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, - // TODO: Can also be Bad Request, if it's used input - Self::IcalError(_err) => StatusCode::INTERNAL_SERVER_ERROR, } } } From 0eef4ffabfb0938f81299ffd06ba9743d682db18 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Mon, 19 Jan 2026 13:40:54 +0100 Subject: [PATCH 24/26] Add test for uploading invalid calendar object and fix precondition --- crates/caldav/src/calendar_object/methods.rs | 12 ++- crates/caldav/src/error.rs | 3 + src/integration_tests/caldav/calendar.rs | 2 +- src/integration_tests/caldav/calendar_put.rs | 77 ++++++++++++++++++++ src/integration_tests/caldav/mod.rs | 1 + 5 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/integration_tests/caldav/calendar_put.rs diff --git a/crates/caldav/src/calendar_object/methods.rs b/crates/caldav/src/calendar_object/methods.rs index 2d7fc3f..3044aa7 100644 --- a/crates/caldav/src/calendar_object/methods.rs +++ b/crates/caldav/src/calendar_object/methods.rs @@ -11,7 +11,7 @@ use rustical_ical::CalendarObject; use rustical_store::CalendarStore; use rustical_store::auth::Principal; use std::str::FromStr; -use tracing::{debug, instrument}; +use tracing::{instrument, warn}; #[instrument(skip(cal_store))] pub async fn get_event( @@ -94,9 +94,13 @@ pub async fn put_event( true }; - let Ok(object) = CalendarObject::from_ics(body.clone()) else { - debug!("invalid calendar data:\n{body}"); - return Err(Error::PreconditionFailed(Precondition::ValidCalendarData)); + let object = match CalendarObject::from_ics(body.clone()) { + Ok(object) => object, + Err(err) => { + warn!("invalid calendar data:\n{body}"); + warn!("{err:#?}"); + return Err(Error::PreconditionFailed(Precondition::ValidCalendarData)); + } }; let etag = object.get_etag(); cal_store diff --git a/crates/caldav/src/error.rs b/crates/caldav/src/error.rs index 51e3da5..3fc5530 100644 --- a/crates/caldav/src/error.rs +++ b/crates/caldav/src/error.rs @@ -79,6 +79,9 @@ impl Error { impl IntoResponse for Error { fn into_response(self) -> axum::response::Response { + if let Self::PreconditionFailed(precondition) = self { + return precondition.into_response(); + } if matches!( self.status_code(), StatusCode::INTERNAL_SERVER_ERROR | StatusCode::PRECONDITION_FAILED diff --git a/src/integration_tests/caldav/calendar.rs b/src/integration_tests/caldav/calendar.rs index 475fe1d..ae3e005 100644 --- a/src/integration_tests/caldav/calendar.rs +++ b/src/integration_tests/caldav/calendar.rs @@ -8,7 +8,7 @@ use rustical_store::{CalendarMetadata, CalendarStore}; use rustical_store_sqlite::tests::{TestStoreContext, test_store_context}; use tower::ServiceExt; -fn mkcalendar_template( +pub fn mkcalendar_template( CalendarMetadata { displayname, order: _order, diff --git a/src/integration_tests/caldav/calendar_put.rs b/src/integration_tests/caldav/calendar_put.rs new file mode 100644 index 0000000..b99f274 --- /dev/null +++ b/src/integration_tests/caldav/calendar_put.rs @@ -0,0 +1,77 @@ +use axum::body::Body; +use headers::{Authorization, HeaderMapExt}; +use http::{Request, StatusCode}; +use rstest::rstest; +use rustical_store::CalendarMetadata; +use rustical_store_sqlite::tests::{TestStoreContext, test_store_context}; +use tower::ServiceExt; + +use crate::integration_tests::{ + ResponseExtractString, caldav::calendar::mkcalendar_template, get_app, +}; + +#[rstest] +#[tokio::test] +async fn test_put_invalid( + #[from(test_store_context)] + #[future] + context: TestStoreContext, +) { + let context = context.await; + let app = get_app(context.clone()); + + let calendar_meta = CalendarMetadata { + displayname: Some("Calendar".to_string()), + description: Some("Description".to_string()), + color: Some("#00FF00".to_string()), + order: 0, + }; + let (principal, cal_id) = ("user", "calendar"); + let url = format!("/caldav/principal/{principal}/{cal_id}"); + + let mut request = Request::builder() + .method("MKCALENDAR") + .uri(&url) + .body(Body::from(mkcalendar_template(&calendar_meta))) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + + // Invalid calendar data + let ical = r"BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Example Corp.//CalDAV Client//EN +BEGIN:VEVENT +UID:20010712T182145Z-123401@example.com +DTSTAMP:20060712T182145Z +DTSTART:20060714T170000Z +RRULE:UNTIL=123 +DTEND:20060715T040000Z +SUMMARY:Bastille Day Party +END:VEVENT +END:VCALENDAR"; + + let mut request = Request::builder() + .method("PUT") + .uri(format!("{url}/qwue23489.ics")) + .header("If-None-Match", "*") + .header("Content-Type", "text/calendar") + .body(Body::from(ical)) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::PRECONDITION_FAILED); + let body = response.extract_string().await; + insta::assert_snapshot!(body, @r#" + + + + + "#); +} diff --git a/src/integration_tests/caldav/mod.rs b/src/integration_tests/caldav/mod.rs index 4a91cd7..5d7d532 100644 --- a/src/integration_tests/caldav/mod.rs +++ b/src/integration_tests/caldav/mod.rs @@ -9,6 +9,7 @@ use tower::ServiceExt; mod calendar; mod calendar_import; +mod calendar_put; mod calendar_report; #[rstest] From 15e1509fe37bf07e5a4a6bebdd40c648b66faaf4 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Mon, 19 Jan 2026 14:48:21 +0100 Subject: [PATCH 25/26] sqlite_store: Add option to skip broken objects and add validation on start-up --- crates/caldav/src/calendar_object/methods.rs | 2 +- .../store_sqlite/src/addressbook_store/mod.rs | 61 ++++++++++-- crates/store_sqlite/src/calendar_store.rs | 94 ++++++++++++++++--- crates/store_sqlite/src/error.rs | 2 +- crates/store_sqlite/src/tests/mod.rs | 4 +- src/commands/mod.rs | 1 + src/config.rs | 2 + src/main.rs | 27 +++--- src/migration_0_12.rs | 76 --------------- 9 files changed, 156 insertions(+), 113 deletions(-) delete mode 100644 src/migration_0_12.rs diff --git a/crates/caldav/src/calendar_object/methods.rs b/crates/caldav/src/calendar_object/methods.rs index 3044aa7..873bc59 100644 --- a/crates/caldav/src/calendar_object/methods.rs +++ b/crates/caldav/src/calendar_object/methods.rs @@ -98,7 +98,7 @@ pub async fn put_event( Ok(object) => object, Err(err) => { warn!("invalid calendar data:\n{body}"); - warn!("{err:#?}"); + warn!("{err}"); return Err(Error::PreconditionFailed(Precondition::ValidCalendarData)); } }; diff --git a/crates/store_sqlite/src/addressbook_store/mod.rs b/crates/store_sqlite/src/addressbook_store/mod.rs index 4d85ff8..be71439 100644 --- a/crates/store_sqlite/src/addressbook_store/mod.rs +++ b/crates/store_sqlite/src/addressbook_store/mod.rs @@ -2,6 +2,7 @@ use super::ChangeOperation; use crate::BEGIN_IMMEDIATE; use async_trait::async_trait; use derive_more::derive::Constructor; +use ical::parser::ParserError; use rustical_ical::AddressObject; use rustical_store::{ Addressbook, AddressbookStore, CollectionMetadata, CollectionOperation, @@ -9,7 +10,7 @@ use rustical_store::{ }; use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction}; use tokio::sync::mpsc::Sender; -use tracing::{error_span, instrument, warn}; +use tracing::{error, error_span, instrument, warn}; pub mod birthday_calendar; @@ -18,6 +19,12 @@ struct AddressObjectRow { id: String, vcf: String, } +impl From for (String, Result) { + fn from(row: AddressObjectRow) -> Self { + let result = AddressObject::from_vcf(row.vcf); + (row.id, result) + } +} impl TryFrom for (String, AddressObject) { type Error = rustical_store::Error; @@ -31,6 +38,7 @@ impl TryFrom for (String, AddressObject) { pub struct SqliteAddressbookStore { db: SqlitePool, sender: Sender, + skip_broken: bool, } impl SqliteAddressbookStore { @@ -88,6 +96,36 @@ impl SqliteAddressbookStore { Ok(()) } + #[allow(clippy::missing_panics_doc)] + pub async fn validate_objects(&self, principal: &str) -> Result<(), Error> { + let mut success = true; + for addressbook in self.get_addressbooks(principal).await? { + for (object_id, res) in Self::_get_objects(&self.db, principal, &addressbook.id).await? + { + if let Err(err) = res { + warn!( + "Invalid address object found at {principal}/{addr_id}/{object_id}.vcf. Error: {err}", + addr_id = addressbook.id + ); + success = false; + } + } + } + if !success { + if self.skip_broken { + error!( + "Not all address objects are valid. Since data_store.sqlite.skip_broken=true they will be hidden. You are still advised to manually remove or repair the object. If you need help feel free to open up an issue on GitHub." + ); + } else { + error!( + "Not all address objects are valid. Since data_store.sqlite.skip_broken=false this causes a panic. Remove or repair the broken objects manually or set data_store.sqlite.skip_broken=false as a temporary solution to ignore the error. If you need help feel free to open up an issue on GitHub." + ); + panic!(); + } + } + Ok(()) + } + // Logs an operation to an address object async fn log_object_operation( tx: &mut Transaction<'_, Sqlite>, @@ -134,7 +172,7 @@ impl SqliteAddressbookStore { if let Err(err) = self.sender.try_send(CollectionOperation { topic, data }) { error_span!( "Error trying to send addressbook update notification:", - err = format!("{err:?}"), + err = format!("{err}"), ); } } @@ -353,8 +391,8 @@ impl SqliteAddressbookStore { executor: E, principal: &str, addressbook_id: &str, - ) -> Result, rustical_store::Error> { - sqlx::query_as!( + ) -> Result)>, Error> { + Ok(sqlx::query_as!( AddressObjectRow, "SELECT id, vcf FROM addressobjects WHERE principal = ? AND addressbook_id = ? AND deleted_at IS NULL", principal, @@ -363,8 +401,8 @@ impl SqliteAddressbookStore { .fetch_all(executor) .await.map_err(crate::Error::from)? .into_iter() - .map(std::convert::TryInto::try_into) - .collect() + .map(Into::into) + ) } async fn _get_object<'e, E: Executor<'e, Database = Sqlite>>( @@ -607,7 +645,16 @@ impl AddressbookStore for SqliteAddressbookStore { principal: &str, addressbook_id: &str, ) -> Result, rustical_store::Error> { - Self::_get_objects(&self.db, principal, addressbook_id).await + let objects = Self::_get_objects(&self.db, principal, addressbook_id).await?; + if self.skip_broken { + Ok(objects + .filter_map(|(id, res)| Some((id, res.ok()?))) + .collect()) + } else { + Ok(objects + .map(|(id, res)| res.map(|obj| (id, obj))) + .collect::, _>>()?) + } } #[instrument] diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index b06b085..7e84236 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -3,6 +3,7 @@ use crate::BEGIN_IMMEDIATE; use async_trait::async_trait; use chrono::TimeDelta; use derive_more::derive::Constructor; +use ical::parser::ParserError; use ical::types::CalDateTime; use regex::Regex; use rustical_ical::{CalendarObject, CalendarObjectType}; @@ -13,7 +14,7 @@ use rustical_store::{CollectionOperation, CollectionOperationInfo}; use sqlx::types::chrono::NaiveDateTime; use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction}; use tokio::sync::mpsc::Sender; -use tracing::{error_span, instrument, warn}; +use tracing::{error, error_span, instrument, warn}; #[derive(Debug, Clone)] struct CalendarObjectRow { @@ -22,6 +23,23 @@ struct CalendarObjectRow { uid: String, } +impl From for (String, Result) { + fn from(row: CalendarObjectRow) -> Self { + let result = CalendarObject::from_ics(row.ics).inspect(|object| { + if object.get_uid() != row.uid { + warn!( + "Calendar object {}.ics: UID={} and row uid={} do not match", + row.id, + object.get_uid(), + row.uid + ); + } + }); + + (row.id, result) + } +} + impl TryFrom for (String, CalendarObject) { type Error = rustical_store::Error; @@ -92,6 +110,7 @@ impl From for Calendar { pub struct SqliteCalendarStore { db: SqlitePool, sender: Sender, + skip_broken: bool, } impl SqliteCalendarStore { @@ -141,11 +160,40 @@ impl SqliteCalendarStore { if let Err(err) = self.sender.try_send(CollectionOperation { topic, data }) { error_span!( "Error trying to send calendar update notification:", - err = format!("{err:?}"), + err = format!("{err}"), ); } } + #[allow(clippy::missing_panics_doc)] + pub async fn validate_objects(&self, principal: &str) -> Result<(), Error> { + let mut success = true; + for calendar in self.get_calendars(principal).await? { + for (object_id, res) in Self::_get_objects(&self.db, principal, &calendar.id).await? { + if let Err(err) = res { + warn!( + "Invalid calendar object found at {principal}/{cal_id}/{object_id}.ics. Error: {err}", + cal_id = calendar.id + ); + success = false; + } + } + } + if !success { + if self.skip_broken { + error!( + "Not all calendar objects are valid. Since data_store.sqlite.skip_broken=true they will be hidden. You are still advised to manually remove or repair the object. If you need help feel free to open up an issue on GitHub." + ); + } else { + error!( + "Not all calendar objects are valid. Since data_store.sqlite.skip_broken=false this causes a panic. Remove or repair the broken objects manually or set data_store.sqlite.skip_broken=false as a temporary solution to ignore the error. If you need help feel free to open up an issue on GitHub." + ); + panic!(); + } + } + Ok(()) + } + /// In the past exports generated objects with invalid VERSION:4.0 /// This repair sets them to VERSION:2.0 #[allow(clippy::missing_panics_doc)] @@ -456,8 +504,8 @@ impl SqliteCalendarStore { executor: E, principal: &str, cal_id: &str, - ) -> Result, Error> { - sqlx::query_as!( + ) -> Result)>, Error> { + Ok(sqlx::query_as!( CalendarObjectRow, "SELECT id, uid, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL", principal, @@ -466,8 +514,8 @@ impl SqliteCalendarStore { .fetch_all(executor) .await.map_err(crate::Error::from)? .into_iter() - .map(std::convert::TryInto::try_into) - .collect() + .map(Into::into) + ) } async fn _calendar_query<'e, E: Executor<'e, Database = Sqlite>>( @@ -475,14 +523,14 @@ impl SqliteCalendarStore { principal: &str, cal_id: &str, query: CalendarQuery, - ) -> Result, Error> { + ) -> Result)>, Error> { // We extend our query interval by one day in each direction since we really don't want to // miss any objects because of timezone differences // I've previously tried NaiveDate::MIN,MAX, but it seems like sqlite cannot handle these let start = query.time_start.map(|start| start - TimeDelta::days(1)); let end = query.time_end.map(|end| end + TimeDelta::days(1)); - sqlx::query_as!( + Ok(sqlx::query_as!( CalendarObjectRow, r"SELECT id, uid, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL @@ -500,8 +548,7 @@ impl SqliteCalendarStore { .await .map_err(crate::Error::from)? .into_iter() - .map(std::convert::TryInto::try_into) - .collect() + .map(Into::into)) } async fn _get_object<'e, E: Executor<'e, Database = Sqlite>>( @@ -641,6 +688,7 @@ impl SqliteCalendarStore { principal: &str, cal_id: &str, synctoken: i64, + skip_broken: bool, ) -> Result<(Vec<(String, CalendarObject)>, Vec, i64), Error> { struct Row { object_id: String, @@ -670,6 +718,8 @@ impl SqliteCalendarStore { match Self::_get_object(&mut *conn, principal, cal_id, &object_id, false).await { Ok(object) => objects.push((object_id, object)), Err(rustical_store::Error::NotFound) => deleted_objects.push(object_id), + // Skip broken object + Err(rustical_store::Error::IcalError(_)) if skip_broken => (), Err(err) => return Err(err), } } @@ -820,7 +870,16 @@ impl CalendarStore for SqliteCalendarStore { cal_id: &str, query: CalendarQuery, ) -> Result, Error> { - Self::_calendar_query(&self.db, principal, cal_id, query).await + let objects = Self::_calendar_query(&self.db, principal, cal_id, query).await?; + if self.skip_broken { + Ok(objects + .filter_map(|(id, res)| Some((id, res.ok()?))) + .collect()) + } else { + Ok(objects + .map(|(id, res)| res.map(|obj| (id, obj))) + .collect::, _>>()?) + } } async fn calendar_metadata( @@ -851,7 +910,16 @@ impl CalendarStore for SqliteCalendarStore { principal: &str, cal_id: &str, ) -> Result, Error> { - Self::_get_objects(&self.db, principal, cal_id).await + let objects = Self::_get_objects(&self.db, principal, cal_id).await?; + if self.skip_broken { + Ok(objects + .filter_map(|(id, res)| Some((id, res.ok()?))) + .collect()) + } else { + Ok(objects + .map(|(id, res)| res.map(|obj| (id, obj))) + .collect::, _>>()?) + } } #[instrument] @@ -974,7 +1042,7 @@ impl CalendarStore for SqliteCalendarStore { cal_id: &str, synctoken: i64, ) -> Result<(Vec<(String, CalendarObject)>, Vec, i64), Error> { - Self::_sync_changes(&self.db, principal, cal_id, synctoken).await + Self::_sync_changes(&self.db, principal, cal_id, synctoken, self.skip_broken).await } fn is_read_only(&self, _cal_id: &str) -> bool { diff --git a/crates/store_sqlite/src/error.rs b/crates/store_sqlite/src/error.rs index 38e2af8..96e598a 100644 --- a/crates/store_sqlite/src/error.rs +++ b/crates/store_sqlite/src/error.rs @@ -18,7 +18,7 @@ impl From for Error { sqlx::Error::RowNotFound => Self::StoreError(rustical_store::Error::NotFound), sqlx::Error::Database(err) => { if err.is_unique_violation() { - warn!("{err:?}"); + warn!("{err}"); Self::StoreError(rustical_store::Error::AlreadyExists) } else { Self::SqlxError(sqlx::Error::Database(err)) diff --git a/crates/store_sqlite/src/tests/mod.rs b/crates/store_sqlite/src/tests/mod.rs index aee4f21..2f33ead 100644 --- a/crates/store_sqlite/src/tests/mod.rs +++ b/crates/store_sqlite/src/tests/mod.rs @@ -52,8 +52,8 @@ pub async fn test_store_context() -> TestStoreContext { let db = get_test_db().await; TestStoreContext { db: db.clone(), - addr_store: SqliteAddressbookStore::new(db.clone(), send_addr), - cal_store: SqliteCalendarStore::new(db.clone(), send_cal), + addr_store: SqliteAddressbookStore::new(db.clone(), send_addr, false), + cal_store: SqliteCalendarStore::new(db.clone(), send_cal, false), principal_store: SqlitePrincipalStore::new(db.clone()), sub_store: SqliteStore::new(db), } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 4f22346..7614232 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -18,6 +18,7 @@ pub fn cmd_gen_config(_args: GenConfigArgs) -> anyhow::Result<()> { data_store: DataStoreConfig::Sqlite(SqliteDataStoreConfig { db_url: "/var/lib/rustical/db.sqlite3".to_owned(), run_repairs: true, + skip_broken: true, }), tracing: TracingConfig::default(), frontend: FrontendConfig { diff --git a/src/config.rs b/src/config.rs index b559f8b..3a23f66 100644 --- a/src/config.rs +++ b/src/config.rs @@ -28,6 +28,8 @@ pub struct SqliteDataStoreConfig { pub db_url: String, #[serde(default = "default_true")] pub run_repairs: bool, + #[serde(default = "default_true")] + pub skip_broken: bool, } #[derive(Debug, Deserialize, Serialize)] diff --git a/src/main.rs b/src/main.rs index b29766a..ae7896d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,9 +34,6 @@ 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 { @@ -73,13 +70,18 @@ async fn get_data_stores( DataStoreConfig::Sqlite(SqliteDataStoreConfig { db_url, run_repairs, + skip_broken, }) => { let db = create_db_pool(db_url, migrate).await?; // Channel to watch for changes (for DAV Push) let (send, recv) = tokio::sync::mpsc::channel(1000); - let addressbook_store = Arc::new(SqliteAddressbookStore::new(db.clone(), send.clone())); - let cal_store = Arc::new(SqliteCalendarStore::new(db.clone(), send)); + let addressbook_store = Arc::new(SqliteAddressbookStore::new( + db.clone(), + send.clone(), + *skip_broken, + )); + let cal_store = Arc::new(SqliteCalendarStore::new(db.clone(), send, *skip_broken)); if *run_repairs { info!("Running repair tasks"); addressbook_store.repair_orphans().await?; @@ -88,6 +90,13 @@ async fn get_data_stores( } let subscription_store = Arc::new(SqliteStore::new(db.clone())); let principal_store = Arc::new(SqlitePrincipalStore::new(db)); + + // Validate all calendar objects + for principal in principal_store.get_principals().await? { + cal_store.validate_objects(&principal.id).await?; + addressbook_store.validate_objects(&principal.id).await?; + } + ( addressbook_store, cal_store, @@ -125,14 +134,6 @@ 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. -In the next major release these will be rejected and cause errors. -If 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 deleted file mode 100644 index f661f8a..0000000 --- a/src/migration_0_12.rs +++ /dev/null @@ -1,76 +0,0 @@ -use ical::parser::{ical::IcalObjectParser, vcard::VcardParser}; -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_id, object) in cal_store - .get_objects(&calendar.principal, &calendar.id) - .await? - { - if let Err(err) = - IcalObjectParser::from_slice(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, - ); - 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_id, object) in addr_store - .get_objects(&addressbook.principal, &addressbook.id) - .await? - { - if let Err(err) = VcardParser::from_slice(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, - ); - 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(()) -} From ea2f8412697ea7b4dcb803c2bfd131de67955939 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Mon, 19 Jan 2026 15:04:54 +0100 Subject: [PATCH 26/26] ical-rs: Pin version to Git commit --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4a8517..54945ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1771,7 +1771,7 @@ dependencies = [ [[package]] name = "ical" version = "0.12.0-dev" -source = "git+https://github.com/lennart-k/ical-rs?branch=dev#8697656303f182ce173efdaf6aa7e842ffdb3f33" +source = "git+https://github.com/lennart-k/ical-rs?rev=f1ad6456fd6cbd1e6da095297febddd2cfe61422#f1ad6456fd6cbd1e6da095297febddd2cfe61422" dependencies = [ "chrono", "chrono-tz", diff --git a/Cargo.toml b/Cargo.toml index c8ba8f2..a6fab4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +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", branch = "dev", features = [ +ical = { git = "https://github.com/lennart-k/ical-rs", rev = "f1ad6456fd6cbd1e6da095297febddd2cfe61422", features = [ "chrono-tz", ] } toml = "0.9"