use std::fmt::{self, Debug, Display};
use reqwest::{blocking::multipart::Form, Method, Url};
use serde::Deserialize;
use crate::file::{ImageInfo, VideoInfo};
use crate::ucare::{upload::Client, upload::Fields, upload::Payload, Result};
pub struct Service<'a> {
client: &'a Client,
}
pub fn new_svc(client: &Client) -> Service {
Service { client }
}
impl Service<'_> {
pub fn file(&self, params: FileParams) -> Result<ShortFileInfo> {
let mut form = Form::new()
.file(params.name.to_string(), params.path.to_string())?
.text(
"UPLOADCARE_STORE",
if let Some(val) = params.to_store {
val
} else {
ToStore::False
}
.to_string(),
);
form = add_signature_expire(&(*self.client.auth_fields)(), form);
self.client.call::<String, ShortFileInfo>(
Method::POST,
format!("/base/"),
None,
Some(Payload::Form(form)),
)
}
pub fn from_url(&self, params: FromUrlParams) -> Result<FromUrlData> {
let mut form = Form::new().text("source_url", params.source_url).text(
"store",
if let Some(val) = params.to_store {
val
} else {
ToStore::False
}
.to_string(),
);
if let Some(val) = params.filename {
form = form.text("filename", val);
}
if let Some(val) = params.check_url_duplicates {
form = form.text("check_URL_duplicates", val.to_string());
}
if let Some(val) = params.save_url_duplicates {
form = form.text("save_URL_duplicates", val.to_string());
}
form = add_signature_expire(&(*self.client.auth_fields)(), form);
self.client.call::<String, FromUrlData>(
Method::POST,
format!("/from_url/"),
None,
Some(Payload::Form(form)),
)
}
pub fn from_url_status(&self, token: String) -> Result<FromUrlStatusData> {
self.client.call::<String, FromUrlStatusData>(
Method::GET,
format!("/from_url/status/?token={}", token),
None,
None,
)
}
pub fn file_info(&self, file_id: String) -> Result<FileInfo> {
let fields = (*self.client.auth_fields)();
self.client.call::<String, FileInfo>(
Method::GET,
format!("/info/?pub_key={}&file_id={}", fields.pub_key, file_id),
None,
None,
)
}
pub fn create_group(&self, ids: &[&str]) -> Result<GroupInfo> {
let mut form = Form::new();
for (pos, id) in ids.iter().enumerate() {
form = form.text(
("file[".to_string() + pos.to_string().as_str() + "]").to_string(),
id.to_string(),
);
}
form = add_signature_expire(&(*self.client.auth_fields)(), form);
self.client.call::<String, GroupInfo>(
Method::POST,
format!("/group/"),
None,
Some(Payload::Form(form)),
)
}
pub fn group_info(&self, group_id: String) -> Result<GroupInfo> {
let fields = (*self.client.auth_fields)();
self.client.call::<String, GroupInfo>(
Method::GET,
format!(
"/group/info/?pub_key={}&group_id={}",
fields.pub_key, group_id,
),
None,
None,
)
}
pub fn multipart_start(&self, params: MultipartParams) -> Result<MultipartData> {
let mut form = Form::new()
.text("filename", params.filename)
.text(
"UPLOADCARE_STORE",
if let Some(val) = params.to_store {
val
} else {
ToStore::False
}
.to_string(),
)
.text("content_type", params.content_type)
.text("size", params.size.to_string());
form = add_signature_expire(&(*self.client.auth_fields)(), form);
self.client.call::<String, MultipartData>(
Method::POST,
format!("/multipart/start/"),
None,
Some(Payload::Form(form)),
)
}
pub fn upload_part(&self, url: String, data: Vec<u8>) -> Result<()> {
self.client.call_url::<()>(
Method::PUT,
Url::parse(url.as_str())?,
Some(Payload::Raw(data)),
)
}
pub fn multipart_complete(&self, uuid: String) -> Result<FileInfo> {
let mut form = Form::new().text("uuid", uuid);
form = add_signature_expire(&(*self.client.auth_fields)(), form);
self.client.call::<String, FileInfo>(
Method::POST,
format!("/multipart/complete/"),
None,
Some(Payload::Form(form)),
)
}
}
pub struct FileParams {
pub path: String,
pub name: String,
pub to_store: Option<ToStore>,
}
#[derive(Debug, Deserialize)]
pub struct ShortFileInfo {
#[serde(rename = "file")]
pub id: String,
}
pub struct FromUrlParams {
pub source_url: String,
pub to_store: Option<ToStore>,
pub filename: Option<String>,
pub check_url_duplicates: Option<UrlDuplicates>,
pub save_url_duplicates: Option<UrlDuplicates>,
}
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
pub enum FromUrlData {
#[serde(rename = "token")]
Token(FileToken),
#[serde(rename = "file_info")]
FileInfo(FileInfo),
}
#[derive(Debug, Deserialize)]
pub struct FileToken {
#[serde(rename = "type")]
pub data_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
}
#[derive(Debug, Deserialize)]
#[serde(tag = "status")]
pub enum FromUrlStatusData {
#[serde(rename = "success")]
Success(FileInfo),
#[serde(rename = "progress")]
Progress {
done: u32,
total: u32,
},
#[serde(rename = "error")]
Error {
error: String,
},
#[serde(rename = "unknown")]
Unknown,
#[serde(rename = "waiting")]
Waiting,
}
#[derive(Debug, Deserialize)]
pub struct FileInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub is_stored: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub done: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub file_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uuid: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_image: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub video_info: Option<VideoInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_ready: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub original_filename: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_info: Option<ImageInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub s3_bucket: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_effects: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct GroupInfo {
pub datetime_created: String,
pub datetime_stored: Option<String>,
#[serde(rename = "files_count")]
pub file_count: u32,
pub cdn_url: String,
pub files: Option<Vec<FileInfo>>,
pub url: String,
pub id: String,
}
#[derive(Debug)]
pub struct MultipartParams {
pub filename: String,
pub size: u32,
pub content_type: String,
pub to_store: Option<ToStore>,
}
#[derive(Debug, Deserialize)]
pub struct MultipartData {
pub parts: Vec<String>,
pub uuid: String,
}
pub enum UploadStatus {
Success,
InProgress,
Error,
Waiting,
Unknown,
}
impl Display for UploadStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let val = match *self {
UploadStatus::Success => "success",
UploadStatus::InProgress => "progress",
UploadStatus::Error => "error",
UploadStatus::Waiting => "waiting",
UploadStatus::Unknown => "unknown",
};
write!(f, "{}", val)
}
}
pub enum ToStore {
True,
False,
Auto,
}
impl Display for ToStore {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let val = match *self {
ToStore::True => "1",
ToStore::False => "0",
ToStore::Auto => "auto",
};
write!(f, "{}", val)
}
}
impl Debug for ToStore {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ToStore {}", self)
}
}
pub enum UrlDuplicates {
True,
False,
}
impl Display for UrlDuplicates {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let val = match *self {
UrlDuplicates::True => "1",
UrlDuplicates::False => "0",
};
write!(f, "{}", val)
}
}
fn add_signature_expire(auth_fields: &Fields, form: Form) -> Form {
let form = form
.text("UPLOADCARE_PUB_KEY", auth_fields.pub_key.to_string())
.text("pub_key", auth_fields.pub_key.to_string());
if let None = auth_fields.signature {
return form;
}
form.text(
"signature",
auth_fields.signature.as_ref().unwrap().to_string(),
)
.text("expire", auth_fields.expire.as_ref().unwrap().to_string())
}