Migrate from Event type to CalendarObject

This is preparation to support other calendar components like VTODO and
VJOURNAL
This commit is contained in:
Lennart
2024-09-30 19:35:54 +02:00
parent 41d68f9ae0
commit b3a7806139
14 changed files with 229 additions and 161 deletions

View File

@@ -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<C: CalendarStore + ?Sized>(
principal: &str,
cid: &str,
store: &RwLock<C>,
) -> Result<Vec<Event>, Error> {
) -> Result<Vec<CalendarObject>, 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<C: CalendarStore + ?Sized>(
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<C: CalendarStore + ?Sized>(
principal: &str,
cid: &str,
cal_store: &RwLock<C>,
) -> Result<MultistatusElement<PropstatWrapper<EventProp>, String>, Error> {
) -> Result<MultistatusElement<PropstatWrapper<CalendarObjectProp>, 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<C: CalendarStore + ?Sized>(
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?,
);

View File

@@ -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<C: CalendarStore + ?Sized>(
principal: &str,
cid: &str,
store: &RwLock<C>,
) -> Result<Vec<Event>, Error> {
) -> Result<Vec<CalendarObject>, 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<C: CalendarStore + ?Sized>(
@@ -107,7 +107,7 @@ pub async fn handle_calendar_query<C: CalendarStore + ?Sized>(
principal: &str,
cid: &str,
cal_store: &RwLock<C>,
) -> Result<MultistatusElement<PropstatWrapper<EventProp>, String>, Error> {
) -> Result<MultistatusElement<PropstatWrapper<CalendarObjectProp>, 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<C: CalendarStore + ?Sized>(
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?,
);

View File

@@ -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<C: CalendarStore + ?Sized>(
principal: &str,
cid: &str,
cal_store: &RwLock<C>,
) -> Result<MultistatusElement<PropstatWrapper<EventProp>, String>, Error> {
) -> Result<MultistatusElement<PropstatWrapper<CalendarObjectProp>, String>, Error> {
let props = match sync_collection.prop {
PropfindType::Allprop => {
vec!["allprop".to_owned()]
@@ -73,7 +73,7 @@ pub async fn handle_sync_collection<C: CalendarStore + ?Sized>(
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?,
);

View File

@@ -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<C: CalendarStore + ?Sized> ResourceService for CalendarResourceService<C> {
type MemberType = EventResource;
type MemberType = CalendarObjectResource;
type PathComponents = (String, String); // principal, calendar_id
type Resource = CalendarResource;
type Error = Error;
@@ -243,7 +243,7 @@ impl<C: CalendarStore + ?Sized> ResourceService for CalendarResourceService<C> {
.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()))

View File

@@ -36,7 +36,7 @@ pub async fn get_event<A: CheckAuthentication, C: CalendarStore + ?Sized>(
.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<A: CheckAuthentication, C: CalendarStore + ?Sized>(
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<A: CheckAuthentication, C: CalendarStore + ?Sized>(
}
}
store.put_event(principal, cid, uid, body).await?;
store.put_object(principal, cid, uid, body).await?;
Ok(HttpResponse::Created().body(""))
}

View File

@@ -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<C: CalendarStore + ?Sized> {
pub struct CalendarObjectResourceService<C: CalendarStore + ?Sized> {
pub cal_store: Arc<RwLock<C>>,
pub path: String,
pub principal: String,
@@ -20,7 +20,7 @@ pub struct EventResourceService<C: CalendarStore + ?Sized> {
#[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<Self::Prop, Self::Error> {
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<C: CalendarStore + ?Sized> ResourceService for EventResourceService<C> {
impl<C: CalendarStore + ?Sized> ResourceService for CalendarObjectResourceService<C> {
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<C: CalendarStore + ?Sized> ResourceService for EventResourceService<C> {
.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<C: CalendarStore + ?Sized> ResourceService for EventResourceService<C> {
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(())
}

View File

@@ -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<A: CheckAuthentication, C: CalendarStore + ?Sized>(
web::resource("/{event}")
.route(
propfind_method()
.to(route_propfind::<A, EventResourceService<C>>),
.to(route_propfind::<A, CalendarObjectResourceService<C>>),
)
.route(
proppatch_method()
.to(route_proppatch::<A, EventResourceService<C>>),
.to(route_proppatch::<A, CalendarObjectResourceService<C>>),
)
.route(
web::method(Method::DELETE)
.to(route_delete::<A, EventResourceService<C>>),
.to(route_delete::<A, CalendarObjectResourceService<C>>),
)
.route(
web::method(Method::GET).to(event::methods::get_event::<A, C>),
web::method(Method::GET)
.to(calendar_object::methods::get_event::<A, C>),
)
.route(
web::method(Method::PUT).to(event::methods::put_event::<A, C>),
web::method(Method::PUT)
.to(calendar_object::methods::put_event::<A, C>),
),
),
),

View File

@@ -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,
}
// Custom implementation for Event (de)serialization
impl<'de> Deserialize<'de> for Event {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
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<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
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<Self, Error> {
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
pub struct EventObject {
pub(crate) event: IcalEvent,
}
impl EventObject {
pub fn get_first_occurence(&self) -> Result<CalDateTime> {
// 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<CalDateTime> {
// 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()

View File

@@ -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;

View File

@@ -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<D>(deserializer: D) -> std::result::Result<Self, D::Error>
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<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
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<Self, Error> {
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
}
}

View File

@@ -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<EventRow> for Event {
impl TryFrom<CalendarObjectRow> for CalendarObject {
type Error = Error;
fn try_from(value: EventRow) -> Result<Self, Error> {
Event::from_ics(value.uid, value.ics)
fn try_from(value: CalendarObjectRow) -> Result<Self, Error> {
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<Vec<Event>, Error> {
async fn get_objects(&self, principal: &str, cid: &str) -> Result<Vec<CalendarObject>, 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<Event, Error> {
async fn get_object(
&self,
principal: &str,
cid: &str,
uid: &str,
) -> Result<CalendarObject, Error> {
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<Event>, Vec<String>, i64), Error> {
) -> Result<(Vec<CalendarObject>, Vec<String>, 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),

View File

@@ -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<Event>, Vec<String>, i64), Error>;
) -> Result<(Vec<CalendarObject>, Vec<String>, i64), Error>;
async fn get_events(&self, principal: &str, cid: &str) -> Result<Vec<Event>, Error>;
async fn get_event(&self, principal: &str, cid: &str, uid: &str) -> Result<Event, Error>;
async fn put_event(
async fn get_objects(&self, principal: &str, cid: &str) -> Result<Vec<CalendarObject>, Error>;
async fn get_object(
&self,
principal: &str,
cid: &str,
uid: &str,
) -> Result<CalendarObject, Error>;
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>;
}

View File

@@ -37,7 +37,7 @@ async fn test_create_event<CS: CalendarStore>(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<CS: CalendarStore>(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");
}