aboutsummaryrefslogtreecommitdiff
path: root/aero-proto/src/dav/node.rs
blob: 0a83f8c5afd6c2a47a8a6d5ceec5d53701b8c30f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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>;
    //fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>>;
    /// 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()
    }
}