ucare/group.rs
1//! Holds all primitives and logic related file entity.
2//!
3//! Individual files on Uploadcare can be joined into groups. Those can be used
4//! to better organize your workflow. Technically, groups are ordered lists of
5//! files and can hold files together with Image Transformations in their URLs.
6//! The most common case with creating groups is when users upload multiple files at once.
7//!
8//! NOTE: a group itself and files within that group MUST belong to the same project.
9//! Groups are immutable and the only way to add/remove a file is creating a new group.
10//!
11//! Groups are identified in a way similar to individual files.
12//! A group ID consists of a UUID followed by a “~” tilde character and a group size:
13//! integer number of files in group.
14//! For example, here is an identifier for a group holding 12 files:
15//! badfc9f7-f88f-4921-9cc0-22e2c08aa2da~12
16
17use std::fmt::{self, Debug, Display};
18
19use reqwest::{Method, Url};
20use serde::Deserialize;
21
22use crate::ucare::{rest::Client, IntoUrlQuery, Result};
23
24/// Service is used to make calls to group API.
25pub struct Service<'a> {
26 client: &'a Client,
27}
28
29/// creates an instance of the group service
30pub fn new_svc(client: &Client) -> Service {
31 Service { client }
32}
33
34impl Service<'_> {
35 /// Acquires some file specific info
36 pub fn info(&self, group_id: &str) -> Result<Info> {
37 self.client.call::<String, String, Info>(
38 Method::GET,
39 format!("/groups/{}/", group_id),
40 None,
41 None,
42 )
43 }
44
45 /// Returns a list of groups
46 ///
47 /// ```rust,ignore
48 /// # use ucare::group;
49 ///
50 /// let params = group::ListParams{
51 /// limit: Some(10),
52 /// ordering: Some(group::Ordering::CreatedAtDesc),
53 /// from: None,
54 /// };
55 /// let list = group_svc.list(params)?;
56 /// let mut next_page = list.next;
57 ///
58 /// let mut groups = list.results.unwrap();
59 /// while let Some(next) = next_page {
60 /// let new_page = group_svc.get_page(&next).unwrap();
61 /// next_page = new_page.next;
62 /// groups.extend(new_page.results.unwrap());
63 /// }
64 ///
65 /// for group in groups.iter() {
66 /// println!("group: {}", group);
67 /// }
68 /// ```
69 pub fn list(&self, params: ListParams) -> Result<List> {
70 self.client.call::<ListParams, String, List>(
71 Method::GET,
72 format!("/groups/"),
73 Some(params),
74 None,
75 )
76 }
77
78 /// Gets next page by its url
79 pub fn get_page(&self, url: &str) -> Result<List> {
80 let url = Url::parse(url)?;
81 self.client.call_url::<String, List>(Method::GET, url, None)
82 }
83
84 /// Marks all files in group as stored
85 pub fn store(&self, group_id: &str) -> Result<Info> {
86 self.client.call::<String, String, Info>(
87 Method::PUT,
88 format!("/groups/{}/storage/", group_id),
89 None,
90 None,
91 )
92 }
93}
94
95/// Info holds group specific information
96#[derive(Debug, Deserialize)]
97pub struct Info {
98 /// group identifier
99 pub id: String,
100 /// date and time when a group was created
101 pub datetime_created: Option<String>,
102 /// date and time when a group was stored
103 pub datetime_stored: Option<String>,
104 /// number of files in a group
105 pub files_count: i32,
106 /// public CDN URL for a group
107 pub cdn_url: String,
108}
109
110/// Holds all possible params for for the list method
111pub struct ListParams {
112 /// Specifies preferred amount of groups in a list for a single
113 /// response. Defaults to 100, while the maximum is 1000
114 pub limit: Option<i32>,
115 /// Specifies the way groups are sorted in a returned list.
116 /// By default is set to datetime_created.
117 pub ordering: Option<Ordering>,
118 /// A starting point for filtering group lists. MUST be a datetime value with T used as a
119 /// separator. Example: "2015-01-02T10:00:00"
120 pub from: Option<String>,
121}
122
123/// Specifies the way groups are sorted in a returned list.
124/// By default is set to datetime_created.
125pub enum Ordering {
126 /// datetime_created
127 CreatedAtAsc,
128 /// -datetime_created
129 CreatedAtDesc,
130}
131
132impl Display for Ordering {
133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134 let val = match *self {
135 Ordering::CreatedAtAsc => "datetime_created",
136 Ordering::CreatedAtDesc => "-datetime_created",
137 };
138
139 write!(f, "{}", val)
140 }
141}
142
143impl IntoUrlQuery for ListParams {
144 fn into_query(self) -> String {
145 let mut q = String::new();
146
147 q.push_str("limit=");
148 if let Some(val) = self.limit {
149 q.push_str(val.to_string().as_str());
150 } else {
151 q.push_str("100");
152 }
153 q.push('&');
154
155 q.push_str("ordering=");
156 if let Some(val) = self.ordering {
157 q.push_str(val.to_string().as_str());
158 } else {
159 q.push_str(Ordering::CreatedAtAsc.to_string().as_str());
160 }
161
162 if let Some(val) = self.from {
163 q.push('&');
164 q.push_str("from=");
165 q.push_str(val.as_str());
166 }
167
168 q
169 }
170}
171
172/// Holds a list of groups
173#[derive(Debug, Deserialize)]
174pub struct List {
175 /// Actual results
176 pub results: Option<Vec<Info>>,
177 /// Next page URL.
178 pub next: Option<String>,
179 /// Previous page URL.
180 pub previous: Option<String>,
181 /// A total number of objects of the queried type.
182 pub total: Option<f32>,
183 /// Number of objects per page.
184 pub per_page: Option<f32>,
185}