webdav_request/client/
mod.rs

1mod inner;
2use std::sync::Arc;
3pub(crate) mod webdav_client;
4use crate::method::Method;
5use crate::multistatus::MultiStatus;
6use crate::reader::LazyResponseReader;
7use crate::DavItem;
8use crate::{header::HeaderMap, Body};
9use inner::InnerClient;
10use reqwest::header::{HeaderName, HeaderValue, CONTENT_TYPE};
11use reqwest::{IntoUrl, Response, Url};
12const DEPTH: HeaderName = HeaderName::from_static("depth");
13const DEPTH_ONE: HeaderValue = HeaderValue::from_static("1");
14const RANGE: HeaderName = HeaderName::from_static("range");
15const APPLICATION_XML: HeaderValue = HeaderValue::from_static("application/xml");
16
17macro_rules! header_value {
18    ($arg:expr) => {
19        reqwest::header::HeaderValue::from_bytes($arg.as_bytes()).unwrap()
20    };
21}
22
23const ALL_DROP: &str = r#"<?xml version="1.0" encoding="utf-8" ?>
24    <D:propfind xmlns:D="DAV:">
25        <D:allprop/>
26    </D:propfind>
27"#;
28
29#[derive(Default, Clone)]
30pub struct DavClient {
31    inner: Arc<InnerClient>,
32}
33unsafe impl Send for DavClient {}
34
35unsafe impl Sync for DavClient {}
36
37impl DavClient {
38    pub fn new(username: &str, password: &str) -> crate::Result<Self> {
39        Ok(Self {
40            inner: Arc::new(InnerClient::new(username, password)?),
41        })
42    }
43    pub fn request(&self, method: Method, url: Url) -> WevDAVRequestBuilder {
44        WevDAVRequestBuilder::new(self.inner.clone(), url, method)
45    }
46
47    /// Initiates a GET request to retrieve a resource from the WebDAV server.
48    #[inline(always)]
49    pub fn get(&self, url: impl IntoUrl) -> crate::Result<WevDAVRequestBuilder> {
50        Ok(self.request(Method::GET, url.into_url()?))
51    }
52
53    /// Initiates a PUT request to upload or update a resource on the WebDAV server.
54    #[inline(always)]
55    pub fn put(&self, url: impl IntoUrl) -> crate::Result<WevDAVRequestBuilder> {
56        Ok(self.request(Method::PUT, url.into_url()?))
57    }
58
59    /// Lists the contents of a WebDAV collection (directory).
60    pub async fn list(&self, url: impl IntoUrl) -> crate::Result<Vec<DavItem>> {
61        let url = url.into_url()?;
62        let url_str = url.as_str().to_owned();
63        let response = self.all_propfind(url).await?;
64        if response.status().is_success() {
65            let xml = response.text().await?;
66            let multi_status = MultiStatus::parse(&xml)?;
67            DavItem::parse(multi_status, &url_str)
68        } else {
69            Err(response.status().into())
70        }
71    }
72
73    #[inline(always)]
74    pub async fn all_propfind(&self, url: impl IntoUrl) -> crate::Result<Response> {
75        self.request(Method::PROPFIND, url.into_url()?)
76            .header(CONTENT_TYPE, APPLICATION_XML)
77            .header(DEPTH, DEPTH_ONE)
78            .body(ALL_DROP)
79            .send()
80            .await
81    }
82}
83
84pub struct WevDAVRequestBuilder {
85    client: Arc<InnerClient>,
86    basic_auth: Option<(String, String)>,
87    url: Url,
88    headers: HeaderMap,
89    body: Option<Body>,
90    method: Method,
91}
92
93impl WevDAVRequestBuilder {
94    pub fn new(client: Arc<InnerClient>, url: Url, method: Method) -> Self {
95        Self {
96            client,
97            basic_auth: None,
98            headers: HeaderMap::new(),
99            url,
100            method,
101            body: None,
102        }
103    }
104    pub fn basic_auth(self, username: &str, password: &str) -> Self {
105        Self {
106            basic_auth: Some((username.to_owned(), password.to_owned())),
107            ..self
108        }
109    }
110    pub fn body(self, body: impl Into<Body>) -> Self {
111        Self {
112            body: Some(body.into()),
113            ..self
114        }
115    }
116    pub fn range(self, start: usize, end: usize) -> Self {
117        self.header(RANGE, header_value!(format!("bytes={}-{}", start, end)))
118    }
119    pub fn header(mut self, key: HeaderName, val: HeaderValue) -> Self {
120        self.headers.insert(key, val);
121        self
122    }
123    pub fn headers(mut self, headers: HeaderMap) -> Self {
124        self.headers.extend(headers);
125        self
126    }
127
128    pub fn build(self) -> crate::RequestBuilder {
129        let builder = self.client.inner.request(self.method.convert(), self.url);
130        let builder = if let Some(body) = self.body {
131            builder.body(body)
132        } else {
133            builder
134        };
135        if let Some((usr, pass)) = &self.basic_auth {
136            builder.basic_auth(usr, Some(pass))
137        } else if let Some((usr, psw)) = &self.client.auth {
138            builder.basic_auth(usr, Some(psw))
139        } else {
140            builder
141        }
142        .headers(self.headers)
143    }
144    pub fn into_lazy_reader(self) -> LazyResponseReader {
145        LazyResponseReader::new(self.build())
146    }
147    pub async fn send(self) -> crate::Result<Response> {
148        self.build().send().await.map_err(Into::into)
149    }
150}