use crate::api::{B2FileInfo, UploadAuth};
use crate::handle_b2error_kinds;
use crate::Error;
use reqwest::header::HeaderMap;
use reqwest::Client;
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct FileParameters<'a> {
pub file_path: &'a str,
pub file_size: u64,
pub content_type: Option<&'a str>,
pub content_sha1: Sha1Variant<'a>,
pub last_modified_millis: u64,
}
#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Sha1Variant<'a> {
Precomputed(&'a str),
HexAtEnd,
DoNotVerify,
}
pub async fn b2_upload_file<B: Into<reqwest::Body>>(
client: &Client,
auth: &UploadAuth,
body: B,
params: FileParameters<'_>,
) -> Result<B2FileInfo, Error> {
let mut headers = HeaderMap::new();
let encoded_file_name =
&url::form_urlencoded::Serializer::new(String::with_capacity(params.file_path.len() + 1))
.append_pair("", params.file_path)
.finish()[1..];
let hash = match params.content_sha1 {
Sha1Variant::Precomputed(hash) => hash,
Sha1Variant::HexAtEnd => "hex_digits_at_end",
Sha1Variant::DoNotVerify => "do_not_verify",
};
let file_size = match params.content_sha1 {
Sha1Variant::HexAtEnd => params.file_size + 40,
_ => params.file_size,
};
headers.insert(
reqwest::header::AUTHORIZATION,
auth.authorization_token.parse().unwrap(),
);
headers.insert(
reqwest::header::CONTENT_TYPE,
params.content_type.unwrap_or("b2/x-auto").parse().unwrap(),
);
headers.insert(reqwest::header::CONTENT_LENGTH, file_size.into());
headers.insert("X-Bz-File-Name", (&encoded_file_name).parse().unwrap());
headers.insert("X-Bz-Content-Sha1", hash.parse().unwrap());
headers.insert(
"X-Bz-Info-src_last_modified_millis",
params.last_modified_millis.into(),
);
let resp = match client
.post(&auth.upload_url)
.headers(headers)
.body(body)
.send()
.await
{
Ok(v) => v,
Err(e) => return Err(Error::ReqwestError(e)),
};
if !resp.status().is_success() {
return Err(Error::from_response(resp).await);
}
let response_string = resp.text().await.unwrap();
let deserialized: B2FileInfo = match serde_json::from_str(&response_string) {
Ok(v) => v,
Err(_e) => {
eprintln!("{:?}", response_string);
return Err(handle_b2error_kinds(&response_string));
}
};
Ok(deserialized)
}