1use std::collections::HashMap;
19use std::fmt::{self, Debug, Display};
20
21use reqwest::{blocking::multipart::Form, Method, Url};
22use serde::Deserialize;
23
24use crate::file::{ImageInfo, VideoInfo};
25use crate::ucare::{upload::Client, upload::Fields, upload::Payload, Result};
26
27pub struct Service<'a> {
29 client: &'a Client,
30}
31
32pub fn new_svc(client: &Client) -> Service {
34 Service { client }
35}
36
37impl Service<'_> {
38 pub fn file(&self, params: FileParams) -> Result<HashMap<String, String>> {
41 let mut form = Form::new()
42 .file(params.name.to_string(), params.path.to_string())?
43 .text(
44 "UPLOADCARE_STORE",
45 if let Some(val) = params.to_store {
46 val
47 } else {
48 ToStore::False
49 }
50 .to_string(),
51 );
52 form = add_signature_expire(&(*self.client.auth_fields)(), form);
53
54 self.client.call::<String, HashMap<String, String>>(
55 Method::POST,
56 format!("/base/"),
57 None,
58 Some(Payload::Form(form)),
59 )
60 }
61
62 pub fn from_url(&self, params: FromUrlParams) -> Result<FromUrlData> {
64 let mut form = Form::new().text("source_url", params.source_url).text(
65 "store",
66 if let Some(val) = params.to_store {
67 val
68 } else {
69 ToStore::False
70 }
71 .to_string(),
72 );
73 if let Some(val) = params.filename {
74 form = form.text("filename", val);
75 }
76 if let Some(val) = params.check_url_duplicates {
77 form = form.text("check_URL_duplicates", val.to_string());
78 }
79 if let Some(val) = params.save_url_duplicates {
80 form = form.text("save_URL_duplicates", val.to_string());
81 }
82 form = add_signature_expire(&(*self.client.auth_fields)(), form);
83
84 self.client.call::<String, FromUrlData>(
85 Method::POST,
86 format!("/from_url/"),
87 None,
88 Some(Payload::Form(form)),
89 )
90 }
91
92 pub fn from_url_status(&self, token: &str) -> Result<FromUrlStatusData> {
94 self.client.call::<String, FromUrlStatusData>(
95 Method::GET,
96 format!("/from_url/status/?token={}", token),
97 None,
98 None,
99 )
100 }
101
102 pub fn file_info(&self, file_id: &str) -> Result<FileInfo> {
104 let fields = (*self.client.auth_fields)();
105 self.client.call::<String, FileInfo>(
106 Method::GET,
107 format!("/info/?pub_key={}&file_id={}", fields.pub_key, file_id),
108 None,
109 None,
110 )
111 }
112
113 pub fn create_group(&self, ids: &[&str]) -> Result<GroupInfo> {
122 let mut form = Form::new();
123 for (pos, id) in ids.iter().enumerate() {
124 form = form.text(
125 ("files[".to_string() + pos.to_string().as_str() + "]").to_string(),
126 id.to_string(),
127 );
128 }
129 form = add_signature_expire(&(*self.client.auth_fields)(), form);
130
131 self.client.call::<String, GroupInfo>(
132 Method::POST,
133 format!("/group/"),
134 None,
135 Some(Payload::Form(form)),
136 )
137 }
138
139 pub fn group_info(&self, group_id: &str) -> Result<GroupInfo> {
144 let fields = (*self.client.auth_fields)();
145 self.client.call::<String, GroupInfo>(
146 Method::GET,
147 format!(
148 "/group/info/?pub_key={}&group_id={}",
149 fields.pub_key, group_id,
150 ),
151 None,
152 None,
153 )
154 }
155
156 pub fn multipart_start(&self, params: MultipartParams) -> Result<MultipartData> {
163 let mut form = Form::new()
164 .text("filename", params.filename)
165 .text(
166 "UPLOADCARE_STORE",
167 if let Some(val) = params.to_store {
168 val
169 } else {
170 ToStore::False
171 }
172 .to_string(),
173 )
174 .text("content_type", params.content_type)
175 .text("size", params.size.to_string());
176 form = add_signature_expire(&(*self.client.auth_fields)(), form);
177
178 self.client.call::<String, MultipartData>(
179 Method::POST,
180 format!("/multipart/start/"),
181 None,
182 Some(Payload::Form(form)),
183 )
184 }
185
186 pub fn upload_part(&self, url: &str, data: Vec<u8>) -> Result<()> {
191 self.client
192 .call_url::<()>(Method::PUT, Url::parse(url)?, Some(Payload::Raw(data)))
193 }
194
195 pub fn multipart_complete(&self, uuid: String) -> Result<FileInfo> {
197 let mut form = Form::new().text("uuid", uuid);
198 form = add_signature_expire(&(*self.client.auth_fields)(), form);
199
200 self.client.call::<String, FileInfo>(
201 Method::POST,
202 format!("/multipart/complete/"),
203 None,
204 Some(Payload::Form(form)),
205 )
206 }
207}
208
209#[derive(Default)]
211pub struct FileParams {
212 pub path: String,
219 pub name: String,
221 pub to_store: Option<ToStore>,
223}
224
225pub struct FromUrlParams {
227 pub source_url: String,
229 pub to_store: Option<ToStore>,
231 pub filename: Option<String>,
234 pub check_url_duplicates: Option<UrlDuplicates>,
236 pub save_url_duplicates: Option<UrlDuplicates>,
240}
241
242#[derive(Debug, Deserialize)]
244#[serde(untagged)]
245pub enum FromUrlData {
246 #[serde(rename = "token")]
248 Token(FileToken),
249 #[serde(rename = "file_info")]
251 FileInfo(FileInfo),
252}
253
254impl Default for FromUrlData {
255 fn default() -> Self {
256 FromUrlData::Token(FileToken::default())
257 }
258}
259
260#[derive(Debug, Deserialize, Default)]
262pub struct FileToken {
263 #[serde(rename = "type")]
265 pub data_type: String,
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub token: Option<String>,
269}
270
271#[derive(Debug, Deserialize)]
273#[serde(tag = "status")]
274pub enum FromUrlStatusData {
275 #[serde(rename = "success")]
277 Success(FileInfo),
278 #[serde(rename = "progress")]
280 Progress {
281 done: u32,
283 total: u32,
285 },
286 #[serde(rename = "error")]
288 Error {
289 error: String,
291 },
292 #[serde(rename = "unknown")]
294 Unknown,
295 #[serde(rename = "waiting")]
297 Waiting,
298}
299
300impl Default for FromUrlStatusData {
301 fn default() -> Self {
302 FromUrlStatusData::Unknown
303 }
304}
305
306#[derive(Debug, Deserialize, Default)]
308pub struct FileInfo {
309 pub is_stored: bool,
311 pub done: u32,
313 pub file_id: String,
315 pub total: u32,
317 pub size: u32,
319 pub uuid: String,
321 pub is_image: bool,
323 pub filename: String,
325 pub video_info: Option<VideoInfo>,
327 pub is_ready: bool,
329 pub original_filename: String,
331 pub image_info: Option<ImageInfo>,
333 pub mime_type: String,
335 pub s3_bucket: Option<String>,
338 pub default_effects: Option<String>,
340}
341
342#[derive(Debug, Deserialize, Default)]
344pub struct GroupInfo {
345 pub datetime_created: String,
347 pub datetime_stored: Option<String>,
349 #[serde(rename = "files_count")]
351 pub file_count: u32,
352 pub cdn_url: String,
354 pub files: Option<Vec<FileInfo>>,
356 pub url: String,
358 pub id: String,
360}
361
362#[derive(Debug, Default)]
364pub struct MultipartParams {
365 pub filename: String,
367 pub size: u32,
369 pub content_type: String,
371 pub to_store: Option<ToStore>,
373}
374
375#[derive(Default, Debug, Deserialize)]
377pub struct MultipartData {
378 pub parts: Vec<String>,
380 pub uuid: String,
382}
383
384pub enum UploadStatus {
386 Success,
388 InProgress,
390 Error,
392 Waiting,
394 Unknown,
396}
397
398impl Display for UploadStatus {
399 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
400 let val = match *self {
401 UploadStatus::Success => "success",
402 UploadStatus::InProgress => "progress",
403 UploadStatus::Error => "error",
404 UploadStatus::Waiting => "waiting",
405 UploadStatus::Unknown => "unknown",
406 };
407
408 write!(f, "{}", val)
409 }
410}
411
412pub enum ToStore {
414 True,
416 False,
418 Auto,
420}
421
422impl Display for ToStore {
423 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424 let val = match *self {
425 ToStore::True => "1",
426 ToStore::False => "0",
427 ToStore::Auto => "auto",
428 };
429
430 write!(f, "{}", val)
431 }
432}
433
434impl Debug for ToStore {
435 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
436 write!(f, "ToStore {}", self)
437 }
438}
439
440pub enum UrlDuplicates {
442 True,
444 False,
446}
447
448impl Display for UrlDuplicates {
449 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
450 let val = match *self {
451 UrlDuplicates::True => "1",
452 UrlDuplicates::False => "0",
453 };
454
455 write!(f, "{}", val)
456 }
457}
458
459fn add_signature_expire(auth_fields: &Fields, form: Form) -> Form {
460 let form = form
461 .text("UPLOADCARE_PUB_KEY", auth_fields.pub_key.to_string())
462 .text("pub_key", auth_fields.pub_key.to_string());
463 if let None = auth_fields.signature {
464 return form;
465 }
466 form.text(
467 "signature",
468 auth_fields.signature.as_ref().unwrap().to_string(),
469 )
470 .text("expire", auth_fields.expire.as_ref().unwrap().to_string())
471}