ucare/file.rs
1//! Holds all primitives and logic around the file resource.
2//!
3//! The file resource is intended to handle user-uploaded files and
4//! is the main Uploadcare resource.
5//!
6//! Each of uploaded files has an ID (UUID) that is assigned once and never
7//! changes later.
8
9use std::collections::HashMap;
10use std::fmt::{self, Debug, Display};
11
12use reqwest::{Method, Url};
13use serde::{self, Deserialize, Serialize};
14use serde_json;
15
16use crate::ucare::{encode_json, rest::Client, IntoUrlQuery, Result};
17
18/// Service is used to make calls to file API.
19pub struct Service<'a> {
20 client: &'a Client,
21}
22
23/// creates an instance of the file service
24pub fn new_svc(client: &Client) -> Service {
25 Service { client }
26}
27
28impl Service<'_> {
29 /// Acquires some file specific info
30 pub fn info(&self, file_id: &str) -> Result<Info> {
31 self.client.call::<String, String, Info>(
32 Method::GET,
33 format!("/files/{}/", file_id),
34 None,
35 None,
36 )
37 }
38
39 /// Returns a list of files
40 ///
41 /// ```rust,ignore
42 /// # use ucare::file;
43 ///
44 /// let params = file::ListParams{
45 /// limit: Some(10),
46 /// ordering: Some(file::Ordering::Size),
47 /// from: None,
48 /// };
49 /// let list = file_svc.list(params)?;
50 /// let mut next_page = list.next;
51 ///
52 /// let mut files = list.results.unwrap();
53 /// while let Some(next) = next_page {
54 /// let new_page = file_svc.get_page(&next).unwrap();
55 /// next_page = new_page.next;
56 /// files.extend(new_page.results.unwrap());
57 /// }
58 ///
59 /// for f in files.iter() {
60 /// println!("file: {}", f);
61 /// }
62 /// ```
63 pub fn list(&self, params: ListParams) -> Result<List> {
64 self.client.call::<ListParams, String, List>(
65 Method::GET,
66 format!("/files/"),
67 Some(params),
68 None,
69 )
70 }
71
72 /// Gets next page by its url
73 pub fn get_page(&self, url: &str) -> Result<List> {
74 let url = Url::parse(url)?;
75 self.client.call_url::<String, List>(Method::GET, url, None)
76 }
77
78 /// Store a single file by its id
79 pub fn store(&self, file_id: &str) -> Result<Info> {
80 self.client.call::<String, String, Info>(
81 Method::PUT,
82 format!("/files/{}/storage/", file_id),
83 None,
84 None,
85 )
86 }
87
88 /// Used to store multiple files in one go. Up to 100 files are
89 /// supported per request.
90 pub fn batch_store(&self, file_ids: &[&str]) -> Result<BatchInfo> {
91 let json = encode_json(&file_ids)?;
92 self.client.call::<String, Vec<u8>, BatchInfo>(
93 Method::PUT,
94 format!("/files/storage/"),
95 None,
96 Some(json),
97 )
98 }
99
100 /// Removes file by its id
101 pub fn delete(&self, file_id: &str) -> Result<Info> {
102 self.client.call::<String, String, Info>(
103 Method::DELETE,
104 format!("/files/{}/storage/", file_id),
105 None,
106 None,
107 )
108 }
109
110 /// Used to delete multiple files in one go. Up to 100 files are
111 /// supported per request.
112 pub fn batch_delete(&self, file_ids: &[&str]) -> Result<BatchInfo> {
113 let json = encode_json(&file_ids)?;
114 self.client.call::<String, Vec<u8>, BatchInfo>(
115 Method::DELETE,
116 format!("/files/storage/"),
117 None,
118 Some(json),
119 )
120 }
121
122 /// Copy is the APIv05 version of the LocalCopy and RemoteCopy, use them instead
123 pub fn copy(&self, params: CopyParams) -> Result<LocalCopyInfo> {
124 let json = encode_json(¶ms)?;
125 self.client.call::<String, Vec<u8>, LocalCopyInfo>(
126 Method::POST,
127 format!("/files/"),
128 None,
129 Some(json),
130 )
131 }
132
133 /// Used to copy original files or their modified versions to
134 /// default storage. Source files MAY either be stored or just uploaded and MUST
135 /// NOT be deleted
136 pub fn local_copy(&self, mut params: CopyParams) -> Result<LocalCopyInfo> {
137 if let None = params.store {
138 params.store = Some(ToStore::False);
139 }
140 if let None = params.make_public {
141 params.make_public = Some(MakePublic::True);
142 }
143
144 let json = encode_json(¶ms)?;
145
146 self.client.call::<String, Vec<u8>, LocalCopyInfo>(
147 Method::POST,
148 format!("/files/local_copy/"),
149 None,
150 Some(json),
151 )
152 }
153
154 /// Used to copy original files or their modified versions to a custom
155 /// storage. Source files MAY either be stored or just uploaded and MUST NOT be
156 /// deleted.
157 pub fn remote_copy(&self, mut params: CopyParams) -> Result<RemoteCopyInfo> {
158 if let None = params.make_public {
159 params.make_public = Some(MakePublic::True);
160 }
161
162 let json = encode_json(¶ms)?;
163
164 self.client.call::<String, Vec<u8>, RemoteCopyInfo>(
165 Method::POST,
166 format!("/files/remote_copy/"),
167 None,
168 Some(json),
169 )
170 }
171}
172
173/// Info holds file specific information
174#[derive(Debug, Deserialize)]
175pub struct Info {
176 /// File UUID.
177 pub uuid: String,
178 /// Date and time when a file was removed, if any.
179 pub datetime_removed: Option<String>,
180 /// Date and time of the last store request, if any.
181 pub datetime_stored: Option<String>,
182 /// Date and time when a file was uploaded.
183 pub datetime_uploaded: Option<String>,
184 /// Image metadata
185 pub image_info: Option<ImageInfo>,
186 /// Is file is image.
187 pub is_image: Option<bool>,
188 /// Is file is ready to be used after upload.
189 pub is_ready: Option<bool>,
190 /// File MIME-type.
191 pub mime_type: Option<String>,
192 /// Publicly available file CDN URL. Available if a file is not deleted.
193 pub original_file_url: Option<String>,
194 /// Original file name taken from uploaded file.
195 pub original_filename: Option<String>,
196 /// File size in bytes.
197 pub size: Option<i32>,
198 /// API resource URL for a particular file.
199 pub url: Option<String>,
200 /// Dictionary of other files that has been created using this file as source. Used for video,
201 /// document and etc. conversion.
202 pub variations: Option<serde_json::Value>,
203 /// Video info
204 pub video_info: Option<VideoInfo>,
205 /// File upload source. This field contains information about from where file was uploaded, for
206 /// example: facebook, gdrive, gphotos, etc.
207 pub source: Option<String>,
208 /// Dictionary of file categories with it\"s confidence.
209 pub rekognition_info: Option<HashMap<String, f32>>,
210}
211
212/// ImageInfo holds image-specific information
213#[derive(Debug, Deserialize)]
214pub struct ImageInfo {
215 /// Image color mode.
216 pub color_mode: Option<ColorMode>,
217 /// Image orientation from EXIF.
218 pub orientation: Option<i32>,
219 /// Image format.
220 pub format: Option<String>,
221 /// Image sequence
222 pub sequence: Option<bool>,
223 /// Image height in pixels.
224 pub height: Option<i32>,
225 /// Image width in pixels.
226 pub width: Option<i32>,
227 /// Image geo location.
228 pub geo_location: Option<ImageInfoGeoLocation>,
229 /// Image date and time from EXIF.
230 pub datetime_original: Option<String>,
231 /// Image DPI for two dimensions.
232 pub dpi: Option<Vec<f32>>,
233}
234
235/// Image geo location
236#[derive(Debug, Deserialize)]
237pub struct ImageInfoGeoLocation {
238 /// Location latitude.
239 pub latitude: Option<f32>,
240 /// Location longitude.
241 pub longitude: Option<f32>,
242}
243
244/// Image color mode.
245#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize)]
246pub enum ColorMode {
247 /// RGB
248 RGB,
249 /// RGBA
250 RGBA,
251 /// RGBa
252 RGBa,
253 /// RGBX
254 RGBX,
255 /// L
256 L,
257 /// LA
258 LA,
259 /// La
260 La,
261 /// P
262 P,
263 /// PA
264 PA,
265 /// CMYK
266 CMYK,
267 /// YCbCr
268 YCbCr,
269 /// HSV
270 HSV,
271 /// LAB
272 LAB,
273}
274
275/// Video related information
276#[derive(Debug, PartialEq, Deserialize)]
277pub struct VideoInfo {
278 /// Video duration in milliseconds.
279 pub duration: Option<f32>,
280 /// Video format (MP4 for example).
281 pub format: Option<String>,
282 /// Video bitrate.
283 pub bitrate: Option<f32>,
284 /// Audio information
285 pub audio: Option<VideoInfoAudio>,
286 /// Video stream info
287 pub video: Option<VideoInfoVideo>,
288}
289
290/// Information about the audio in video
291#[derive(Debug, PartialEq, Deserialize)]
292pub struct VideoInfoAudio {
293 /// Audio stream metadata.
294 pub bitrate: Option<f32>,
295 /// Audio stream codec.
296 pub codec: Option<String>,
297 /// Audio stream sample rate.
298 pub sample_rate: Option<f32>,
299 /// Audio stream number of channels.
300 pub channels: Option<String>,
301}
302
303/// Video stream info
304#[derive(Debug, PartialEq, Deserialize)]
305pub struct VideoInfoVideo {
306 /// Video stream image height.
307 pub height: Option<f32>,
308 /// Video stream image width.
309 pub width: Option<f32>,
310 /// Video stream frame rate.
311 pub frame_rate: Option<f32>,
312 /// Video stream bitrate.
313 pub bitrate: Option<f32>,
314 /// Video stream codec.
315 pub codec: Option<String>,
316}
317
318/// Holds all possible params for for the list method
319pub struct ListParams {
320 /// Is set to true if only include removed files in the response,
321 /// otherwise existing files are included. Defaults to false.
322 pub removed: Option<bool>,
323 /// Is set to true if only include files that were stored.
324 /// Set to false to include only temporary files.
325 /// The default is unset: both stored and not stored files are returned
326 pub stored: Option<bool>,
327 /// Specifies preferred amount of files in a list for a single
328 /// response. Defaults to 100, while the maximum is 1000
329 pub limit: Option<i32>,
330 /// Specifies the way files are sorted in a returned list.
331 /// By default is set to datetime_uploaded.
332 pub ordering: Option<Ordering>,
333 /// Specifies a starting point for filtering files.
334 /// The value depends on your ordering parameter value.
335 pub from: Option<String>,
336}
337
338/// Specifies the way files are sorted in a returned list.
339/// By default is set to datetime_uploaded.
340pub enum Ordering {
341 /// "datetime_uploaded"
342 DatetimeUploaded,
343 /// "-datetime_uploaded"
344 DatetimeUploadedNeg,
345 /// "size"
346 Size,
347 /// "-size"
348 SizeNeg,
349}
350
351impl Display for Ordering {
352 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
353 let val = match *self {
354 Ordering::DatetimeUploaded => "datetime_uploaded",
355 Ordering::DatetimeUploadedNeg => "-datetime_uploaded",
356 Ordering::Size => "size",
357 Ordering::SizeNeg => "-size",
358 };
359
360 write!(f, "{}", val)
361 }
362}
363
364impl IntoUrlQuery for ListParams {
365 fn into_query(self) -> String {
366 let mut q = String::new();
367 q.push_str("removed=");
368 if let Some(val) = self.removed {
369 q.push_str(val.to_string().as_str());
370 } else {
371 q.push_str("false");
372 }
373 q.push('&');
374
375 if let Some(val) = self.stored {
376 q.push_str("stored=");
377 q.push_str(val.to_string().as_str());
378 q.push('&');
379 }
380
381 q.push_str("limit=");
382 if let Some(val) = self.limit {
383 q.push_str(val.to_string().as_str());
384 } else {
385 q.push_str("1000");
386 }
387 q.push('&');
388
389 q.push_str("ordering=");
390 if let Some(val) = self.ordering {
391 q.push_str(val.to_string().as_str());
392 } else {
393 q.push_str(Ordering::DatetimeUploaded.to_string().as_str());
394 }
395
396 if let Some(val) = self.from {
397 q.push('&');
398 q.push_str("from=");
399 q.push_str(val.as_str());
400 }
401
402 q
403 }
404}
405
406/// Holds a list of files
407#[derive(Debug, Deserialize)]
408pub struct List {
409 /// Actual results
410 pub results: Option<Vec<Info>>,
411 /// Next page URL.
412 pub next: Option<String>,
413 /// Previous page URL.
414 pub previous: Option<String>,
415 /// A total number of objects of the queried type. For files, the queried type depends on
416 /// the stored and removed query parameters.
417 pub total: Option<i32>,
418 /// Number of objects per page.
419 pub per_page: Option<i32>,
420}
421
422/// MUST be either true or false
423#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
424pub enum ToStore {
425 /// True
426 #[serde(rename = "true")]
427 True,
428 /// False
429 #[serde(rename = "false")]
430 False,
431}
432
433/// MUST be either true or false. true to make copied files available via public links,
434/// false to reverse the behavior.
435#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
436pub enum MakePublic {
437 /// True
438 #[serde(rename = "true")]
439 True,
440 /// False
441 #[serde(rename = "false")]
442 False,
443}
444
445/// The parameter is used to specify file names Uploadcare passes to a custom storage.
446/// In case the parameter is omitted, we use pattern of your custom storage.
447/// Use any combination of allowed values.
448#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
449pub enum Pattern {
450 /// Default
451 #[serde(rename = "${default}")]
452 Default,
453 /// AutoFilename
454 #[serde(rename = "${filename} ${effects} ${ext}")]
455 AutoFilename,
456 /// Effects
457 #[serde(rename = "${effects}")]
458 Effects,
459 /// Filename
460 #[serde(rename = "${filename}")]
461 Filename,
462 /// Uuid
463 #[serde(rename = "${uuid}")]
464 Uuid,
465 /// Ext
466 #[serde(rename = "${ext}")]
467 Ext,
468}
469
470/// CopyParams is used when copy original files or their modified
471/// versions to default storage. Source files MAY either be stored or just
472/// uploaded and MUST NOT be deleted
473#[derive(Debug, PartialEq, Serialize)]
474pub struct CopyParams {
475 /// Source is a CDN URL or just ID (UUID) of a file subjected to copy
476 pub source: String,
477 /// Store parameter only applies to the Uploadcare storage and MUST
478 /// be either true or false.
479 #[serde(skip_serializing_if = "Option::is_none")]
480 pub store: Option<ToStore>,
481 /// MakePublic is applicable to custom storage only. MUST be either true or
482 /// false. True to make copied files available via public links, false to
483 /// reverse the behavior.
484 #[serde(skip_serializing_if = "Option::is_none")]
485 pub make_public: Option<MakePublic>,
486 /// Target identifies a custom storage name related to your project.
487 /// Implies you are copying a file to a specified custom storage. Keep in
488 /// mind you can have multiple storages associated with a single S3
489 /// bucket.
490 #[serde(skip_serializing_if = "Option::is_none")]
491 pub target: Option<String>,
492 /// Pattern is used to specify file names Uploadcare passes to a custom
493 /// storage. In case the parameter is omitted, we use pattern of your
494 /// custom storage.
495 #[serde(skip_serializing_if = "Option::is_none")]
496 pub pattern: Option<Pattern>,
497}
498
499/// Holds local_copy response data
500#[derive(Debug, Deserialize)]
501pub struct LocalCopyInfo {
502 /// holds actual data
503 pub result: Info,
504}
505
506/// Holds remote_copy response data
507#[derive(Debug, Deserialize)]
508pub struct RemoteCopyInfo {
509 /// AlreadyExists is true if destination file with that name
510 /// already exists
511 #[serde(skip_deserializing)]
512 pub already_exists: bool,
513 /// Result is a URL with the s3 scheme. Your bucket name is put
514 /// as a host, and an s3 object path follows
515 pub result: Option<String>,
516}
517
518/// Holds batch operation response data
519#[derive(Debug, Deserialize)]
520pub struct BatchInfo {
521 /// Map of passed files IDs and problems associated problems
522 pub problems: Option<HashMap<String, String>>,
523 /// Results describes successfully operated files
524 pub result: Option<Vec<Info>>,
525}