use std::borrow::Cow;
use reqwest::header::{CONTENT_LENGTH, CONTENT_TYPE};
use reqwest::multipart::{Form, Part};
use reqwest_middleware::{ClientWithMiddleware as Client, RequestBuilder};
use crate::http::object_access_controls::{PredefinedObjectAcl, Projection};
use crate::http::objects::{Encryption, Object};
use crate::http::{Error, Escape};
#[derive(Clone, Debug)]
pub struct Media {
pub name: Cow<'static, str>,
pub content_type: Cow<'static, str>,
pub content_length: Option<u64>,
}
impl Media {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
content_type: "application/octet-stream".into(),
content_length: None,
}
}
}
#[derive(Clone, Debug)]
pub enum UploadType {
Simple(Media),
Multipart(Box<Object>),
}
#[derive(Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct UploadObjectRequest {
#[serde(skip_serializing)]
pub bucket: String,
pub generation: Option<i64>,
pub if_generation_match: Option<i64>,
pub if_generation_not_match: Option<i64>,
pub if_metageneration_match: Option<i64>,
pub if_metageneration_not_match: Option<i64>,
pub kms_key_name: Option<String>,
pub predefined_acl: Option<PredefinedObjectAcl>,
pub projection: Option<Projection>,
#[serde(skip_serializing)]
pub encryption: Option<Encryption>,
}
pub(crate) fn build<T: Into<reqwest::Body>>(
base_url: &str,
client: &Client,
req: &UploadObjectRequest,
media: &Media,
body: T,
) -> RequestBuilder {
let url = format!("{}/b/{}/o?uploadType=media", base_url, req.bucket.escape(),);
let mut builder = client
.post(url)
.query(&req)
.query(&[("name", &media.name)])
.body(body)
.header(CONTENT_TYPE, media.content_type.to_string());
if let Some(len) = media.content_length {
builder = builder.header(CONTENT_LENGTH, len.to_string())
}
if let Some(e) = &req.encryption {
e.with_headers(builder)
} else {
builder
}
}
pub(crate) fn build_multipart<T: Into<reqwest::Body>>(
base_url: &str,
client: &Client,
req: &UploadObjectRequest,
metadata: &Object,
body: T,
) -> Result<RequestBuilder, Error> {
let url = format!("{}/b/{}/o?uploadType=multipart", base_url, req.bucket.escape(),);
let form = Form::new();
let metadata_part = Part::text(serde_json::to_string(metadata).expect("object serialize failed"))
.mime_str("application/json; charset=UTF-8")?;
let data_part = Part::stream(body);
let form = form.part("metadata", metadata_part).part("data", data_part);
let builder = client.post(url).query(&req).multipart(form);
Ok(if let Some(e) = &req.encryption {
e.with_headers(builder)
} else {
builder
})
}
pub(crate) fn build_resumable_session_simple(
base_url: &str,
client: &Client,
req: &UploadObjectRequest,
media: &Media,
) -> RequestBuilder {
let url = format!("{}/b/{}/o?uploadType=resumable", base_url, req.bucket.escape(),);
let mut builder = client
.post(url)
.query(&req)
.query(&[("name", &media.name)])
.header(CONTENT_LENGTH, 0)
.header("X-Upload-Content-Type", media.content_type.to_string());
if let Some(len) = media.content_length {
builder = builder.header("X-Upload-Content-Length", len)
}
if let Some(e) = &req.encryption {
e.with_headers(builder)
} else {
builder
}
}
pub(crate) fn build_resumable_session_metadata(
base_url: &str,
client: &Client,
req: &UploadObjectRequest,
metadata: &Object,
) -> RequestBuilder {
let url = format!("{}/b/{}/o?uploadType=resumable", base_url, req.bucket.escape(),);
let builder = client.post(url).query(&req).json(&metadata);
if let Some(e) = &req.encryption {
e.with_headers(builder)
} else {
builder
}
}