use crate::backend::Backend;
use http::{HeaderMap, HeaderValue};
use url::Url;
pub struct UploadBuilder {
backend: Backend,
url: Url,
file_path: Option<(std::path::PathBuf, String)>, data: Option<Vec<u8>>,
headers: HeaderMap,
progress_callback: Option<Box<dyn Fn(u64, Option<u64>) + Send + Sync + 'static>>,
}
impl UploadBuilder {
pub(crate) fn new(backend: Backend, url: Url) -> Self {
Self {
backend,
url,
file_path: None,
data: None,
headers: HeaderMap::new(),
progress_callback: None,
}
}
pub fn from_file<P: AsRef<std::path::Path>>(mut self, path: P) -> Self {
let path = path.as_ref().to_path_buf();
let content_type = match path.extension().and_then(|ext| ext.to_str()) {
Some("txt") => "text/plain",
Some("html") => "text/html",
Some("css") => "text/css",
Some("js") => "application/javascript",
Some("pdf") => "application/pdf",
Some("png") => "image/png",
Some("jpg") | Some("jpeg") => "image/jpeg",
Some("gif") => "image/gif",
Some("zip") => "application/zip",
_ => "application/octet-stream",
}
.to_string();
self.file_path = Some((path, content_type));
self
}
pub fn from_data(mut self, data: Vec<u8>) -> Self {
self.data = Some(data);
self
}
pub fn header(
mut self,
name: impl TryInto<http::HeaderName>,
value: impl Into<String>,
) -> crate::Result<Self> {
let header_name = name.try_into().map_err(|_| crate::Error::InvalidHeader)?;
let header_value =
http::HeaderValue::from_str(&value.into()).map_err(|_| crate::Error::InvalidHeader)?;
self.headers.insert(header_name, header_value);
Ok(self)
}
pub fn progress<F>(mut self, callback: F) -> Self
where
F: Fn(u64, Option<u64>) + Send + Sync + 'static,
{
self.progress_callback = Some(Box::new(callback));
self
}
pub fn auth(mut self, auth: crate::Auth) -> crate::Result<Self> {
let header_value = HeaderValue::from_str(&auth.to_header_value())
.map_err(|_| crate::Error::InvalidHeader)?;
self.headers.insert("authorization", header_value);
Ok(self)
}
pub async fn send(mut self) -> crate::Result<crate::Response> {
let body = if let Some(data) = self.data {
crate::Body::bytes(data, "application/octet-stream")
} else if let Some((path, content_type)) = self.file_path {
if !self.headers.contains_key("content-type") {
self.headers.insert(
"content-type",
HeaderValue::from_str(&content_type)
.map_err(|_| crate::Error::InvalidHeader)?,
);
}
crate::Body::from_file(path, Some(content_type)).await?
} else {
return Err(crate::Error::Internal(
"No file or data specified for upload".to_string(),
));
};
let mut request_builder =
crate::RequestBuilder::new(http::Method::POST, self.url, self.backend);
for (name, value) in &self.headers {
request_builder = request_builder.header(name.as_str(), value.to_str().unwrap())?;
}
if let Some(callback) = self.progress_callback {
request_builder = request_builder.progress(callback);
}
request_builder.body(body).send().await
}
}