From 0eb8359e262530becd88df9d95940da4a0468fea Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:30:16 +0200 Subject: [PATCH] rewrite combined calendar store in preparation for sharing --- crates/store/src/combined_calendar_store.rs | 384 ++++++++------------ crates/store/src/contact_birthday_store.rs | 5 + src/app.rs | 7 +- 3 files changed, 163 insertions(+), 233 deletions(-) diff --git a/crates/store/src/combined_calendar_store.rs b/crates/store/src/combined_calendar_store.rs index 4b4df4a..2a7110c 100644 --- a/crates/store/src/combined_calendar_store.rs +++ b/crates/store/src/combined_calendar_store.rs @@ -1,282 +1,208 @@ +use crate::CalendarStore; use async_trait::async_trait; -use derive_more::Constructor; -use rustical_ical::CalendarObject; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; -use crate::{ - Calendar, CalendarStore, Error, calendar_store::CalendarQuery, - contact_birthday_store::BIRTHDAYS_PREFIX, -}; - -#[derive(Debug, Constructor)] -pub struct CombinedCalendarStore { - cal_store: Arc, - birthday_store: Arc, +pub trait PrefixedCalendarStore: CalendarStore { + const PREFIX: &'static str; } -impl Clone for CombinedCalendarStore { - fn clone(&self) -> Self { +#[derive(Clone)] +pub struct CombinedCalendarStore { + stores: HashMap<&'static str, Arc>, + default: Arc, +} + +impl CombinedCalendarStore { + pub fn new(default: Arc) -> Self { Self { - cal_store: self.cal_store.clone(), - birthday_store: self.birthday_store.clone(), + stores: HashMap::new(), + default, } } + + pub fn with_store(mut self, store: Arc) -> Self { + let store: Arc = store; + self.stores.insert(CS::PREFIX, store); + self + } + + fn store_for_id(&self, id: &str) -> Arc { + self.stores + .iter() + .find(|&(prefix, _store)| id.starts_with(prefix)) + .map(|(_prefix, store)| store.clone()) + .unwrap_or(self.default.clone()) + } } #[async_trait] -impl CalendarStore for CombinedCalendarStore { +impl CalendarStore for CombinedCalendarStore { #[inline] async fn get_calendar( &self, principal: &str, id: &str, show_deleted: bool, - ) -> Result { - if id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .get_calendar(principal, id, show_deleted) - .await - } else { - self.cal_store - .get_calendar(principal, id, show_deleted) - .await - } + ) -> Result { + self.store_for_id(id) + .get_calendar(principal, id, show_deleted) + .await } - #[inline] async fn update_calendar( &self, principal: String, id: String, - calendar: Calendar, + calendar: crate::Calendar, ) -> Result<(), crate::Error> { - if id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .update_calendar(principal, id, calendar) - .await - } else { - self.cal_store - .update_calendar(principal, id, calendar) - .await - } + self.store_for_id(&id) + .update_calendar(principal, id, calendar) + .await } - #[inline] - async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error> { - if calendar.id.starts_with(BIRTHDAYS_PREFIX) { - Err(Error::ReadOnly) - } else { - self.cal_store.insert_calendar(calendar).await - } + async fn insert_calendar(&self, calendar: crate::Calendar) -> Result<(), crate::Error> { + self.store_for_id(&calendar.id) + .insert_calendar(calendar) + .await } - #[inline] - async fn get_calendars(&self, principal: &str) -> Result, Error> { - Ok([ - self.cal_store.get_calendars(principal).await?, - self.birthday_store.get_calendars(principal).await?, - ] - .concat()) + async fn delete_calendar( + &self, + principal: &str, + name: &str, + use_trashbin: bool, + ) -> Result<(), crate::Error> { + self.store_for_id(name) + .delete_calendar(principal, name, use_trashbin) + .await + } + + async fn restore_calendar(&self, principal: &str, name: &str) -> Result<(), crate::Error> { + self.store_for_id(name) + .restore_calendar(principal, name) + .await + } + + async fn sync_changes( + &self, + principal: &str, + cal_id: &str, + synctoken: i64, + ) -> Result<(Vec, Vec, i64), crate::Error> { + self.store_for_id(cal_id) + .sync_changes(principal, cal_id, synctoken) + .await + } + + async fn import_calendar( + &self, + calendar: crate::Calendar, + objects: Vec, + merge_existing: bool, + ) -> Result<(), crate::Error> { + self.store_for_id(&calendar.id) + .import_calendar(calendar, objects, merge_existing) + .await + } + + async fn calendar_query( + &self, + principal: &str, + cal_id: &str, + query: crate::calendar_store::CalendarQuery, + ) -> Result, crate::Error> { + self.store_for_id(cal_id) + .calendar_query(principal, cal_id, query) + .await + } + + async fn restore_object( + &self, + principal: &str, + cal_id: &str, + object_id: &str, + ) -> Result<(), crate::Error> { + self.store_for_id(cal_id) + .restore_object(principal, cal_id, object_id) + .await + } + + async fn calendar_metadata( + &self, + principal: &str, + cal_id: &str, + ) -> Result { + self.store_for_id(cal_id) + .calendar_metadata(principal, cal_id) + .await + } + + async fn get_objects( + &self, + principal: &str, + cal_id: &str, + ) -> Result, crate::Error> { + self.store_for_id(cal_id) + .get_objects(principal, cal_id) + .await + } + + async fn put_object( + &self, + principal: String, + cal_id: String, + object: rustical_ical::CalendarObject, + overwrite: bool, + ) -> Result<(), crate::Error> { + self.store_for_id(&cal_id) + .put_object(principal, cal_id, object, overwrite) + .await } - #[inline] async fn delete_object( &self, principal: &str, cal_id: &str, object_id: &str, use_trashbin: bool, - ) -> Result<(), Error> { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .delete_object(principal, cal_id, object_id, use_trashbin) - .await - } else { - self.cal_store - .delete_object(principal, cal_id, object_id, use_trashbin) - .await - } + ) -> Result<(), crate::Error> { + self.store_for_id(cal_id) + .delete_object(principal, cal_id, object_id, use_trashbin) + .await } - #[inline] async fn get_object( &self, principal: &str, cal_id: &str, object_id: &str, show_deleted: bool, - ) -> Result { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .get_object(principal, cal_id, object_id, show_deleted) - .await - } else { - self.cal_store - .get_object(principal, cal_id, object_id, show_deleted) - .await - } + ) -> Result { + self.store_for_id(cal_id) + .get_object(principal, cal_id, object_id, show_deleted) + .await } - #[inline] - async fn sync_changes( + async fn get_calendars(&self, principal: &str) -> Result, crate::Error> { + let mut calendars = self.default.get_calendars(principal).await?; + for store in self.stores.values() { + calendars.extend(store.get_calendars(principal).await?); + } + Ok(calendars) + } + + async fn get_deleted_calendars( &self, principal: &str, - cal_id: &str, - synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error> { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .sync_changes(principal, cal_id, synctoken) - .await - } else { - self.cal_store - .sync_changes(principal, cal_id, synctoken) - .await + ) -> Result, crate::Error> { + let mut calendars = self.default.get_deleted_calendars(principal).await?; + for store in self.stores.values() { + calendars.extend(store.get_deleted_calendars(principal).await?); } + Ok(calendars) } - #[inline] - async fn calendar_metadata( - &self, - principal: &str, - cal_id: &str, - ) -> Result { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .calendar_metadata(principal, cal_id) - .await - } else { - self.cal_store.calendar_metadata(principal, cal_id).await - } - } - #[inline] - async fn get_objects( - &self, - principal: &str, - cal_id: &str, - ) -> Result, Error> { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store.get_objects(principal, cal_id).await - } else { - self.cal_store.get_objects(principal, cal_id).await - } - } - - #[inline] - async fn calendar_query( - &self, - principal: &str, - cal_id: &str, - query: CalendarQuery, - ) -> Result, Error> { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .calendar_query(principal, cal_id, query) - .await - } else { - self.cal_store - .calendar_query(principal, cal_id, query) - .await - } - } - - #[inline] - async fn restore_calendar(&self, principal: &str, name: &str) -> Result<(), Error> { - if name.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store.restore_calendar(principal, name).await - } else { - self.cal_store.restore_calendar(principal, name).await - } - } - - #[inline] - async fn import_calendar( - &self, - calendar: Calendar, - objects: Vec, - merge_existing: bool, - ) -> Result<(), Error> { - if calendar.id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .import_calendar(calendar, objects, merge_existing) - .await - } else { - self.cal_store - .import_calendar(calendar, objects, merge_existing) - .await - } - } - - #[inline] - async fn delete_calendar( - &self, - principal: &str, - name: &str, - use_trashbin: bool, - ) -> Result<(), Error> { - if name.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .delete_calendar(principal, name, use_trashbin) - .await - } else { - self.cal_store - .delete_calendar(principal, name, use_trashbin) - .await - } - } - - #[inline] - async fn get_deleted_calendars(&self, principal: &str) -> Result, Error> { - Ok([ - self.birthday_store.get_deleted_calendars(principal).await?, - self.cal_store.get_deleted_calendars(principal).await?, - ] - .concat()) - } - - #[inline] - async fn restore_object( - &self, - principal: &str, - cal_id: &str, - object_id: &str, - ) -> Result<(), Error> { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .restore_object(principal, cal_id, object_id) - .await - } else { - self.cal_store - .restore_object(principal, cal_id, object_id) - .await - } - } - - #[inline] - async fn put_object( - &self, - principal: String, - cal_id: String, - object: CalendarObject, - overwrite: bool, - ) -> Result<(), Error> { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store - .put_object(principal, cal_id, object, overwrite) - .await - } else { - self.cal_store - .put_object(principal, cal_id, object, overwrite) - .await - } - } - - #[inline] fn is_read_only(&self, cal_id: &str) -> bool { - if cal_id.starts_with(BIRTHDAYS_PREFIX) { - self.birthday_store.is_read_only(cal_id) - } else { - self.cal_store.is_read_only(cal_id) - } + self.store_for_id(cal_id).is_read_only(cal_id) } } diff --git a/crates/store/src/contact_birthday_store.rs b/crates/store/src/contact_birthday_store.rs index 7a2da66..246d436 100644 --- a/crates/store/src/contact_birthday_store.rs +++ b/crates/store/src/contact_birthday_store.rs @@ -1,5 +1,6 @@ use crate::{ Addressbook, AddressbookStore, Calendar, CalendarStore, Error, calendar::CalendarMetadata, + combined_calendar_store::PrefixedCalendarStore, }; use async_trait::async_trait; use derive_more::derive::Constructor; @@ -12,6 +13,10 @@ pub(crate) const BIRTHDAYS_PREFIX: &str = "_birthdays_"; #[derive(Constructor, Clone)] pub struct ContactBirthdayStore(Arc); +impl PrefixedCalendarStore for ContactBirthdayStore { + const PREFIX: &'static str = BIRTHDAYS_PREFIX; +} + fn birthday_calendar(addressbook: Addressbook) -> Calendar { Calendar { principal: addressbook.principal, diff --git a/src/app.rs b/src/app.rs index 0f51d85..53fa934 100644 --- a/src/app.rs +++ b/src/app.rs @@ -40,10 +40,9 @@ pub fn make_app( dav_push_enabled: bool, session_cookie_samesite_strict: bool, ) -> Router<()> { - let combined_cal_store = Arc::new(CombinedCalendarStore::new( - cal_store.clone(), - ContactBirthdayStore::new(addr_store.clone()).into(), - )); + let birthday_store = Arc::new(ContactBirthdayStore::new(addr_store.clone())); + let combined_cal_store = + Arc::new(CombinedCalendarStore::new(cal_store.clone()).with_store(birthday_store)); let mut router = Router::new() .merge(caldav_router(