From b3a7806139d851d97dfcd2eaf45c1f927e21d424 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Mon, 30 Sep 2024 19:35:54 +0200 Subject: [PATCH] Migrate from Event type to CalendarObject This is preparation to support other calendar components like VTODO and VJOURNAL --- .../methods/report/calendar_multiget.rs | 12 +- .../calendar/methods/report/calendar_query.rs | 12 +- .../methods/report/sync_collection.rs | 6 +- crates/caldav/src/calendar/resource.rs | 6 +- .../src/{event => calendar_object}/methods.rs | 6 +- .../src/{event => calendar_object}/mod.rs | 0 .../{event => calendar_object}/resource.rs | 38 +++--- crates/caldav/src/lib.rs | 16 ++- crates/store/src/model/event.rs | 103 ++------------ crates/store/src/model/mod.rs | 3 +- crates/store/src/model/object.rs | 129 ++++++++++++++++++ crates/store/src/sqlite_store.rs | 35 +++-- crates/store/src/store.rs | 20 ++- crates/store/tests/test_calendar.rs | 4 +- 14 files changed, 229 insertions(+), 161 deletions(-) rename crates/caldav/src/{event => calendar_object}/methods.rs (94%) rename crates/caldav/src/{event => calendar_object}/mod.rs (100%) rename crates/caldav/src/{event => calendar_object}/resource.rs (69%) create mode 100644 crates/store/src/model/object.rs diff --git a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs index 184defb..0dc70a6 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs @@ -1,5 +1,5 @@ use crate::{ - event::resource::{EventProp, EventResource}, + calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, Error, }; use actix_web::{ @@ -11,7 +11,7 @@ use rustical_dav::{ resource::HandlePropfind, xml::{multistatus::PropstatWrapper, MultistatusElement}, }; -use rustical_store::{model::Event, CalendarStore}; +use rustical_store::{model::object::CalendarObject, CalendarStore}; use serde::Deserialize; use tokio::sync::RwLock; @@ -31,7 +31,7 @@ pub async fn get_events_calendar_multiget( principal: &str, cid: &str, store: &RwLock, -) -> Result, Error> { +) -> Result, Error> { // TODO: add proper error results for single events let resource_def = ResourceDef::prefix(prefix).join(&ResourceDef::new("/user/{principal}/{cid}/{uid}")); @@ -54,7 +54,7 @@ pub async fn get_events_calendar_multiget( continue; } let uid = path.get("uid").unwrap(); - result.push(store.get_event(principal, cid, uid).await?); + result.push(store.get_object(principal, cid, uid).await?); } Ok(result) @@ -67,7 +67,7 @@ pub async fn handle_calendar_multiget( principal: &str, cid: &str, cal_store: &RwLock, -) -> Result, String>, Error> { +) -> Result, String>, Error> { let events = get_events_calendar_multiget(&cal_multiget, prefix, principal, cid, cal_store).await?; @@ -87,7 +87,7 @@ pub async fn handle_calendar_multiget( for event in events { let path = format!("{}/{}", req.path(), event.get_uid()); responses.push( - EventResource::from(event) + CalendarObjectResource::from(event) .propfind(prefix, &path, props.clone()) .await?, ); diff --git a/crates/caldav/src/calendar/methods/report/calendar_query.rs b/crates/caldav/src/calendar/methods/report/calendar_query.rs index a34b1c5..350151e 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query.rs @@ -4,12 +4,12 @@ use rustical_dav::{ resource::HandlePropfind, xml::{multistatus::PropstatWrapper, MultistatusElement}, }; -use rustical_store::{model::Event, CalendarStore}; +use rustical_store::{model::object::CalendarObject, CalendarStore}; use serde::Deserialize; use tokio::sync::RwLock; use crate::{ - event::resource::{EventProp, EventResource}, + calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, Error, }; @@ -95,9 +95,9 @@ pub async fn get_events_calendar_query( principal: &str, cid: &str, store: &RwLock, -) -> Result, Error> { +) -> Result, Error> { // TODO: Implement filtering - Ok(store.read().await.get_events(principal, cid).await?) + Ok(store.read().await.get_objects(principal, cid).await?) } pub async fn handle_calendar_query( @@ -107,7 +107,7 @@ pub async fn handle_calendar_query( principal: &str, cid: &str, cal_store: &RwLock, -) -> Result, String>, Error> { +) -> Result, String>, Error> { let events = get_events_calendar_query(&cal_query, principal, cid, cal_store).await?; let props = match cal_query.prop { @@ -126,7 +126,7 @@ pub async fn handle_calendar_query( for event in events { let path = format!("{}/{}", req.path(), event.get_uid()); responses.push( - EventResource::from(event) + CalendarObjectResource::from(event) .propfind(prefix, &path, props.clone()) .await?, ); diff --git a/crates/caldav/src/calendar/methods/report/sync_collection.rs b/crates/caldav/src/calendar/methods/report/sync_collection.rs index 596c9d8..c1d04b4 100644 --- a/crates/caldav/src/calendar/methods/report/sync_collection.rs +++ b/crates/caldav/src/calendar/methods/report/sync_collection.rs @@ -15,7 +15,7 @@ use serde::Deserialize; use tokio::sync::RwLock; use crate::{ - event::resource::{EventProp, EventResource}, + calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, Error, }; @@ -49,7 +49,7 @@ pub async fn handle_sync_collection( principal: &str, cid: &str, cal_store: &RwLock, -) -> Result, String>, Error> { +) -> Result, String>, Error> { let props = match sync_collection.prop { PropfindType::Allprop => { vec!["allprop".to_owned()] @@ -73,7 +73,7 @@ pub async fn handle_sync_collection( for event in new_events { let path = format!("{}/{}", req.path(), event.get_uid()); responses.push( - EventResource::from(event) + CalendarObjectResource::from(event) .propfind(prefix, &path, props.clone()) .await?, ); diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index 2fd0c87..2094d4b 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -1,4 +1,4 @@ -use crate::event::resource::EventResource; +use crate::calendar_object::resource::CalendarObjectResource; use crate::Error; use actix_web::{web::Data, HttpRequest}; use async_trait::async_trait; @@ -218,7 +218,7 @@ impl Resource for CalendarResource { #[async_trait(?Send)] impl ResourceService for CalendarResourceService { - type MemberType = EventResource; + type MemberType = CalendarObjectResource; type PathComponents = (String, String); // principal, calendar_id type Resource = CalendarResource; type Error = Error; @@ -243,7 +243,7 @@ impl ResourceService for CalendarResourceService { .cal_store .read() .await - .get_events(&self.principal, &self.calendar_id) + .get_objects(&self.principal, &self.calendar_id) .await? .into_iter() .map(|event| (format!("{}/{}", self.path, &event.get_uid()), event.into())) diff --git a/crates/caldav/src/event/methods.rs b/crates/caldav/src/calendar_object/methods.rs similarity index 94% rename from crates/caldav/src/event/methods.rs rename to crates/caldav/src/calendar_object/methods.rs index 5f1e68c..6d64281 100644 --- a/crates/caldav/src/event/methods.rs +++ b/crates/caldav/src/calendar_object/methods.rs @@ -36,7 +36,7 @@ pub async fn get_event( .store .read() .await - .get_event(&principal, &cid, &uid) + .get_object(&principal, &cid, &uid) .await?; Ok(HttpResponse::Ok() @@ -79,7 +79,7 @@ pub async fn put_event( if Some(&HeaderValue::from_static("*")) == req.headers().get(header::IF_NONE_MATCH) { // Only write if not existing - match store.get_event(&principal, &cid, &uid).await { + match store.get_object(&principal, &cid, &uid).await { Ok(_) => { // Conflict return Ok(HttpResponse::Conflict().body("Resource with this URI already existing")); @@ -94,7 +94,7 @@ pub async fn put_event( } } - store.put_event(principal, cid, uid, body).await?; + store.put_object(principal, cid, uid, body).await?; Ok(HttpResponse::Created().body("")) } diff --git a/crates/caldav/src/event/mod.rs b/crates/caldav/src/calendar_object/mod.rs similarity index 100% rename from crates/caldav/src/event/mod.rs rename to crates/caldav/src/calendar_object/mod.rs diff --git a/crates/caldav/src/event/resource.rs b/crates/caldav/src/calendar_object/resource.rs similarity index 69% rename from crates/caldav/src/event/resource.rs rename to crates/caldav/src/calendar_object/resource.rs index db36514..98191ac 100644 --- a/crates/caldav/src/event/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -3,14 +3,14 @@ use actix_web::{web::Data, HttpRequest}; use async_trait::async_trait; use derive_more::derive::{From, Into}; use rustical_dav::resource::{InvalidProperty, Resource, ResourceService}; -use rustical_store::model::Event; +use rustical_store::model::object::CalendarObject; use rustical_store::CalendarStore; use serde::{Deserialize, Serialize}; use std::sync::Arc; use strum::{EnumString, VariantNames}; use tokio::sync::RwLock; -pub struct EventResourceService { +pub struct CalendarObjectResourceService { pub cal_store: Arc>, pub path: String, pub principal: String, @@ -20,7 +20,7 @@ pub struct EventResourceService { #[derive(EnumString, Debug, VariantNames, Clone)] #[strum(serialize_all = "kebab-case")] -pub enum EventPropName { +pub enum CalendarObjectPropName { Getetag, CalendarData, Getcontenttype, @@ -28,7 +28,7 @@ pub enum EventPropName { #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] -pub enum EventProp { +pub enum CalendarObjectProp { Getetag(String), #[serde(rename = "C:calendar-data")] CalendarData(String), @@ -37,36 +37,38 @@ pub enum EventProp { Invalid, } -impl InvalidProperty for EventProp { +impl InvalidProperty for CalendarObjectProp { fn invalid_property(&self) -> bool { matches!(self, Self::Invalid) } } #[derive(Clone, From, Into)] -pub struct EventResource(Event); +pub struct CalendarObjectResource(CalendarObject); -impl Resource for EventResource { - type PropName = EventPropName; - type Prop = EventProp; +impl Resource for CalendarObjectResource { + type PropName = CalendarObjectPropName; + type Prop = CalendarObjectProp; type Error = Error; fn get_prop(&self, _prefix: &str, prop: Self::PropName) -> Result { Ok(match prop { - EventPropName::Getetag => EventProp::Getetag(self.0.get_etag()), - EventPropName::CalendarData => EventProp::CalendarData(self.0.get_ics().to_owned()), - EventPropName::Getcontenttype => { - EventProp::Getcontenttype("text/calendar;charset=utf-8".to_owned()) + CalendarObjectPropName::Getetag => CalendarObjectProp::Getetag(self.0.get_etag()), + CalendarObjectPropName::CalendarData => { + CalendarObjectProp::CalendarData(self.0.get_ics().to_owned()) + } + CalendarObjectPropName::Getcontenttype => { + CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8".to_owned()) } }) } } #[async_trait(?Send)] -impl ResourceService for EventResourceService { +impl ResourceService for CalendarObjectResourceService { type PathComponents = (String, String, String); // principal, calendar, event - type Resource = EventResource; - type MemberType = EventResource; + type Resource = CalendarObjectResource; + type MemberType = CalendarObjectResource; type Error = Error; async fn new( @@ -102,7 +104,7 @@ impl ResourceService for EventResourceService { .cal_store .read() .await - .get_event(&self.principal, &self.cid, &self.uid) + .get_object(&self.principal, &self.cid, &self.uid) .await?; Ok(event.into()) } @@ -115,7 +117,7 @@ impl ResourceService for EventResourceService { self.cal_store .write() .await - .delete_event(&self.principal, &self.cid, &self.uid, use_trashbin) + .delete_object(&self.principal, &self.cid, &self.uid, use_trashbin) .await?; Ok(()) } diff --git a/crates/caldav/src/lib.rs b/crates/caldav/src/lib.rs index a55dbf7..e56333a 100644 --- a/crates/caldav/src/lib.rs +++ b/crates/caldav/src/lib.rs @@ -2,7 +2,7 @@ use actix_web::http::Method; use actix_web::web::{self, Data}; use actix_web::{guard, HttpResponse, Responder}; use calendar::resource::CalendarResourceService; -use event::resource::EventResourceService; +use calendar_object::resource::CalendarObjectResourceService; use principal::PrincipalResourceService; use root::RootResourceService; use rustical_auth::CheckAuthentication; @@ -15,8 +15,8 @@ use std::sync::Arc; use tokio::sync::RwLock; pub mod calendar; +pub mod calendar_object; pub mod error; -pub mod event; pub mod principal; pub mod root; @@ -102,21 +102,23 @@ pub fn configure_dav( web::resource("/{event}") .route( propfind_method() - .to(route_propfind::>), + .to(route_propfind::>), ) .route( proppatch_method() - .to(route_proppatch::>), + .to(route_proppatch::>), ) .route( web::method(Method::DELETE) - .to(route_delete::>), + .to(route_delete::>), ) .route( - web::method(Method::GET).to(event::methods::get_event::), + web::method(Method::GET) + .to(calendar_object::methods::get_event::), ) .route( - web::method(Method::PUT).to(event::methods::put_event::), + web::method(Method::PUT) + .to(calendar_object::methods::put_event::), ), ), ), diff --git a/crates/store/src/model/event.rs b/crates/store/src/model/event.rs index 94b84a0..c482cb0 100644 --- a/crates/store/src/model/event.rs +++ b/crates/store/src/model/event.rs @@ -1,96 +1,21 @@ -use crate::{ - timestamp::{parse_duration, CalDateTime}, - Error, -}; +use std::str::FromStr; + use anyhow::{anyhow, Result}; use chrono::Duration; -use ical::parser::{ical::component::IcalCalendar, Component}; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::{io::BufReader, str::FromStr}; +use ical::{generator::IcalEvent, parser::Component}; + +use crate::timestamp::{parse_duration, CalDateTime}; #[derive(Debug, Clone)] -pub struct Event { - uid: String, - ics: String, - cal: IcalCalendar, +pub struct EventObject { + pub(crate) event: IcalEvent, } -// Custom implementation for Event (de)serialization -impl<'de> Deserialize<'de> for Event { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - #[derive(Deserialize)] - struct Inner { - uid: String, - ics: String, - } - let Inner { uid, ics } = Inner::deserialize(deserializer)?; - Self::from_ics(uid, ics).map_err(serde::de::Error::custom) - } -} -impl Serialize for Event { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - #[derive(Serialize)] - struct Inner { - uid: String, - ics: String, - } - Inner::serialize( - &Inner { - uid: self.get_uid().to_string(), - ics: self.get_ics().to_string(), - }, - serializer, - ) - } -} - -impl Event { - // https://datatracker.ietf.org/doc/html/rfc4791#section-4.1 - // MUST NOT contain more than one calendar objects (VEVENT, VTODO, VJOURNAL) - pub fn from_ics(uid: String, ics: String) -> Result { - let mut parser = ical::IcalParser::new(BufReader::new(ics.as_bytes())); - let cal = parser.next().ok_or(Error::NotFound)??; - if parser.next().is_some() { - return Err(Error::InvalidIcs( - "multiple calendars, only one allowed".to_owned(), - )); - } - if cal.events.len() != 1 { - return Err(Error::InvalidIcs( - "ics calendar must contain exactly one event".to_owned(), - )); - } - let event = Self { uid, cal, ics }; - // Run getters now to validate the input and ensure that they'll work later on - event.get_first_occurence()?; - event.get_last_occurence()?; - Ok(event) - } - pub fn get_uid(&self) -> &str { - &self.uid - } - pub fn get_etag(&self) -> String { - let mut hasher = Sha256::new(); - hasher.update(&self.uid); - hasher.update(self.get_ics()); - format!("{:x}", hasher.finalize()) - } - - pub fn get_ics(&self) -> &str { - &self.ics - } - +impl EventObject { pub fn get_first_occurence(&self) -> Result { // This is safe since we enforce the event's existance in the constructor - let event = self.cal.events.first().unwrap(); - let dtstart = event + let dtstart = self + .event .get_property("DTSTART") .ok_or(anyhow!("DTSTART property missing!"))? .value @@ -101,14 +26,12 @@ impl Event { pub fn get_last_occurence(&self) -> Result { // This is safe since we enforce the event's existence in the constructor - let event = self.cal.events.first().unwrap(); - - if event.get_property("RRULE").is_some() { + if self.event.get_property("RRULE").is_some() { // TODO: understand recurrence rules return Err(anyhow!("event is recurring, we cannot handle that yet")); } - if let Some(dtend_prop) = event.get_property("DTEND") { + if let Some(dtend_prop) = self.event.get_property("DTEND") { let dtend = dtend_prop .value .to_owned() @@ -116,7 +39,7 @@ impl Event { return Ok(CalDateTime::from_str(&dtend)?); } - if let Some(dtend_prop) = event.get_property("DURATION") { + if let Some(dtend_prop) = self.event.get_property("DURATION") { let duration = dtend_prop .value .to_owned() diff --git a/crates/store/src/model/mod.rs b/crates/store/src/model/mod.rs index 53a4e42..553a430 100644 --- a/crates/store/src/model/mod.rs +++ b/crates/store/src/model/mod.rs @@ -1,5 +1,6 @@ pub mod calendar; pub mod event; +pub mod object; pub use calendar::Calendar; -pub use event::Event; +pub use object::CalendarObject; diff --git a/crates/store/src/model/object.rs b/crates/store/src/model/object.rs new file mode 100644 index 0000000..606cdb5 --- /dev/null +++ b/crates/store/src/model/object.rs @@ -0,0 +1,129 @@ +use super::event::EventObject; +use crate::Error; +use anyhow::Result; +use ical::parser::ical::component::IcalTodo; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use std::io::BufReader; + +#[derive(Debug, Clone)] +// specified in https://datatracker.ietf.org/doc/html/rfc5545#section-3.6 +pub enum CalendarObjectType { + Event, + Journal, + Todo, +} + +#[derive(Debug, Clone)] +pub struct TodoObject { + todo: IcalTodo, +} + +#[derive(Debug, Clone)] +pub enum CalendarObjectComponent { + Event(EventObject), + Todo(TodoObject), +} + +#[derive(Debug, Clone)] +pub struct CalendarObject { + uid: String, + ics: String, + data: CalendarObjectComponent, +} + +// Custom implementation for CalendarObject (de)serialization +impl<'de> Deserialize<'de> for CalendarObject { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Inner { + uid: String, + ics: String, + } + let Inner { uid, ics } = Inner::deserialize(deserializer)?; + Self::from_ics(uid, ics).map_err(serde::de::Error::custom) + } +} + +impl Serialize for CalendarObject { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + #[derive(Serialize)] + struct Inner { + uid: String, + ics: String, + } + Inner::serialize( + &Inner { + uid: self.get_uid().to_string(), + ics: self.get_ics().to_string(), + }, + serializer, + ) + } +} + +impl CalendarObject { + pub fn from_ics(uid: String, ics: String) -> Result { + let mut parser = ical::IcalParser::new(BufReader::new(ics.as_bytes())); + let cal = parser.next().ok_or(Error::NotFound)??; + if parser.next().is_some() { + return Err(Error::InvalidIcs( + "multiple calendars, only one allowed".to_owned(), + )); + } + if cal.events.len() + + cal.alarms.len() + + cal.todos.len() + + cal.journals.len() + + cal.free_busys.len() + != 1 + { + // https://datatracker.ietf.org/doc/html/rfc4791#section-4.1 + return Err(Error::InvalidIcs( + "iCalendar object is only allowed to have exactly one component".to_owned(), + )); + } + + if let Some(event) = cal.events.first() { + return Ok(CalendarObject { + uid, + ics, + data: CalendarObjectComponent::Event(EventObject { + event: event.clone(), + }), + }); + } + if let Some(todo) = cal.todos.first() { + return Ok(CalendarObject { + uid, + ics, + data: CalendarObjectComponent::Todo(TodoObject { todo: todo.clone() }), + }); + } + + Err(Error::InvalidIcs( + "iCalendar component type not supported :(".to_owned(), + )) + } + + pub fn get_uid(&self) -> &str { + &self.uid + } + pub fn get_etag(&self) -> String { + // TODO: Probably save it in the database so we don't have to recompute every time? + let mut hasher = Sha256::new(); + hasher.update(&self.uid); + hasher.update(self.get_ics()); + format!("{:x}", hasher.finalize()) + } + + pub fn get_ics(&self) -> &str { + &self.ics + } +} diff --git a/crates/store/src/sqlite_store.rs b/crates/store/src/sqlite_store.rs index 873d951..fa99b97 100644 --- a/crates/store/src/sqlite_store.rs +++ b/crates/store/src/sqlite_store.rs @@ -1,5 +1,5 @@ +use crate::model::object::CalendarObject; use crate::model::Calendar; -use crate::model::Event; use crate::{CalendarStore, Error}; use anyhow::Result; use async_trait::async_trait; @@ -19,16 +19,16 @@ impl SqliteCalendarStore { } #[derive(Debug, Clone)] -struct EventRow { +struct CalendarObjectRow { uid: String, ics: String, } -impl TryFrom for Event { +impl TryFrom for CalendarObject { type Error = Error; - fn try_from(value: EventRow) -> Result { - Event::from_ics(value.uid, value.ics) + fn try_from(value: CalendarObjectRow) -> Result { + CalendarObject::from_ics(value.uid, value.ics) } } @@ -186,9 +186,9 @@ impl CalendarStore for SqliteCalendarStore { Ok(()) } - async fn get_events(&self, principal: &str, cid: &str) -> Result, Error> { + async fn get_objects(&self, principal: &str, cid: &str) -> Result, Error> { sqlx::query_as!( - EventRow, + CalendarObjectRow, "SELECT uid, ics FROM events WHERE principal = ? AND cid = ? AND deleted_at IS NULL", principal, cid @@ -200,9 +200,14 @@ impl CalendarStore for SqliteCalendarStore { .collect() } - async fn get_event(&self, principal: &str, cid: &str, uid: &str) -> Result { + async fn get_object( + &self, + principal: &str, + cid: &str, + uid: &str, + ) -> Result { let event = sqlx::query_as!( - EventRow, + CalendarObjectRow, "SELECT uid, ics FROM events WHERE (principal, cid, uid) = (?, ?, ?)", principal, cid, @@ -214,7 +219,7 @@ impl CalendarStore for SqliteCalendarStore { Ok(event) } - async fn put_event( + async fn put_object( &mut self, principal: String, cid: String, @@ -224,7 +229,7 @@ impl CalendarStore for SqliteCalendarStore { let mut tx = self.db.begin().await?; // input validation - Event::from_ics(uid.to_owned(), ics.to_owned())?; + CalendarObject::from_ics(uid.to_owned(), ics.to_owned())?; sqlx::query!( "REPLACE INTO events (principal, cid, uid, ics) VALUES (?, ?, ?, ?)", principal, @@ -247,7 +252,7 @@ impl CalendarStore for SqliteCalendarStore { Ok(()) } - async fn delete_event( + async fn delete_object( &mut self, principal: &str, cid: &str, @@ -285,7 +290,7 @@ impl CalendarStore for SqliteCalendarStore { Ok(()) } - async fn restore_event(&mut self, principal: &str, cid: &str, uid: &str) -> Result<(), Error> { + async fn restore_object(&mut self, principal: &str, cid: &str, uid: &str) -> Result<(), Error> { let mut tx = self.db.begin().await?; sqlx::query!( @@ -314,7 +319,7 @@ impl CalendarStore for SqliteCalendarStore { principal: &str, cid: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error> { + ) -> Result<(Vec, Vec, i64), Error> { struct Row { uid: String, synctoken: i64, @@ -340,7 +345,7 @@ impl CalendarStore for SqliteCalendarStore { .unwrap_or(0); for Row { uid, .. } in changes { - match self.get_event(principal, cid, &uid).await { + match self.get_object(principal, cid, &uid).await { Ok(event) => events.push(event), Err(Error::NotFound) => deleted_events.push(uid), Err(err) => return Err(err), diff --git a/crates/store/src/store.rs b/crates/store/src/store.rs index abf0dc7..d1bfcbf 100644 --- a/crates/store/src/store.rs +++ b/crates/store/src/store.rs @@ -2,7 +2,8 @@ use anyhow::Result; use async_trait::async_trait; use crate::error::Error; -use crate::model::{Calendar, Event}; +use crate::model::object::CalendarObject; +use crate::model::Calendar; #[async_trait] pub trait CalendarStore: Send + Sync + 'static { @@ -29,23 +30,28 @@ pub trait CalendarStore: Send + Sync + 'static { principal: &str, cid: &str, synctoken: i64, - ) -> Result<(Vec, Vec, i64), Error>; + ) -> Result<(Vec, Vec, i64), Error>; - async fn get_events(&self, principal: &str, cid: &str) -> Result, Error>; - async fn get_event(&self, principal: &str, cid: &str, uid: &str) -> Result; - async fn put_event( + async fn get_objects(&self, principal: &str, cid: &str) -> Result, Error>; + async fn get_object( + &self, + principal: &str, + cid: &str, + uid: &str, + ) -> Result; + async fn put_object( &mut self, principal: String, cid: String, uid: String, ics: String, ) -> Result<(), Error>; - async fn delete_event( + async fn delete_object( &mut self, principal: &str, cid: &str, uid: &str, use_trashbin: bool, ) -> Result<(), Error>; - async fn restore_event(&mut self, principal: &str, cid: &str, uid: &str) -> Result<(), Error>; + async fn restore_object(&mut self, principal: &str, cid: &str, uid: &str) -> Result<(), Error>; } diff --git a/crates/store/tests/test_calendar.rs b/crates/store/tests/test_calendar.rs index b346b12..b664553 100644 --- a/crates/store/tests/test_calendar.rs +++ b/crates/store/tests/test_calendar.rs @@ -37,7 +37,7 @@ async fn test_create_event(mut store: CS) { .unwrap(); store - .put_event( + .put_object( "testuser".to_owned(), "test".to_owned(), "asd".to_owned(), @@ -46,7 +46,7 @@ async fn test_create_event(mut store: CS) { .await .unwrap(); - let event = store.get_event("testuser", "test", "asd").await.unwrap(); + let event = store.get_object("testuser", "test", "asd").await.unwrap(); assert_eq!(event.get_ics(), EVENT); assert_eq!(event.get_uid(), "asd"); }