1
2use std::option::Option;
3use std::error::Error;
4use std::fmt::{Debug, Display, Formatter};
5use lazy_static::lazy_static;
6use reqwest::Client;
7use std::sync::Arc;
8use serde::{Deserialize, Serialize};
9use serde::de::DeserializeOwned;
10use serde_json::Value;
11
12#[derive(Serialize, Deserialize, Debug)]
13struct AdminResponse {
14 token: String,
15 admin: Admin,
16}
17
18#[derive(Serialize, Deserialize, Debug)]
19struct Admin {
20 id: String,
21 created: String,
22 updated: String,
23 email: String,
24 avatar: u8,
25}
26
27#[derive(Deserialize)]
28struct PbAuthError {
29 code: u16,
30 message: String,
31 data: ErrorData,
32}
33
34#[derive(Serialize, Deserialize, Debug)]
35struct ErrorData {
36 password: Option<PasswordError>,
37}
38
39#[derive(Serialize, Deserialize, Debug)]
40struct PasswordError {
41 code: String,
42 message: String,
43}
44
45#[derive(Debug, Clone)]
46pub struct PbAdminClient {
47 url: String,
48 username: Option<String>,
49 password: Option<String>,
50 token: Option<String>,
51}
52
53#[derive(Debug)]
54struct PbError {
55 detail: String
56}
57
58impl PbError {
59 fn new(msg: &str) -> PbError {
60 PbError { detail: msg.to_string() }
61 }
62}
63impl Display for PbError {
64 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
65 write!(f, "{}", self.detail)
66 }
67}
68
69lazy_static! {
70 static ref REQ_CLIENT: Arc<Client> = Arc::new(Client::new());
71}
72impl Error for PbError {}
73
74
75#[derive(Serialize, Debug)]
76struct PasswordAuth {
77 identity: String,
78 password: String
79}
80
81
82impl PbAdminClient {
83 pub fn new(url: &str) -> Self {
84 PbAdminClient {
85 url: url.trim_end_matches("/").to_string(),
86 password: None,
87 username: None,
88 token: None,
89 }
90 }
91 pub async fn auth_with_password(mut self, username: &str, password: &str) -> Result<Self, Box<dyn Error>> {
92 self.password = Option::from(username.to_string());
93 self.username = Option::from(password.to_string());
94 let auth_input = PasswordAuth {
95 identity: username.to_string(),
96 password: password.to_string()
97 };
98
99 let req = REQ_CLIENT.post(format!("{}/api/admins/auth-with-password", self.url))
100 .json(&auth_input).send().await?;
101 let code = req.status();
102 if code == 400 {
103 let err: PbAuthError = req.json().await?;
104 return Err(Box::new(PbError::new(&err.message)))
105 }
106 let req: AdminResponse = req.json().await?;
107 self.token = Option::from(req.token);
108
109
110 return Ok(self);
111 }
112 pub fn collection(&self, collection: &str) -> Result<PbCollection, Box<dyn Error>> {
113
114 if self.token.is_none() {
115 return Err(Box::new(PbError::new("unauthenticated")));
116 }
117 let pbcol = PbCollection {
118 url: self.url.clone(),
119 token: self.token.clone().unwrap(),
120 collection: collection.to_string()
121 };
122
123 Ok(pbcol)
124 }
125}
126
127#[derive(Debug)]
128pub struct PbCollection {
129 url: String,
130 token: String,
131 collection: String
132}
133
134impl PbCollection {
135 pub fn get_list(self, page: u16, per_page: u16) -> PbGetList {
136 PbGetList {
137 url: self.url,
138 token: self.token,
139 collection: self.collection,
140 page,
141 per_page,
142 sort: None,
143 filter: None,
144 expand: None,
145 fields: None,
146 skip_total: None,
147 }
148 }
149 pub fn get_one(self, record_id: &str) -> PbViewRecord {
150 PbViewRecord {
151 url: self.url,
152 token: self.token,
153 collection: self.collection,
154 expand: None,
155 fields: None,
156 record_id: record_id.to_string(),
157 }
158 }
159
160 pub fn create<T: Serialize>( self, record: T) -> PbCreateRecord<T> {
161 PbCreateRecord {
162 url: self.url,
163 token: self.token,
164 collection: self.collection,
165 record,
166 expand: None,
167 fields: None
168 }
169 }
170 pub fn update<T: Serialize>( self, record_id: &str, record: T) -> PbUpdateRecord<T> {
171 PbUpdateRecord {
172 url: self.url,
173 token: self.token,
174 collection: self.collection,
175 record,
176 expand: None,
177 fields: None,
178 record_id: record_id.to_string(),
179 }
180 }
181 pub fn delete( self, record_id: &str) -> PbDeleteRecord {
182 PbDeleteRecord {
183 url: self.url,
184 token: self.token,
185 collection: self.collection,
186 record_id: record_id.to_string()
187 }
188 }
189}
190
191pub struct PbDeleteRecord {
192 url: String,
193 token: String,
194 collection: String,
195 record_id: String,
196}
197
198impl PbDeleteRecord {
199 pub async fn call (self) -> Result<(), Box<dyn Error>> {
200
201 let req = REQ_CLIENT.delete(format!("{}/api/collections/{}/records/{}", self.url, self.collection, self.record_id))
202 .header("authorization", format!("Bearer {}", self.token))
203 .send().await?;
204
205 if req.status() == 204 {
206 Ok(())
207 }else {
208 let obj: GetListFail = req.json().await?;
209 Err(obj.into())
210 }
211 }
212}
213
214
215pub struct PbUpdateRecord<T> {
216 url: String,
217 token: String,
218 collection: String,
219 record: T,
220 expand: Option<String>,
221 fields: Option<String>,
222 record_id: String
223}
224
225impl<T: Serialize> PbUpdateRecord<T> {
226 pub fn expand(mut self, expand: &str) -> Self {
227 self.expand = Option::from(expand.to_string());
228 self
229 }
230 pub fn fields(mut self, fields: &str) -> Self {
231 self.fields = Option::from(fields.to_string());
232 self
233 }
234 pub async fn call<Y: DeserializeOwned> (self) -> Result<Y, Box<dyn Error>> {
235 let mut query: String = "?".to_string();
236 if self.expand.is_some() {
237 if query.eq("?"){
238 query += &format!("expand={}", self.expand.unwrap());
239 }else {
240 query += &format!("&expand={}", self.expand.unwrap());
241 }
242 }
243 if self.fields.is_some() {
244 if query.eq("?"){
245 query += &format!("fields={}", self.fields.unwrap());
246 }else {
247 query += &format!("&fields={}", self.fields.unwrap());
248 }
249 }
250
251 let req = REQ_CLIENT.patch(format!("{}/api/collections/{}/records/{}{}", self.url, self.collection, self.record_id, query))
252 .header("authorization", format!("Bearer {}", self.token))
253 .json(&self.record)
254 .send().await?;
255
256 if req.status() == 200 {
257 Ok(req.json().await?)
258 }else {
259 let obj: GetListFail = req.json().await?;
260 Err(obj.into())
261 }
262 }
263}
264
265pub struct PbCreateRecord<T> {
266 url: String,
267 token: String,
268 collection: String,
269 record: T,
270 expand: Option<String>,
271 fields: Option<String>,
272}
273
274impl<T: Serialize> PbCreateRecord<T> {
275 pub fn expand(mut self, expand: &str) -> Self {
276 self.expand = Option::from(expand.to_string());
277 self
278 }
279 pub fn fields(mut self, fields: &str) -> Self {
280 self.fields = Option::from(fields.to_string());
281 self
282 }
283 pub async fn call<Y: DeserializeOwned> (self) -> Result<Y, Box<dyn Error>> {
284 let mut query: String = "?".to_string();
285 if self.expand.is_some() {
286 if query.eq("?"){
287 query += &format!("expand={}", self.expand.unwrap());
288 }else {
289 query += &format!("&expand={}", self.expand.unwrap());
290 }
291 }
292 if self.fields.is_some() {
293 if query.eq("?"){
294 query += &format!("fields={}", self.fields.unwrap());
295 }else {
296 query += &format!("&fields={}", self.fields.unwrap());
297 }
298 }
299
300 let req = REQ_CLIENT.post(format!("{}/api/collections/{}/records{}", self.url, self.collection, query))
301 .header("authorization", format!("Bearer {}", self.token))
302 .json(&self.record)
303 .send().await?;
304
305 if req.status() == 200 {
306 Ok(req.json().await?)
307 }else {
308 let obj: GetListFail = req.json().await?;
309 Err(obj.into())
310 }
311 }
312}
313pub struct PbViewRecord {
314 url: String,
315 token: String,
316 collection: String,
317 expand: Option<String>,
318 fields: Option<String>,
319 record_id: String,
320}
321
322impl PbViewRecord {
323 pub fn expand(mut self, expand: &str) -> Self {
324 self.expand = Option::from(expand.to_string());
325 self
326 }
327 pub fn fields(mut self, fields: &str) -> Self {
328 self.fields = Option::from(fields.to_string());
329 self
330 }
331 pub async fn call<T: DeserializeOwned> (self) -> Result<T, Box<dyn Error>> {
332 let mut query: String = "?".to_string();
333
334 if self.expand.is_some() {
335 if query.eq("?"){
336 query += &format!("expand={}", self.expand.unwrap());
337 }else {
338 query += &format!("&expand={}", self.expand.unwrap());
339 }
340 }
341 if self.fields.is_some() {
342 if query.eq("?"){
343 query += &format!("fields={}", self.fields.unwrap());
344 }else {
345 query += &format!("&fields={}", self.fields.unwrap());
346 }
347 }
348
349 let req = REQ_CLIENT.get(format!("{}/api/collections/{}/records/{}{}", self.url, self.collection, self.record_id, query))
350 .header("authorization", format!("Bearer {}", self.token))
351 .send().await?;
352
353 if req.status() == 200 {
354 Ok(req.json().await?)
355 }else {
356 let obj: GetListFail = req.json().await?;
357 Err(obj.into())
358 }
359 }
360}
361
362
363pub struct PbGetList {
364 url: String,
365 token: String,
366 collection: String,
367 page: u16,
368 per_page: u16,
369 sort: Option<String>,
370 filter: Option<String>,
371 expand: Option<String>,
372 fields: Option<String>,
373 skip_total: Option<bool>
374}
375impl PbGetList {
376 pub fn page(mut self, page: u16) -> Self {
377 self.page = page;
378 self
379 }
380 pub fn per_page(mut self, per_page: u16) -> Self {
381 self.per_page = per_page;
382 self
383 }
384 pub fn sort(mut self, sort: &str) -> Self {
385 self.sort = Option::from(sort.to_string());
386 self
387 }
388 pub fn filter(mut self, filter: &str) -> Self {
389 self.filter = Option::from(filter.to_string());
390 self
391 }
392 pub fn expand(mut self, expand: &str) -> Self {
393 self.expand = Option::from(expand.to_string());
394 self
395 }
396 pub fn fields(mut self, fields: &str) -> Self {
397 self.fields = Option::from(fields.to_string());
398 self
399 }
400 pub fn skip_total(mut self, skip_total: bool) -> Self {
401 self.skip_total = Option::from(skip_total);
402 self
403 }
404 pub async fn call<T: DeserializeOwned> (self) -> Result<GetListSuccess<T>, Box<dyn Error>> {
405 let mut query: String = "?".to_string();
406
407 query += &format!("page={}", self.page);
408 query += &format!("&perPage={}", self.per_page);
409 if self.sort.is_some() {
410 query += &format!("&sort={}", self.sort.unwrap());
411 }
412 if self.filter.is_some() {
413 query += &format!("&filter={}", self.filter.unwrap());
414 }
415 if self.expand.is_some() {
416 query += &format!("&expand={}", self.expand.unwrap());
417 }
418 if self.fields.is_some() {
419 query += &format!("&fields=({})", self.fields.unwrap());
420 }
421 if self.skip_total.is_some() {
422 query += &format!("&skipTotal={}", self.skip_total.unwrap());
423 }
424
425 let req = REQ_CLIENT.get(format!("{}/api/collections/{}/records{}", self.url, self.collection, query))
426 .header("authorization", format!("Bearer {}", self.token))
427 .send().await?;
428
429 if req.status() == 200 {
430 Ok(req.json().await?)
431 }else {
432 let obj: GetListFail = req.json().await?;
433 Err(obj.into())
434 }
435
436
437
438
439
440
441
442 }
443}
444
445#[derive(Deserialize, Debug, Serialize)]
446pub struct GetListSuccess<T> {
447 pub page: u16,
448 #[serde(rename = "perPage")]
449 pub per_page: u16,
450 #[serde(rename = "totalItems")]
451 pub total_items: u16,
452 #[serde(rename = "totalPages")]
453 pub total_pages: u16,
454 pub items: Vec<T>
455}
456#[derive(Deserialize, Debug, Serialize)]
457pub struct GetListFail {
458 pub code: u16,
459 pub message: String,
460 pub data: Value
461}
462
463impl Display for GetListFail {
464 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
465 write!(f, "{}", self.message)
466 }
467}
468
469impl Error for GetListFail {}
470
471
472
473
474
475
476#[cfg(test)]
477mod tests {}