rewrite combined calendar store in preparation for sharing

This commit is contained in:
Lennart
2025-09-02 23:30:16 +02:00
parent 7d961ea93b
commit 0eb8359e26
3 changed files with 163 additions and 233 deletions

View File

@@ -1,282 +1,208 @@
use crate::CalendarStore;
use async_trait::async_trait; use async_trait::async_trait;
use derive_more::Constructor; use std::{collections::HashMap, sync::Arc};
use rustical_ical::CalendarObject;
use std::sync::Arc;
use crate::{ pub trait PrefixedCalendarStore: CalendarStore {
Calendar, CalendarStore, Error, calendar_store::CalendarQuery, const PREFIX: &'static str;
contact_birthday_store::BIRTHDAYS_PREFIX,
};
#[derive(Debug, Constructor)]
pub struct CombinedCalendarStore<CS: CalendarStore, BS: CalendarStore> {
cal_store: Arc<CS>,
birthday_store: Arc<BS>,
} }
impl<CS: CalendarStore, BS: CalendarStore> Clone for CombinedCalendarStore<CS, BS> { #[derive(Clone)]
fn clone(&self) -> Self { pub struct CombinedCalendarStore {
stores: HashMap<&'static str, Arc<dyn CalendarStore>>,
default: Arc<dyn CalendarStore>,
}
impl CombinedCalendarStore {
pub fn new(default: Arc<dyn CalendarStore>) -> Self {
Self { Self {
cal_store: self.cal_store.clone(), stores: HashMap::new(),
birthday_store: self.birthday_store.clone(), default,
} }
} }
pub fn with_store<CS: PrefixedCalendarStore>(mut self, store: Arc<CS>) -> Self {
let store: Arc<dyn CalendarStore> = store;
self.stores.insert(CS::PREFIX, store);
self
}
fn store_for_id(&self, id: &str) -> Arc<dyn CalendarStore> {
self.stores
.iter()
.find(|&(prefix, _store)| id.starts_with(prefix))
.map(|(_prefix, store)| store.clone())
.unwrap_or(self.default.clone())
}
} }
#[async_trait] #[async_trait]
impl<CS: CalendarStore, BS: CalendarStore> CalendarStore for CombinedCalendarStore<CS, BS> { impl CalendarStore for CombinedCalendarStore {
#[inline] #[inline]
async fn get_calendar( async fn get_calendar(
&self, &self,
principal: &str, principal: &str,
id: &str, id: &str,
show_deleted: bool, show_deleted: bool,
) -> Result<Calendar, Error> { ) -> Result<crate::Calendar, crate::Error> {
if id.starts_with(BIRTHDAYS_PREFIX) { self.store_for_id(id)
self.birthday_store .get_calendar(principal, id, show_deleted)
.get_calendar(principal, id, show_deleted) .await
.await
} else {
self.cal_store
.get_calendar(principal, id, show_deleted)
.await
}
} }
#[inline]
async fn update_calendar( async fn update_calendar(
&self, &self,
principal: String, principal: String,
id: String, id: String,
calendar: Calendar, calendar: crate::Calendar,
) -> Result<(), crate::Error> { ) -> Result<(), crate::Error> {
if id.starts_with(BIRTHDAYS_PREFIX) { self.store_for_id(&id)
self.birthday_store .update_calendar(principal, id, calendar)
.update_calendar(principal, id, calendar) .await
.await
} else {
self.cal_store
.update_calendar(principal, id, calendar)
.await
}
} }
#[inline] async fn insert_calendar(&self, calendar: crate::Calendar) -> Result<(), crate::Error> {
async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error> { self.store_for_id(&calendar.id)
if calendar.id.starts_with(BIRTHDAYS_PREFIX) { .insert_calendar(calendar)
Err(Error::ReadOnly) .await
} else {
self.cal_store.insert_calendar(calendar).await
}
} }
#[inline] async fn delete_calendar(
async fn get_calendars(&self, principal: &str) -> Result<Vec<Calendar>, Error> { &self,
Ok([ principal: &str,
self.cal_store.get_calendars(principal).await?, name: &str,
self.birthday_store.get_calendars(principal).await?, use_trashbin: bool,
] ) -> Result<(), crate::Error> {
.concat()) 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<rustical_ical::CalendarObject>, Vec<String>, 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<rustical_ical::CalendarObject>,
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<Vec<rustical_ical::CalendarObject>, 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<crate::CollectionMetadata, crate::Error> {
self.store_for_id(cal_id)
.calendar_metadata(principal, cal_id)
.await
}
async fn get_objects(
&self,
principal: &str,
cal_id: &str,
) -> Result<Vec<rustical_ical::CalendarObject>, 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( async fn delete_object(
&self, &self,
principal: &str, principal: &str,
cal_id: &str, cal_id: &str,
object_id: &str, object_id: &str,
use_trashbin: bool, use_trashbin: bool,
) -> Result<(), Error> { ) -> Result<(), crate::Error> {
if cal_id.starts_with(BIRTHDAYS_PREFIX) { self.store_for_id(cal_id)
self.birthday_store .delete_object(principal, cal_id, object_id, use_trashbin)
.delete_object(principal, cal_id, object_id, use_trashbin) .await
.await
} else {
self.cal_store
.delete_object(principal, cal_id, object_id, use_trashbin)
.await
}
} }
#[inline]
async fn get_object( async fn get_object(
&self, &self,
principal: &str, principal: &str,
cal_id: &str, cal_id: &str,
object_id: &str, object_id: &str,
show_deleted: bool, show_deleted: bool,
) -> Result<CalendarObject, Error> { ) -> Result<rustical_ical::CalendarObject, crate::Error> {
if cal_id.starts_with(BIRTHDAYS_PREFIX) { self.store_for_id(cal_id)
self.birthday_store .get_object(principal, cal_id, object_id, show_deleted)
.get_object(principal, cal_id, object_id, show_deleted) .await
.await
} else {
self.cal_store
.get_object(principal, cal_id, object_id, show_deleted)
.await
}
} }
#[inline] async fn get_calendars(&self, principal: &str) -> Result<Vec<crate::Calendar>, crate::Error> {
async fn sync_changes( 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, &self,
principal: &str, principal: &str,
cal_id: &str, ) -> Result<Vec<crate::Calendar>, crate::Error> {
synctoken: i64, let mut calendars = self.default.get_deleted_calendars(principal).await?;
) -> Result<(Vec<CalendarObject>, Vec<String>, i64), Error> { for store in self.stores.values() {
if cal_id.starts_with(BIRTHDAYS_PREFIX) { calendars.extend(store.get_deleted_calendars(principal).await?);
self.birthday_store
.sync_changes(principal, cal_id, synctoken)
.await
} else {
self.cal_store
.sync_changes(principal, cal_id, synctoken)
.await
} }
Ok(calendars)
} }
#[inline]
async fn calendar_metadata(
&self,
principal: &str,
cal_id: &str,
) -> Result<crate::CollectionMetadata, Error> {
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<Vec<CalendarObject>, 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<Vec<CalendarObject>, 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<CalendarObject>,
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<Vec<Calendar>, 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 { fn is_read_only(&self, cal_id: &str) -> bool {
if cal_id.starts_with(BIRTHDAYS_PREFIX) { self.store_for_id(cal_id).is_read_only(cal_id)
self.birthday_store.is_read_only(cal_id)
} else {
self.cal_store.is_read_only(cal_id)
}
} }
} }

View File

@@ -1,5 +1,6 @@
use crate::{ use crate::{
Addressbook, AddressbookStore, Calendar, CalendarStore, Error, calendar::CalendarMetadata, Addressbook, AddressbookStore, Calendar, CalendarStore, Error, calendar::CalendarMetadata,
combined_calendar_store::PrefixedCalendarStore,
}; };
use async_trait::async_trait; use async_trait::async_trait;
use derive_more::derive::Constructor; use derive_more::derive::Constructor;
@@ -12,6 +13,10 @@ pub(crate) const BIRTHDAYS_PREFIX: &str = "_birthdays_";
#[derive(Constructor, Clone)] #[derive(Constructor, Clone)]
pub struct ContactBirthdayStore<AS: AddressbookStore>(Arc<AS>); pub struct ContactBirthdayStore<AS: AddressbookStore>(Arc<AS>);
impl<AS: AddressbookStore> PrefixedCalendarStore for ContactBirthdayStore<AS> {
const PREFIX: &'static str = BIRTHDAYS_PREFIX;
}
fn birthday_calendar(addressbook: Addressbook) -> Calendar { fn birthday_calendar(addressbook: Addressbook) -> Calendar {
Calendar { Calendar {
principal: addressbook.principal, principal: addressbook.principal,

View File

@@ -40,10 +40,9 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
dav_push_enabled: bool, dav_push_enabled: bool,
session_cookie_samesite_strict: bool, session_cookie_samesite_strict: bool,
) -> Router<()> { ) -> Router<()> {
let combined_cal_store = Arc::new(CombinedCalendarStore::new( let birthday_store = Arc::new(ContactBirthdayStore::new(addr_store.clone()));
cal_store.clone(), let combined_cal_store =
ContactBirthdayStore::new(addr_store.clone()).into(), Arc::new(CombinedCalendarStore::new(cal_store.clone()).with_store(birthday_store));
));
let mut router = Router::new() let mut router = Router::new()
.merge(caldav_router( .merge(caldav_router(