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(&params)?;
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(&params)?;
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(&params)?;
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}