Skip to main content

peasy_pdf/
client.rs

1use crate::error::{PeasyError, Result};
2use crate::types::*;
3
4const DEFAULT_BASE_URL: &str = "https://peasypdf.com";
5
6pub struct Client {
7    http: reqwest::Client,
8    base_url: String,
9}
10
11impl Client {
12    pub fn new() -> Self {
13        Self {
14            http: reqwest::Client::new(),
15            base_url: DEFAULT_BASE_URL.to_string(),
16        }
17    }
18
19    pub fn with_base_url(base_url: &str) -> Self {
20        Self {
21            http: reqwest::Client::new(),
22            base_url: base_url.trim_end_matches('/').to_string(),
23        }
24    }
25
26    async fn get(&self, path: &str, params: &[(&str, String)]) -> Result<Vec<u8>> {
27        let url = format!("{}{}", self.base_url, path);
28        let filtered: Vec<(&str, &str)> = params
29            .iter()
30            .filter(|(_, v)| !v.is_empty())
31            .map(|(k, v)| (*k, v.as_str()))
32            .collect();
33
34        let resp = self.http.get(&url).query(&filtered).send().await?;
35        let status = resp.status().as_u16();
36
37        if status == 404 {
38            return Err(PeasyError::NotFound {
39                resource: "resource".into(),
40                identifier: path.into(),
41            });
42        }
43        if status != 200 {
44            let body = resp.text().await.unwrap_or_default();
45            return Err(PeasyError::Api { status, body });
46        }
47        let bytes = resp.bytes().await?;
48        Ok(bytes.to_vec())
49    }
50
51    fn list_params(opts: &ListOptions) -> Vec<(&'static str, String)> {
52        let mut p = Vec::new();
53        if let Some(v) = opts.page {
54            p.push(("page", v.to_string()));
55        }
56        if let Some(v) = opts.limit {
57            p.push(("limit", v.to_string()));
58        }
59        if let Some(ref v) = opts.category {
60            p.push(("category", v.clone()));
61        }
62        if let Some(ref v) = opts.search {
63            p.push(("search", v.clone()));
64        }
65        p
66    }
67
68    pub async fn list_tools(&self, opts: &ListOptions) -> Result<PaginatedResponse<Tool>> {
69        let body = self.get("/api/v1/tools/", &Self::list_params(opts)).await?;
70        Ok(serde_json::from_slice(&body)?)
71    }
72
73    pub async fn get_tool(&self, slug: &str) -> Result<Tool> {
74        let body = self.get(&format!("/api/v1/tools/{}/", slug), &[]).await?;
75        Ok(serde_json::from_slice(&body)?)
76    }
77
78    pub async fn list_categories(
79        &self,
80        opts: &ListOptions,
81    ) -> Result<PaginatedResponse<Category>> {
82        let mut params = Vec::new();
83        if let Some(v) = opts.page {
84            params.push(("page", v.to_string()));
85        }
86        if let Some(v) = opts.limit {
87            params.push(("limit", v.to_string()));
88        }
89        let body = self.get("/api/v1/categories/", &params).await?;
90        Ok(serde_json::from_slice(&body)?)
91    }
92
93    pub async fn list_formats(&self, opts: &ListOptions) -> Result<PaginatedResponse<Format>> {
94        let body = self
95            .get("/api/v1/formats/", &Self::list_params(opts))
96            .await?;
97        Ok(serde_json::from_slice(&body)?)
98    }
99
100    pub async fn get_format(&self, slug: &str) -> Result<Format> {
101        let body = self
102            .get(&format!("/api/v1/formats/{}/", slug), &[])
103            .await?;
104        Ok(serde_json::from_slice(&body)?)
105    }
106
107    pub async fn list_conversions(
108        &self,
109        opts: &ListConversionsOptions,
110    ) -> Result<PaginatedResponse<Conversion>> {
111        let mut params = Vec::new();
112        if let Some(v) = opts.page {
113            params.push(("page", v.to_string()));
114        }
115        if let Some(v) = opts.limit {
116            params.push(("limit", v.to_string()));
117        }
118        if let Some(ref v) = opts.source {
119            params.push(("source", v.clone()));
120        }
121        if let Some(ref v) = opts.target {
122            params.push(("target", v.clone()));
123        }
124        let body = self.get("/api/v1/conversions/", &params).await?;
125        Ok(serde_json::from_slice(&body)?)
126    }
127
128    pub async fn list_glossary(
129        &self,
130        opts: &ListOptions,
131    ) -> Result<PaginatedResponse<GlossaryTerm>> {
132        let body = self
133            .get("/api/v1/glossary/", &Self::list_params(opts))
134            .await?;
135        Ok(serde_json::from_slice(&body)?)
136    }
137
138    pub async fn get_glossary_term(&self, slug: &str) -> Result<GlossaryTerm> {
139        let body = self
140            .get(&format!("/api/v1/glossary/{}/", slug), &[])
141            .await?;
142        Ok(serde_json::from_slice(&body)?)
143    }
144
145    pub async fn list_guides(
146        &self,
147        opts: &ListGuidesOptions,
148    ) -> Result<PaginatedResponse<Guide>> {
149        let mut params = Vec::new();
150        if let Some(v) = opts.page {
151            params.push(("page", v.to_string()));
152        }
153        if let Some(v) = opts.limit {
154            params.push(("limit", v.to_string()));
155        }
156        if let Some(ref v) = opts.category {
157            params.push(("category", v.clone()));
158        }
159        if let Some(ref v) = opts.audience_level {
160            params.push(("audience_level", v.clone()));
161        }
162        if let Some(ref v) = opts.search {
163            params.push(("search", v.clone()));
164        }
165        let body = self.get("/api/v1/guides/", &params).await?;
166        Ok(serde_json::from_slice(&body)?)
167    }
168
169    pub async fn get_guide(&self, slug: &str) -> Result<Guide> {
170        let body = self
171            .get(&format!("/api/v1/guides/{}/", slug), &[])
172            .await?;
173        Ok(serde_json::from_slice(&body)?)
174    }
175
176    pub async fn list_use_cases(
177        &self,
178        opts: &ListOptions,
179    ) -> Result<PaginatedResponse<UseCase>> {
180        let mut params = Vec::new();
181        if let Some(v) = opts.page {
182            params.push(("page", v.to_string()));
183        }
184        if let Some(v) = opts.limit {
185            params.push(("limit", v.to_string()));
186        }
187        if let Some(ref v) = opts.category {
188            params.push(("industry", v.clone()));
189        }
190        if let Some(ref v) = opts.search {
191            params.push(("search", v.clone()));
192        }
193        let body = self.get("/api/v1/use-cases/", &params).await?;
194        Ok(serde_json::from_slice(&body)?)
195    }
196
197    pub async fn search(&self, query: &str, limit: Option<u32>) -> Result<SearchResult> {
198        let mut params = vec![("q", query.to_string())];
199        if let Some(v) = limit {
200            params.push(("limit", v.to_string()));
201        }
202        let body = self.get("/api/v1/search/", &params).await?;
203        Ok(serde_json::from_slice(&body)?)
204    }
205
206    pub async fn list_sites(&self) -> Result<PaginatedResponse<Site>> {
207        let body = self.get("/api/v1/sites/", &[]).await?;
208        Ok(serde_json::from_slice(&body)?)
209    }
210
211    pub async fn openapi_spec(&self) -> Result<serde_json::Value> {
212        let body = self.get("/api/openapi.json", &[]).await?;
213        Ok(serde_json::from_slice(&body)?)
214    }
215}
216
217impl Default for Client {
218    fn default() -> Self {
219        Self::new()
220    }
221}