1#![warn(missing_docs)]
37
38use serde::{Deserialize, Serialize};
39use url::Url;
40
41const AUR_RPC_URL: &str = "https://aur.archlinux.org/rpc/v5";
43
44#[derive(Serialize, Deserialize, Debug)]
46pub struct Request {
47 pub endpoint: String,
51}
52
53impl Default for Request {
54 fn default() -> Self {
55 Self {
56 endpoint: AUR_RPC_URL.to_string(),
57 }
58 }
59}
60
61#[derive(Serialize, Deserialize, Debug)]
63pub struct ReturnData {
64 version: u32,
66 #[serde(rename = "type")]
67 type_: String,
69 pub resultcount: u32,
71 pub results: Vec<Package>,
74 error: Option<String>,
77}
78
79#[derive(Serialize, Deserialize, Debug)]
81pub struct Package {
82 #[serde(rename = "Name")]
84 pub name: String,
85 #[serde(rename = "Version")]
87 pub version: String,
88 #[serde(rename = "Description")]
90 pub description: Option<String>,
91 #[serde(rename = "Maintainer")]
93 pub maintainer: Option<String>,
94 #[serde(rename = "URL")]
96 pub url: Option<String>,
97 #[serde(rename = "NumVotes")]
99 pub num_votes: u32,
100 #[serde(rename = "Popularity")]
102 pub popularity: f32,
103 #[serde(rename = "OutOfDate")]
105 pub out_of_date: Option<u32>,
106 #[serde(rename = "PackageBase")]
108 pub package_base: String,
109 #[serde(rename = "PackageBaseID")]
111 pub package_base_id: u32,
112 #[serde(rename = "FirstSubmitted")]
114 pub first_submitted: u32,
115 #[serde(rename = "LastModified")]
117 pub last_modified: u32,
118 #[serde(rename = "URLPath")]
120 pub url_path: Option<String>,
121 #[serde(rename = "ID")]
123 pub id: u32,
124
125 #[serde(rename = "Depends")]
129 pub depends: Option<Vec<String>>,
130 #[serde(rename = "MakeDepends")]
132 pub make_depends: Option<Vec<String>>,
133 #[serde(rename = "OptDepends")]
135 pub opt_depends: Option<Vec<String>>,
136 #[serde(rename = "CheckDepends")]
138 pub check_depends: Option<Vec<String>>,
139 #[serde(rename = "Conflicts")]
141 pub conflicts: Option<Vec<String>>,
142 #[serde(rename = "Provides")]
144 pub provides: Option<Vec<String>>,
145 #[serde(rename = "Replaces")]
147 pub replaces: Option<Vec<String>>,
148 #[serde(rename = "Groups")]
150 pub groups: Option<Vec<String>>,
151 #[serde(rename = "License")]
153 pub license: Option<Vec<String>>,
154 #[serde(rename = "Keywords")]
156 pub keywords: Option<Vec<String>>,
157}
158
159impl Request {
160 async fn search(&self, search_term: &str) -> Result<ReturnData, Box<dyn std::error::Error>> {
162 let resp = reqwest::get(search_term).await?.text().await?;
163 let search_response: ReturnData = serde_json::from_str(&resp)?;
164 Ok(search_response)
165 }
166
167 async fn starts_with(&self, url: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
168 let resp = reqwest::get(url).await?.text().await?;
169 let search_response: Vec<String> = serde_json::from_str(&resp)?;
170
171 Ok(search_response)
172 }
173
174 pub async fn search_package_by_name(
182 &self,
183 package: &str,
184 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
185 let url = format!("{}/search/{}?by=name", self.endpoint, package);
186 self.search(&url).await
187 }
188
189 pub async fn search_package_by_name_desc(
191 &self,
192 package: &str,
193 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
194 let url = format!("{}/search/{}?by=name-desc", self.endpoint, package);
195 self.search(&url).await
196 }
197
198 pub async fn search_package_by_depends(
200 &self,
201 package: &str,
202 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
203 let url = format!("{}/search/{}?by=depends", self.endpoint, package);
204 self.search(&url).await
205 }
206
207 pub async fn search_package_by_checkdepends(
209 &self,
210 package: &str,
211 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
212 let url = format!("{}/search/{}?by=checkdepends", self.endpoint, package);
213 self.search(&url).await
214 }
215
216 pub async fn search_package_by_optdepends(
218 &self,
219 package: &str,
220 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
221 let url = format!("{}/search/{}?by=optdepends", self.endpoint, package);
222 self.search(&url).await
223 }
224
225 pub async fn search_package_by_makedepends(
227 &self,
228 package: &str,
229 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
230 let url = format!("{}/search/{}?by=optdepends", self.endpoint, package);
231 self.search(&url).await
232 }
233
234 pub async fn search_package_by_maintainer(
236 &self,
237 package: &str,
238 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
239 let url = format!("{}/search/{}?by=maintainer", self.endpoint, package);
240 self.search(&url).await
241 }
242
243 pub async fn search_package_by_submitter(
245 &self,
246 package: &str,
247 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
248 let url = format!("{}/search/{}?by=submitter", self.endpoint, package);
249 self.search(&url).await
250 }
251
252 pub async fn search_package_by_provides(
254 &self,
255 package: &str,
256 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
257 let url = format!("{}/search/{}?by=provides", self.endpoint, package);
258 self.search(&url).await
259 }
260
261 pub async fn search_package_by_conflicts(
263 &self,
264 package: &str,
265 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
266 let url = format!("{}/search/{}?by=conflicts", self.endpoint, package);
267 self.search(&url).await
268 }
269
270 pub async fn search_package_by_replaces(
272 &self,
273 package: &str,
274 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
275 let url = format!("{}/search/{}?by=replaces", self.endpoint, package);
276 self.search(&url).await
277 }
278
279 pub async fn search_package_by_keywords(
281 &self,
282 package: &str,
283 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
284 let url = format!("{}/search/{}?by=keywords", self.endpoint, package);
285 self.search(&url).await
286 }
287
288 pub async fn search_package_by_groups(
290 &self,
291 package: &str,
292 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
293 let url = format!("{}/search/{}?by=groups", self.endpoint, package);
294 self.search(&url).await
295 }
296
297 pub async fn search_package_by_comaintainers(
299 &self,
300 package: &str,
301 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
302 let url = format!("{}/search/{}?by=comaintainers", self.endpoint, package);
303 self.search(&url).await
304 }
305
306 pub async fn search_info_by_name(
308 &self,
309 package: &str,
310 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
311 let url = format!("{}/info/{}", self.endpoint, package);
312 self.search(&url).await
313 }
314
315 pub async fn search_multi_info_by_names(
317 &self,
318 packages: &[&str],
319 ) -> Result<ReturnData, Box<dyn std::error::Error>> {
320 let url_str = format!("{}/info", self.endpoint);
321
322 let mut args: Vec<(&str, &str)> = vec![];
323
324 for package in packages {
325 args.push(("arg[]", package))
328 }
329
330 let url = Url::parse_with_params(&url_str, args)?;
331
332 return self.search(url.as_str()).await;
333 }
334
335 pub async fn starts_with_name(
338 &self,
339 starts_with: &str,
340 ) -> Result<Vec<String>, Box<dyn std::error::Error>> {
341 let url = format!("{}/suggest/{}", self.endpoint, starts_with);
342 self.starts_with(&url).await
343 }
344
345 pub async fn starts_with_basename(
348 &self,
349 base_start: &str,
350 ) -> Result<Vec<String>, Box<dyn std::error::Error>> {
351 let url = format!("{}/suggest-pkgbase/{}", self.endpoint, base_start);
352 self.starts_with(&url).await
353 }
354}