1use crate::oauth::OAuth;
2use crate::types::{FileDetail, FilesPage};
3use crate::{Result, API_BASE};
4use serde::de::DeserializeOwned;
5use serde_json::Value;
6
7pub struct Client {
8 oauth: OAuth,
9}
10
11impl Default for Client {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl Client {
18 pub fn new() -> Self {
19 Client {
20 oauth: OAuth::new(),
21 }
22 }
23
24 pub fn oauth(&self) -> &OAuth {
25 &self.oauth
26 }
27
28 fn request<T: DeserializeOwned>(&self, method: &str, path: &str) -> Result<T> {
29 let token = self
30 .oauth
31 .get_access_token()?
32 .ok_or("Not authenticated. Run `plaud login` first.")?;
33 let url = format!("{API_BASE}{path}");
34 let req = if method == "POST" {
35 ureq::post(&url)
36 } else {
37 ureq::get(&url)
38 };
39 match req
40 .set("Authorization", &format!("Bearer {token}"))
41 .set("Accept", "application/json")
42 .call()
43 {
44 Ok(r) => Ok(r.into_json::<T>()?),
45 Err(ureq::Error::Status(code, r)) => {
46 let body = r.into_string().unwrap_or_default();
47 if code == 422 {
48 if let Ok(v) = serde_json::from_str::<Value>(&body) {
49 if let Some(detail) = v.get("detail").and_then(|d| d.as_array()) {
50 let msgs: Vec<String> = detail
51 .iter()
52 .map(|d| {
53 let loc = d
54 .get("loc")
55 .and_then(|l| l.as_array())
56 .and_then(|a| a.last())
57 .and_then(|x| x.as_str())
58 .unwrap_or("");
59 let m = d.get("msg").and_then(|x| x.as_str()).unwrap_or("");
60 format!("{loc}: {m}")
61 })
62 .collect();
63 return Err(msgs.join("; ").into());
64 }
65 }
66 }
67 Err(format!("API error: {code}").into())
68 }
69 Err(e) => Err(e.into()),
70 }
71 }
72
73 pub fn get_current_user(&self) -> Result<Value> {
74 self.request("GET", "/open/third-party/users/current")
75 }
76
77 pub fn revoke_current_user(&self) {
79 let _ = self.request::<Value>("POST", "/open/third-party/users/current/revoke");
80 }
81
82 pub fn list_files(&self, page: u32, page_size: u32) -> Result<FilesPage> {
83 self.request(
84 "GET",
85 &format!("/open/third-party/files/?page={page}&page_size={page_size}"),
86 )
87 }
88
89 pub fn get_file(&self, id: &str) -> Result<FileDetail> {
90 self.request("GET", &format!("/open/third-party/files/{id}"))
91 }
92}