use std::borrow::Cow;
use std::io::Read;
use reqwest::{header, Body, Client as HttpClient, IntoUrl, Method, RequestBuilder, Response};
use url::Url;
use super::error::Result;
use super::header::{Depth, Destination};
use super::response::{parse_propfind_response, PropfindResponse};
use Error;
type CowStr = Cow<'static, str>;
#[derive(Debug)]
pub struct Client {
http_client: HttpClient,
webdav_url: Url,
credentials: Option<Credentials>,
}
#[derive(Default, Debug)]
pub struct Credentials {
username: CowStr,
password: CowStr,
}
#[derive(Default, Debug)]
pub struct ClientBuilder {
username: Option<CowStr>,
password: Option<CowStr>,
}
impl ClientBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn credentials<S>(mut self, username: S, password: S) -> Self
where
S: Into<CowStr>,
{
self.username = Some(username.into());
self.password = Some(password.into());
self
}
pub fn build<U>(self, webdav_url: U) -> Result<Client>
where
U: IntoUrl,
{
let credentials = if let ClientBuilder {
username: Some(u),
password: Some(p),
} = self
{
Some(Credentials {
username: u,
password: p,
})
} else {
None
};
Ok(Client {
http_client: HttpClient::new(),
webdav_url: webdav_url.into_url()?,
credentials,
})
}
}
impl Client {
pub fn new() -> ClientBuilder {
ClientBuilder::new()
}
pub fn get<I>(&self, path: I) -> Result<Response>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let res = self.request(Method::Get, path).send()?;
if !res.status().is_success() {
Err(Error::FailedRequest(res.status()))?;
}
Ok(res)
}
pub fn put<R, I>(&self, body: R, path: I) -> Result<()>
where
R: Read + Send + 'static,
I: IntoIterator,
I::Item: AsRef<str>,
{
let res = self
.request(Method::Put, path)
.body(Body::new(body))
.send()?;
if !res.status().is_success() {
Err(Error::FailedRequest(res.status()))?;
}
Ok(())
}
pub fn mkcol<I>(&self, path: I) -> Result<()>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let res = self
.request(Method::Extension("MKCOL".to_string()), path)
.send()?;
if !res.status().is_success() {
Err(Error::FailedRequest(res.status()))?
}
Ok(())
}
pub fn mv<F, T>(&self, from: F, to: T) -> Result<()>
where
F: IntoIterator,
F::Item: AsRef<str>,
T: IntoIterator,
T::Item: AsRef<str>,
{
let mut req = self.request(Method::Extension("MOVE".to_string()), from);
let mut url = self.webdav_url.clone();
url.path_segments_mut().unwrap().extend(to);
req.header(Destination(url.to_string()));
let res = req.send()?;
if !res.status().is_success() {
Err(Error::FailedRequest(res.status()))?;
}
Ok(())
}
pub fn list<I>(&self, path: I) -> Result<Vec<PropfindResponse>>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let body = r#"<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
<D:allprop/>
</D:propfind>
"#;
let res = self
.request(Method::Extension("PROPFIND".to_string()), path)
.header(Depth("Infinity".into()))
.body(body)
.send()?;
if !res.status().is_success() {
Err(Error::FailedRequest(res.status()))?;
}
Ok(parse_propfind_response(res)?)
}
pub fn request<I>(&self, method: Method, path: I) -> RequestBuilder
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let mut url = self.webdav_url.clone();
url.path_segments_mut().unwrap().extend(path);
let mut request = self.http_client.request(method, url.as_str());
if let Some(Credentials {
ref username,
ref password,
}) = self.credentials
{
request.header(header::Authorization(header::Basic {
username: username.to_string(),
password: Some(password.to_string()),
}));
}
request
}
}