arch_toolkit/aur/mod.rs
1//! AUR (Arch User Repository) operations.
2
3#[cfg(feature = "aur")]
4mod comments;
5#[cfg(feature = "aur")]
6mod info;
7#[cfg(feature = "aur")]
8mod mock;
9#[cfg(feature = "aur")]
10mod pkgbuild;
11#[cfg(feature = "aur")]
12mod search;
13#[cfg(feature = "aur")]
14mod traits;
15#[cfg(feature = "aur")]
16pub mod utils;
17#[cfg(feature = "aur")]
18pub mod validation;
19
20#[cfg(feature = "aur")]
21use crate::client::ArchClient;
22#[cfg(feature = "aur")]
23use crate::error::Result;
24#[cfg(feature = "aur")]
25use crate::types::{AurComment, AurPackage, AurPackageDetails};
26
27#[cfg(feature = "aur")]
28pub use mock::MockAurApi;
29#[cfg(feature = "aur")]
30pub use traits::AurApi;
31
32/// What: Wrapper for AUR operations using an `ArchClient`.
33///
34/// Inputs: None (created via `ArchClient::aur()`)
35///
36/// Output: `Aur` instance that provides AUR operation methods
37///
38/// Details:
39/// - Holds a reference to `ArchClient` to access HTTP client and configuration
40/// - Provides methods: `search()`, `info()`, `comments()`, `pkgbuild()`
41/// - All operations use the client's configured timeout and user agent
42/// - Rate limiting is handled automatically
43#[cfg(feature = "aur")]
44#[derive(Debug)]
45pub struct Aur<'a> {
46 /// Reference to the parent `ArchClient`.
47 client: &'a ArchClient,
48}
49
50#[cfg(feature = "aur")]
51impl<'a> Aur<'a> {
52 /// What: Create a new `Aur` wrapper for the given client.
53 ///
54 /// Inputs:
55 /// - `client`: Reference to `ArchClient` to use for operations
56 ///
57 /// Output:
58 /// - `Aur` wrapper instance
59 ///
60 /// Details:
61 /// - Internal constructor, typically called via `ArchClient::aur()`
62 /// - The wrapper uses the client's HTTP client and configuration
63 pub(crate) const fn new(client: &'a ArchClient) -> Self {
64 Self { client }
65 }
66
67 /// What: Search for packages in the AUR by name.
68 ///
69 /// Inputs:
70 /// - `query`: Search query string.
71 ///
72 /// Output:
73 /// - `Result<Vec<AurPackage>>` containing search results, or an error.
74 ///
75 /// Details:
76 /// - Uses AUR RPC v5 search endpoint.
77 /// - Limits results to 200 packages (AUR default).
78 /// - Percent-encodes the query string for URL safety.
79 /// - Applies rate limiting for archlinux.org requests.
80 /// - Returns empty vector if no results found (not an error).
81 ///
82 /// # Errors
83 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
84 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
85 pub async fn search(&self, query: &str) -> Result<Vec<AurPackage>> {
86 search::search(self.client, query).await
87 }
88
89 /// What: Fetch detailed information for one or more AUR packages.
90 ///
91 /// Inputs:
92 /// - `names`: Slice of package names to fetch info for.
93 ///
94 /// Output:
95 /// - `Result<Vec<AurPackageDetails>>` containing package details, or an error.
96 ///
97 /// Details:
98 /// - Uses AUR RPC v5 info endpoint.
99 /// - Fetches info for all packages in a single request (more efficient).
100 /// - Returns empty vector if no packages found (not an error).
101 /// - Applies rate limiting for archlinux.org requests.
102 ///
103 /// # Errors
104 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
105 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
106 pub async fn info(&self, names: &[&str]) -> Result<Vec<AurPackageDetails>> {
107 info::info(self.client, names).await
108 }
109
110 /// What: Fetch AUR package comments by scraping the AUR package page.
111 ///
112 /// Inputs:
113 /// - `pkgname`: Package name to fetch comments for.
114 ///
115 /// Output:
116 /// - `Result<Vec<AurComment>>` with parsed comments sorted by date (latest first); `Err` on failure.
117 ///
118 /// Details:
119 /// - Fetches HTML from `https://aur.archlinux.org/packages/<pkgname>`
120 /// - Uses `scraper` to parse HTML and extract comment elements
121 /// - Parses dates to Unix timestamps for sorting
122 /// - Sorts comments by date descending (latest first)
123 /// - Handles pinned comments (appear before "Latest Comments" heading)
124 ///
125 /// # Errors
126 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
127 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
128 /// - Returns `Err(ArchToolkitError::Parse)` if HTML parsing fails
129 pub async fn comments(&self, pkgname: &str) -> Result<Vec<AurComment>> {
130 comments::comments(self.client, pkgname).await
131 }
132
133 /// What: Fetch PKGBUILD content for an AUR package.
134 ///
135 /// Inputs:
136 /// - `package`: Package name to fetch PKGBUILD for.
137 ///
138 /// Output:
139 /// - `Result<String>` with PKGBUILD text when available; `Err` on network or lookup failure.
140 ///
141 /// Details:
142 /// - Fetches from `https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h={package}`
143 /// - Applies rate limiting (200ms minimum interval between requests)
144 /// - Uses timeout from client configuration
145 /// - Returns raw PKGBUILD text
146 ///
147 /// # Errors
148 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
149 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
150 /// - Returns `Err(ArchToolkitError::Parse)` if rate limiter mutex is poisoned
151 pub async fn pkgbuild(&self, package: &str) -> Result<String> {
152 pkgbuild::pkgbuild(self.client, package).await
153 }
154}
155
156#[cfg(feature = "aur")]
157use async_trait::async_trait;
158
159#[cfg(feature = "aur")]
160#[async_trait]
161impl AurApi for Aur<'_> {
162 /// What: Search for packages in the AUR by name.
163 ///
164 /// Inputs:
165 /// - `query`: Search query string
166 ///
167 /// Output:
168 /// - `Result<Vec<AurPackage>>` containing search results, or an error
169 ///
170 /// Details:
171 /// - Delegates to the underlying search module function
172 async fn search(&self, query: &str) -> Result<Vec<AurPackage>> {
173 search::search(self.client, query).await
174 }
175
176 /// What: Fetch detailed information for one or more AUR packages.
177 ///
178 /// Inputs:
179 /// - `names`: Slice of package names to fetch info for
180 ///
181 /// Output:
182 /// - `Result<Vec<AurPackageDetails>>` containing package details, or an error
183 ///
184 /// Details:
185 /// - Delegates to the underlying info module function
186 async fn info(&self, names: &[&str]) -> Result<Vec<AurPackageDetails>> {
187 info::info(self.client, names).await
188 }
189
190 /// What: Fetch AUR package comments.
191 ///
192 /// Inputs:
193 /// - `pkgname`: Package name to fetch comments for
194 ///
195 /// Output:
196 /// - `Result<Vec<AurComment>>` with parsed comments, or an error
197 ///
198 /// Details:
199 /// - Delegates to the underlying comments module function
200 async fn comments(&self, pkgname: &str) -> Result<Vec<AurComment>> {
201 comments::comments(self.client, pkgname).await
202 }
203
204 /// What: Fetch PKGBUILD content for an AUR package.
205 ///
206 /// Inputs:
207 /// - `package`: Package name to fetch PKGBUILD for
208 ///
209 /// Output:
210 /// - `Result<String>` with PKGBUILD text, or an error
211 ///
212 /// Details:
213 /// - Delegates to the underlying pkgbuild module function
214 async fn pkgbuild(&self, package: &str) -> Result<String> {
215 pkgbuild::pkgbuild(self.client, package).await
216 }
217}