diff options
author | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-05-16 17:38:34 +0200 |
---|---|---|
committer | Quentin Dufour <quentin@deuxfleurs.fr> | 2024-05-16 17:38:34 +0200 |
commit | 32dfd25f570b7a55bf43752684d286be0f6b2dc2 (patch) | |
tree | dd77871cda851bb5795743a3f04be61cf4c3ad61 /aero-proto/src/dav/resource.rs | |
parent | 6b9542088cd1b66af46e95b787493b601accb495 (diff) | |
download | aerogramme-32dfd25f570b7a55bf43752684d286be0f6b2dc2.tar.gz aerogramme-32dfd25f570b7a55bf43752684d286be0f6b2dc2.zip |
format + WIP calendar-query
Diffstat (limited to 'aero-proto/src/dav/resource.rs')
-rw-r--r-- | aero-proto/src/dav/resource.rs | 565 |
1 files changed, 387 insertions, 178 deletions
diff --git a/aero-proto/src/dav/resource.rs b/aero-proto/src/dav/resource.rs index 944c6c8..d65ce38 100644 --- a/aero-proto/src/dav/resource.rs +++ b/aero-proto/src/dav/resource.rs @@ -2,23 +2,32 @@ use std::sync::Arc; type ArcUser = std::sync::Arc<User>; use anyhow::{anyhow, Result}; -use futures::stream::{TryStreamExt, StreamExt}; use futures::io::AsyncReadExt; +use futures::stream::{StreamExt, TryStreamExt}; use futures::{future::BoxFuture, future::FutureExt}; -use aero_collections::{user::User, calendar::Calendar, davdag::{BlobId, Etag}}; -use aero_dav::types as dav; -use aero_dav::caltypes as cal; +use aero_collections::{ + calendar::Calendar, + davdag::{BlobId, Etag}, + user::User, +}; use aero_dav::acltypes as acl; -use aero_dav::realization::{All, self as all}; +use aero_dav::caltypes as cal; +use aero_dav::realization::{self as all, All}; +use aero_dav::types as dav; -use crate::dav::node::{DavNode, PutPolicy, Content}; use super::node::PropertyStream; +use crate::dav::node::{Content, DavNode, PutPolicy}; #[derive(Clone)] pub(crate) struct RootNode {} impl DavNode for RootNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { + fn fetch<'a>( + &self, + user: &'a ArcUser, + path: &'a [&str], + create: bool, + ) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { if path.len() == 0 { let this = self.clone(); return async { Ok(Box::new(this) as Box<dyn DavNode>) }.boxed(); @@ -34,7 +43,7 @@ impl DavNode for RootNode { } fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>> { - async { vec![Box::new(HomeNode { }) as Box<dyn DavNode>] }.boxed() + async { vec![Box::new(HomeNode {}) as Box<dyn DavNode>] }.boxed() } fn path(&self, user: &ArcUser) -> String { @@ -46,33 +55,53 @@ impl DavNode for RootNode { dav::PropertyRequest::DisplayName, dav::PropertyRequest::ResourceType, dav::PropertyRequest::GetContentType, - dav::PropertyRequest::Extension(all::PropertyRequest::Acl(acl::PropertyRequest::CurrentUserPrincipal)), + dav::PropertyRequest::Extension(all::PropertyRequest::Acl( + acl::PropertyRequest::CurrentUserPrincipal, + )), ]) } fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> { let user = user.clone(); - futures::stream::iter(prop.0).map(move |n| { - let prop = match n { - dav::PropertyRequest::DisplayName => dav::Property::DisplayName("DAV Root".to_string()), - dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![ - dav::ResourceType::Collection, - ]), - dav::PropertyRequest::GetContentType => dav::Property::GetContentType("httpd/unix-directory".into()), - dav::PropertyRequest::Extension(all::PropertyRequest::Acl(acl::PropertyRequest::CurrentUserPrincipal)) => - dav::Property::Extension(all::Property::Acl(acl::Property::CurrentUserPrincipal(acl::User::Authenticated(dav::Href(HomeNode{}.path(&user)))))), - v => return Err(v), - }; - Ok(prop) - }).boxed() + futures::stream::iter(prop.0) + .map(move |n| { + let prop = match n { + dav::PropertyRequest::DisplayName => { + dav::Property::DisplayName("DAV Root".to_string()) + } + dav::PropertyRequest::ResourceType => { + dav::Property::ResourceType(vec![dav::ResourceType::Collection]) + } + dav::PropertyRequest::GetContentType => { + dav::Property::GetContentType("httpd/unix-directory".into()) + } + dav::PropertyRequest::Extension(all::PropertyRequest::Acl( + acl::PropertyRequest::CurrentUserPrincipal, + )) => dav::Property::Extension(all::Property::Acl( + acl::Property::CurrentUserPrincipal(acl::User::Authenticated(dav::Href( + HomeNode {}.path(&user), + ))), + )), + v => return Err(v), + }; + Ok(prop) + }) + .boxed() } - fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { + fn put<'a>( + &'a self, + _policy: PutPolicy, + stream: Content<'a>, + ) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported)).boxed() } - fn content(&self) -> Content<'static> { - futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed() + fn content<'a>(&self) -> Content<'a> { + futures::stream::once(futures::future::err(std::io::Error::from( + std::io::ErrorKind::Unsupported, + ))) + .boxed() } fn content_type(&self) -> &str { @@ -91,29 +120,37 @@ impl DavNode for RootNode { #[derive(Clone)] pub(crate) struct HomeNode {} impl DavNode for HomeNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { + fn fetch<'a>( + &self, + user: &'a ArcUser, + path: &'a [&str], + create: bool, + ) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box<dyn DavNode>; - return async { Ok(node) }.boxed() + return async { Ok(node) }.boxed(); } if path[0] == "calendar" { return async move { let child = Box::new(CalendarListNode::new(user).await?); child.fetch(user, &path[1..], create).await - }.boxed(); + } + .boxed(); } - + //@NOTE: we can't create a node at this level async { Err(anyhow!("Not found")) }.boxed() } fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>> { - async { - CalendarListNode::new(user).await + async { + CalendarListNode::new(user) + .await .map(|c| vec![Box::new(c) as Box<dyn DavNode>]) - .unwrap_or(vec![]) - }.boxed() + .unwrap_or(vec![]) + } + .boxed() } fn path(&self, user: &ArcUser) -> String { @@ -125,38 +162,58 @@ impl DavNode for HomeNode { dav::PropertyRequest::DisplayName, dav::PropertyRequest::ResourceType, dav::PropertyRequest::GetContentType, - dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarHomeSet)), + dav::PropertyRequest::Extension(all::PropertyRequest::Cal( + cal::PropertyRequest::CalendarHomeSet, + )), ]) } fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> { let user = user.clone(); - futures::stream::iter(prop.0).map(move |n| { - let prop = match n { - dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} home", user.username)), - dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![ - dav::ResourceType::Collection, - dav::ResourceType::Extension(all::ResourceType::Acl(acl::ResourceType::Principal)), - ]), - dav::PropertyRequest::GetContentType => dav::Property::GetContentType("httpd/unix-directory".into()), - dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarHomeSet)) => - dav::Property::Extension(all::Property::Cal(cal::Property::CalendarHomeSet(dav::Href( - //@FIXME we are hardcoding the calendar path, instead we would want to use - //objects - format!("/{}/calendar/", user.username) - )))), - v => return Err(v), - }; - Ok(prop) - }).boxed() + futures::stream::iter(prop.0) + .map(move |n| { + let prop = match n { + dav::PropertyRequest::DisplayName => { + dav::Property::DisplayName(format!("{} home", user.username)) + } + dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![ + dav::ResourceType::Collection, + dav::ResourceType::Extension(all::ResourceType::Acl( + acl::ResourceType::Principal, + )), + ]), + dav::PropertyRequest::GetContentType => { + dav::Property::GetContentType("httpd/unix-directory".into()) + } + dav::PropertyRequest::Extension(all::PropertyRequest::Cal( + cal::PropertyRequest::CalendarHomeSet, + )) => dav::Property::Extension(all::Property::Cal( + cal::Property::CalendarHomeSet(dav::Href( + //@FIXME we are hardcoding the calendar path, instead we would want to use + //objects + format!("/{}/calendar/", user.username), + )), + )), + v => return Err(v), + }; + Ok(prop) + }) + .boxed() } - fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { + fn put<'a>( + &'a self, + _policy: PutPolicy, + stream: Content<'a>, + ) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported)).boxed() } - - fn content(&self) -> Content<'static> { - futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed() + + fn content<'a>(&self) -> Content<'a> { + futures::stream::once(futures::future::err(std::io::Error::from( + std::io::ErrorKind::Unsupported, + ))) + .boxed() } fn content_type(&self) -> &str { @@ -183,7 +240,12 @@ impl CalendarListNode { } } impl DavNode for CalendarListNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { + fn fetch<'a>( + &self, + user: &'a ArcUser, + path: &'a [&str], + create: bool, + ) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box<dyn DavNode>; return async { Ok(node) }.boxed(); @@ -191,13 +253,18 @@ impl DavNode for CalendarListNode { async move { //@FIXME: we should create a node if the open returns a "not found". - let cal = user.calendars.open(user, path[0]).await?.ok_or(anyhow!("Not found"))?; - let child = Box::new(CalendarNode { + let cal = user + .calendars + .open(user, path[0]) + .await? + .ok_or(anyhow!("Not found"))?; + let child = Box::new(CalendarNode { col: cal, - calname: path[0].to_string() + calname: path[0].to_string(), }); child.fetch(user, &path[1..], create).await - }.boxed() + } + .boxed() } fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>> { @@ -206,18 +273,23 @@ impl DavNode for CalendarListNode { //@FIXME maybe we want to be lazy here?! futures::stream::iter(list.iter()) .filter_map(|name| async move { - user.calendars.open(user, name).await + user.calendars + .open(user, name) + .await .ok() .flatten() .map(|v| (name, v)) }) - .map(|(name, cal)| Box::new(CalendarNode { - col: cal, - calname: name.to_string(), - }) as Box<dyn DavNode>) + .map(|(name, cal)| { + Box::new(CalendarNode { + col: cal, + calname: name.to_string(), + }) as Box<dyn DavNode> + }) .collect::<Vec<Box<dyn DavNode>>>() .await - }.boxed() + } + .boxed() } fn path(&self, user: &ArcUser) -> String { @@ -234,23 +306,38 @@ impl DavNode for CalendarListNode { fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> { let user = user.clone(); - futures::stream::iter(prop.0).map(move |n| { - let prop = match n { - dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} calendars", user.username)), - dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![dav::ResourceType::Collection]), - dav::PropertyRequest::GetContentType => dav::Property::GetContentType("httpd/unix-directory".into()), - v => return Err(v), - }; - Ok(prop) - }).boxed() + futures::stream::iter(prop.0) + .map(move |n| { + let prop = match n { + dav::PropertyRequest::DisplayName => { + dav::Property::DisplayName(format!("{} calendars", user.username)) + } + dav::PropertyRequest::ResourceType => { + dav::Property::ResourceType(vec![dav::ResourceType::Collection]) + } + dav::PropertyRequest::GetContentType => { + dav::Property::GetContentType("httpd/unix-directory".into()) + } + v => return Err(v), + }; + Ok(prop) + }) + .boxed() } - fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { + fn put<'a>( + &'a self, + _policy: PutPolicy, + stream: Content<'a>, + ) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported)).boxed() } - fn content(&self) -> Content<'static> { - futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed() + fn content<'a>(&self) -> Content<'a> { + futures::stream::once(futures::future::err(std::io::Error::from( + std::io::ErrorKind::Unsupported, + ))) + .boxed() } fn content_type(&self) -> &str { @@ -272,17 +359,22 @@ pub(crate) struct CalendarNode { calname: String, } impl DavNode for CalendarNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { + fn fetch<'a>( + &self, + user: &'a ArcUser, + path: &'a [&str], + create: bool, + ) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box<dyn DavNode>; - return async { Ok(node) }.boxed() + return async { Ok(node) }.boxed(); } let col = self.col.clone(); let calname = self.calname.clone(); async move { match (col.dag().await.idx_by_filename.get(path[0]), create) { - (Some(blob_id), _) => { + (Some(blob_id), _) => { let child = Box::new(EventNode { col: col.clone(), calname, @@ -290,7 +382,7 @@ impl DavNode for CalendarNode { blob_id: *blob_id, }); child.fetch(user, &path[1..], create).await - }, + } (None, true) => { let child = Box::new(CreateEventNode { col: col.clone(), @@ -298,11 +390,11 @@ impl DavNode for CalendarNode { filename: path[0].to_string(), }); child.fetch(user, &path[1..], create).await - }, + } _ => Err(anyhow!("Not found")), } - - }.boxed() + } + .boxed() } fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>> { @@ -310,15 +402,21 @@ impl DavNode for CalendarNode { let calname = self.calname.clone(); async move { - col.dag().await.idx_by_filename.iter().map(|(filename, blob_id)| { - Box::new(EventNode { - col: col.clone(), - calname: calname.clone(), - filename: filename.to_string(), - blob_id: *blob_id, - }) as Box<dyn DavNode> - }).collect() - }.boxed() + col.dag() + .await + .idx_by_filename + .iter() + .map(|(filename, blob_id)| { + Box::new(EventNode { + col: col.clone(), + calname: calname.clone(), + filename: filename.to_string(), + blob_id: *blob_id, + }) as Box<dyn DavNode> + }) + .collect() + } + .boxed() } fn path(&self, user: &ArcUser) -> String { @@ -330,38 +428,58 @@ impl DavNode for CalendarNode { dav::PropertyRequest::DisplayName, dav::PropertyRequest::ResourceType, dav::PropertyRequest::GetContentType, - dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::SupportedCalendarComponentSet)), + dav::PropertyRequest::Extension(all::PropertyRequest::Cal( + cal::PropertyRequest::SupportedCalendarComponentSet, + )), ]) } fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> { let calname = self.calname.to_string(); - futures::stream::iter(prop.0).map(move |n| { - let prop = match n { - dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} calendar", calname)), - dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![ - dav::ResourceType::Collection, - dav::ResourceType::Extension(all::ResourceType::Cal(cal::ResourceType::Calendar)), - ]), - //dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())), - //@FIXME seems wrong but seems to be what Thunderbird expects... - dav::PropertyRequest::GetContentType => dav::Property::GetContentType("text/calendar".into()), - dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::SupportedCalendarComponentSet)) - => dav::Property::Extension(all::Property::Cal(cal::Property::SupportedCalendarComponentSet(vec![ - cal::CompSupport(cal::Component::VEvent), - ]))), - v => return Err(v), - }; - Ok(prop) - }).boxed() + futures::stream::iter(prop.0) + .map(move |n| { + let prop = match n { + dav::PropertyRequest::DisplayName => { + dav::Property::DisplayName(format!("{} calendar", calname)) + } + dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![ + dav::ResourceType::Collection, + dav::ResourceType::Extension(all::ResourceType::Cal( + cal::ResourceType::Calendar, + )), + ]), + //dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())), + //@FIXME seems wrong but seems to be what Thunderbird expects... + dav::PropertyRequest::GetContentType => { + dav::Property::GetContentType("text/calendar".into()) + } + dav::PropertyRequest::Extension(all::PropertyRequest::Cal( + cal::PropertyRequest::SupportedCalendarComponentSet, + )) => dav::Property::Extension(all::Property::Cal( + cal::Property::SupportedCalendarComponentSet(vec![cal::CompSupport( + cal::Component::VEvent, + )]), + )), + v => return Err(v), + }; + Ok(prop) + }) + .boxed() } - fn put<'a>(&'a self, _policy: PutPolicy, _stream: Content<'a>) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { + fn put<'a>( + &'a self, + _policy: PutPolicy, + _stream: Content<'a>, + ) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported)).boxed() } - fn content<'a>(&'a self) -> Content<'static> { - futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed() + fn content<'a>(&self) -> Content<'a> { + futures::stream::once(futures::future::err(std::io::Error::from( + std::io::ErrorKind::Unsupported, + ))) + .boxed() } fn content_type(&self) -> &str { @@ -386,13 +504,23 @@ pub(crate) struct EventNode { } impl DavNode for EventNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { + fn fetch<'a>( + &self, + user: &'a ArcUser, + path: &'a [&str], + create: bool, + ) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box<dyn DavNode>; - return async { Ok(node) }.boxed() + return async { Ok(node) }.boxed(); } - async { Err(anyhow!("Not supported: can't create a child on an event node")) }.boxed() + async { + Err(anyhow!( + "Not supported: can't create a child on an event node" + )) + } + .boxed() } fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>> { @@ -400,7 +528,10 @@ impl DavNode for EventNode { } fn path(&self, user: &ArcUser) -> String { - format!("/{}/calendar/{}/{}", user.username, self.calname, self.filename) + format!( + "/{}/calendar/{}/{}", + user.username, self.calname, self.filename + ) } fn supported_properties(&self, user: &ArcUser) -> dav::PropName<All> { @@ -408,66 +539,106 @@ impl DavNode for EventNode { dav::PropertyRequest::DisplayName, dav::PropertyRequest::ResourceType, dav::PropertyRequest::GetEtag, - dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarData(cal::CalendarDataRequest::default()))), + dav::PropertyRequest::Extension(all::PropertyRequest::Cal( + cal::PropertyRequest::CalendarData(cal::CalendarDataRequest::default()), + )), ]) } fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> { let this = self.clone(); - futures::stream::iter(prop.0).then(move |n| { - let this = this.clone(); - - async move { - let prop = match &n { - dav::PropertyRequest::DisplayName => dav::Property::DisplayName(format!("{} event", this.filename)), - dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![]), - dav::PropertyRequest::GetContentType => dav::Property::GetContentType("text/calendar".into()), - dav::PropertyRequest::GetEtag => { - let etag = this.etag().await.ok_or(n.clone())?; - dav::Property::GetEtag(etag) - }, - dav::PropertyRequest::Extension(all::PropertyRequest::Cal(cal::PropertyRequest::CalendarData(_req))) => { - let ics = String::from_utf8(this.col.get(this.blob_id).await.or(Err(n.clone()))?).or(Err(n.clone()))?; - - dav::Property::Extension(all::Property::Cal(cal::Property::CalendarData(cal::CalendarDataPayload { - mime: None, - payload: ics, - }))) - }, - _ => return Err(n), - }; - Ok(prop) - } - }).boxed() - } - - fn put<'a>(&'a self, policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { + futures::stream::iter(prop.0) + .then(move |n| { + let this = this.clone(); + + async move { + let prop = match &n { + dav::PropertyRequest::DisplayName => { + dav::Property::DisplayName(format!("{} event", this.filename)) + } + dav::PropertyRequest::ResourceType => dav::Property::ResourceType(vec![]), + dav::PropertyRequest::GetContentType => { + dav::Property::GetContentType("text/calendar".into()) + } + dav::PropertyRequest::GetEtag => { + let etag = this.etag().await.ok_or(n.clone())?; + dav::Property::GetEtag(etag) + } + dav::PropertyRequest::Extension(all::PropertyRequest::Cal( + cal::PropertyRequest::CalendarData(_req), + )) => { + let ics = String::from_utf8( + this.col.get(this.blob_id).await.or(Err(n.clone()))?, + ) + .or(Err(n.clone()))?; + + dav::Property::Extension(all::Property::Cal( + cal::Property::CalendarData(cal::CalendarDataPayload { + mime: None, + payload: ics, + }), + )) + } + _ => return Err(n), + }; + Ok(prop) + } + }) + .boxed() + } + + fn put<'a>( + &'a self, + policy: PutPolicy, + stream: Content<'a>, + ) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { async { - let existing_etag = self.etag().await.ok_or(std::io::Error::new(std::io::ErrorKind::Other, "Etag error"))?; + let existing_etag = self + .etag() + .await + .ok_or(std::io::Error::new(std::io::ErrorKind::Other, "Etag error"))?; match policy { - PutPolicy::CreateOnly => return Err(std::io::Error::from(std::io::ErrorKind::AlreadyExists)), - PutPolicy::ReplaceEtag(etag) if etag != existing_etag.as_str() => return Err(std::io::Error::from(std::io::ErrorKind::AlreadyExists)), - _ => () + PutPolicy::CreateOnly => { + return Err(std::io::Error::from(std::io::ErrorKind::AlreadyExists)) + } + PutPolicy::ReplaceEtag(etag) if etag != existing_etag.as_str() => { + return Err(std::io::Error::from(std::io::ErrorKind::AlreadyExists)) + } + _ => (), }; //@FIXME for now, our storage interface does not allow streaming, // so we load everything in memory let mut evt = Vec::new(); let mut reader = stream.into_async_read(); - reader.read_to_end(&mut evt).await.or(Err(std::io::Error::from(std::io::ErrorKind::BrokenPipe)))?; - let (_token, entry) = self.col.put(self.filename.as_str(), evt.as_ref()).await.or(Err(std::io::ErrorKind::Interrupted))?; - self.col.opportunistic_sync().await.or(Err(std::io::ErrorKind::ConnectionReset))?; + reader + .read_to_end(&mut evt) + .await + .or(Err(std::io::Error::from(std::io::ErrorKind::BrokenPipe)))?; + let (_token, entry) = self + .col + .put(self.filename.as_str(), evt.as_ref()) + .await + .or(Err(std::io::ErrorKind::Interrupted))?; + self.col + .opportunistic_sync() + .await + .or(Err(std::io::ErrorKind::ConnectionReset))?; Ok(entry.2) - }.boxed() + } + .boxed() } - fn content<'a>(&'a self) -> Content<'static> { + fn content<'a>(&self) -> Content<'a> { //@FIXME for now, our storage interface does not allow streaming, // so we load everything in memory let calendar = self.col.clone(); let blob_id = self.blob_id.clone(); let r = async move { - let content = calendar.get(blob_id).await.or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted))); + let content = calendar + .get(blob_id) + .await + .or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted))); Ok(hyper::body::Bytes::from(content?)) }; futures::stream::once(Box::pin(r)).boxed() @@ -481,8 +652,14 @@ impl DavNode for EventNode { let calendar = self.col.clone(); async move { - calendar.dag().await.table.get(&self.blob_id).map(|(_, _, etag)| etag.to_string()) - }.boxed() + calendar + .dag() + .await + .table + .get(&self.blob_id) + .map(|(_, _, etag)| etag.to_string()) + } + .boxed() } fn delete(&self) -> BoxFuture<std::result::Result<(), std::io::Error>> { @@ -494,12 +671,16 @@ impl DavNode for EventNode { Ok(v) => v, Err(e) => { tracing::error!(err=?e, "delete event node"); - return Err(std::io::Error::from(std::io::ErrorKind::Interrupted)) - }, + return Err(std::io::Error::from(std::io::ErrorKind::Interrupted)); + } }; - calendar.opportunistic_sync().await.or(Err(std::io::ErrorKind::ConnectionReset))?; + calendar + .opportunistic_sync() + .await + .or(Err(std::io::ErrorKind::ConnectionReset))?; Ok(()) - }.boxed() + } + .boxed() } } @@ -510,13 +691,23 @@ pub(crate) struct CreateEventNode { filename: String, } impl DavNode for CreateEventNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { + fn fetch<'a>( + &self, + user: &'a ArcUser, + path: &'a [&str], + create: bool, + ) -> BoxFuture<'a, Result<Box<dyn DavNode>>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box<dyn DavNode>; - return async { Ok(node) }.boxed() + return async { Ok(node) }.boxed(); } - async { Err(anyhow!("Not supported: can't create a child on an event node")) }.boxed() + async { + Err(anyhow!( + "Not supported: can't create a child on an event node" + )) + } + .boxed() } fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>> { @@ -524,33 +715,51 @@ impl DavNode for CreateEventNode { } fn path(&self, user: &ArcUser) -> String { - format!("/{}/calendar/{}/{}", user.username, self.calname, self.filename) + format!( + "/{}/calendar/{}/{}", + user.username, self.calname, self.filename + ) } fn supported_properties(&self, user: &ArcUser) -> dav::PropName<All> { dav::PropName(vec![]) } - fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> { + fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static> { futures::stream::iter(vec![]).boxed() } - fn put<'a>(&'a self, _policy: PutPolicy, stream: Content<'a>) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { + fn put<'a>( + &'a self, + _policy: PutPolicy, + stream: Content<'a>, + ) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>> { //@NOTE: policy might not be needed here: whatever we put, there is no known entries here - + async { //@FIXME for now, our storage interface does not allow for streaming let mut evt = Vec::new(); let mut reader = stream.into_async_read(); reader.read_to_end(&mut evt).await.unwrap(); - let (_token, entry) = self.col.put(self.filename.as_str(), evt.as_ref()).await.or(Err(std::io::ErrorKind::Interrupted))?; - self.col.opportunistic_sync().await.or(Err(std::io::ErrorKind::ConnectionReset))?; + let (_token, entry) = self + .col + .put(self.filename.as_str(), evt.as_ref()) + .await + .or(Err(std::io::ErrorKind::Interrupted))?; + self.col + .opportunistic_sync() + .await + .or(Err(std::io::ErrorKind::ConnectionReset))?; Ok(entry.2) - }.boxed() + } + .boxed() } - fn content(&self) -> Content<'static> { - futures::stream::once(futures::future::err(std::io::Error::from(std::io::ErrorKind::Unsupported))).boxed() + fn content<'a>(&self) -> Content<'a> { + futures::stream::once(futures::future::err(std::io::Error::from( + std::io::ErrorKind::Unsupported, + ))) + .boxed() } fn content_type(&self) -> &str { |