diff options
Diffstat (limited to 'aero-proto/src/dav/node.rs')
-rw-r--r-- | aero-proto/src/dav/node.rs | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/aero-proto/src/dav/node.rs b/aero-proto/src/dav/node.rs new file mode 100644 index 0000000..3af3b81 --- /dev/null +++ b/aero-proto/src/dav/node.rs @@ -0,0 +1,145 @@ +use anyhow::Result; +use futures::future::{BoxFuture, FutureExt}; +use futures::stream::{BoxStream, StreamExt}; +use hyper::body::Bytes; + +use aero_collections::davdag::{Etag, Token}; +use aero_dav::realization::All; +use aero_dav::types as dav; + +use super::controller::ArcUser; + +pub(crate) type Content<'a> = BoxStream<'a, std::result::Result<Bytes, std::io::Error>>; +pub(crate) type PropertyStream<'a> = + BoxStream<'a, std::result::Result<dav::Property<All>, dav::PropertyRequest<All>>>; + +pub(crate) enum PutPolicy { + OverwriteAll, + CreateOnly, + ReplaceEtag(String), +} + +/// A DAV node should implement the following methods +/// @FIXME not satisfied by BoxFutures but I have no better idea currently +pub(crate) trait DavNode: Send { + // recurence, filesystem hierarchy + /// This node direct children + fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>>; + /// Recursively fetch a child (progress inside the filesystem hierarchy) + fn fetch<'a>( + &self, + user: &'a ArcUser, + path: &'a [&str], + create: bool, + ) -> BoxFuture<'a, Result<Box<dyn DavNode>>>; + + // node properties + /// Get the path + fn path(&self, user: &ArcUser) -> String; + /// Get the supported WebDAV properties + fn supported_properties(&self, user: &ArcUser) -> dav::PropName<All>; + /// Get the values for the given properties + fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> PropertyStream<'static>; + /// Get the value of the DAV header to return + fn dav_header(&self) -> String; + + /// Put an element (create or update) + fn put<'a>( + &'a self, + policy: PutPolicy, + stream: Content<'a>, + ) -> BoxFuture<'a, std::result::Result<Etag, std::io::Error>>; + /// Content type of the element + fn content_type(&self) -> &str; + /// Get ETag + fn etag(&self) -> BoxFuture<Option<Etag>>; + /// Get content + fn content<'a>(&self) -> Content<'a>; + /// Delete + fn delete(&self) -> BoxFuture<std::result::Result<(), std::io::Error>>; + /// Sync + fn diff<'a>( + &self, + sync_token: Option<Token>, + ) -> BoxFuture< + 'a, + std::result::Result<(Token, Vec<Box<dyn DavNode>>, Vec<dav::Href>), std::io::Error>, + >; + + /// Utility function to get a propname response from a node + fn response_propname(&self, user: &ArcUser) -> dav::Response<All> { + dav::Response { + status_or_propstat: dav::StatusOrPropstat::PropStat( + dav::Href(self.path(user)), + vec![dav::PropStat { + status: dav::Status(hyper::StatusCode::OK), + prop: dav::AnyProp( + self.supported_properties(user) + .0 + .into_iter() + .map(dav::AnyProperty::Request) + .collect(), + ), + error: None, + responsedescription: None, + }], + ), + error: None, + location: None, + responsedescription: None, + } + } + + /// Utility function to get a prop response from a node & a list of propname + fn response_props( + &self, + user: &ArcUser, + props: dav::PropName<All>, + ) -> BoxFuture<'static, dav::Response<All>> { + //@FIXME we should make the DAV parsed object a stream... + let mut result_stream = self.properties(user, props); + let path = self.path(user); + + async move { + let mut prop_desc = vec![]; + let (mut found, mut not_found) = (vec![], vec![]); + while let Some(maybe_prop) = result_stream.next().await { + match maybe_prop { + Ok(v) => found.push(dav::AnyProperty::Value(v)), + Err(v) => not_found.push(dav::AnyProperty::Request(v)), + } + } + + // If at least one property has been found on this object, adding a HTTP 200 propstat to + // the response + if !found.is_empty() { + prop_desc.push(dav::PropStat { + status: dav::Status(hyper::StatusCode::OK), + prop: dav::AnyProp(found), + error: None, + responsedescription: None, + }); + } + + // If at least one property can't be found on this object, adding a HTTP 404 propstat to + // the response + if !not_found.is_empty() { + prop_desc.push(dav::PropStat { + status: dav::Status(hyper::StatusCode::NOT_FOUND), + prop: dav::AnyProp(not_found), + error: None, + responsedescription: None, + }) + } + + // Build the finale response + dav::Response { + status_or_propstat: dav::StatusOrPropstat::PropStat(dav::Href(path), prop_desc), + error: None, + location: None, + responsedescription: None, + } + } + .boxed() + } +} |