mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 19:22:26 +00:00
rewrite combined calendar store in preparation for sharing
This commit is contained in:
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user