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(all(feature = "aur", feature = "index"))]
10mod official;
11#[cfg(feature = "aur")]
12mod pkgbuild;
13#[cfg(feature = "aur")]
14mod search;
15#[cfg(feature = "aur")]
16mod traits;
17#[cfg(feature = "aur")]
18pub mod utils;
19#[cfg(feature = "aur")]
20pub mod validation;
21
22#[cfg(feature = "aur")]
23use crate::client::ArchClient;
24#[cfg(feature = "aur")]
25use crate::error::Result;
26#[cfg(feature = "aur")]
27use crate::types::{AurComment, AurPackage, AurPackageDetails};
28
29#[cfg(all(feature = "aur", feature = "index"))]
30pub use crate::types::package::{
31 MetadataFetchLimits, MirrorHealth, MirrorHealthLimits, MirrorHealthStatus,
32};
33#[cfg(feature = "aur")]
34pub use mock::MockAurApi;
35#[cfg(all(feature = "aur", feature = "index"))]
36pub use official::{
37 ARCH_PACKAGE_SEARCH_URL, check_mirror_health, fetch_arch_package_detail,
38 fetch_official_package_detail_from,
39};
40#[cfg(feature = "aur")]
41pub use traits::AurApi;
42
43/// What: Wrapper for AUR operations using an `ArchClient`.
44///
45/// Inputs: None (created via `ArchClient::aur()`)
46///
47/// Output: `Aur` instance that provides AUR operation methods
48///
49/// Details:
50/// - Holds a reference to `ArchClient` to access HTTP client and configuration
51/// - Provides methods: `search()`, `info()`, `comments()`, `pkgbuild()`
52/// - All operations use the client's configured timeout and user agent
53/// - Rate limiting is handled automatically
54#[cfg(feature = "aur")]
55#[derive(Debug)]
56pub struct Aur<'a> {
57 /// Reference to the parent `ArchClient`.
58 client: &'a ArchClient,
59}
60
61#[cfg(feature = "aur")]
62impl<'a> Aur<'a> {
63 /// What: Create a new `Aur` wrapper for the given client.
64 ///
65 /// Inputs:
66 /// - `client`: Reference to `ArchClient` to use for operations
67 ///
68 /// Output:
69 /// - `Aur` wrapper instance
70 ///
71 /// Details:
72 /// - Internal constructor, typically called via `ArchClient::aur()`
73 /// - The wrapper uses the client's HTTP client and configuration
74 pub(crate) const fn new(client: &'a ArchClient) -> Self {
75 Self { client }
76 }
77
78 /// What: Search for packages in the AUR by name.
79 ///
80 /// Inputs:
81 /// - `query`: Search query string.
82 ///
83 /// Output:
84 /// - `Result<Vec<AurPackage>>` containing search results, or an error.
85 ///
86 /// Details:
87 /// - Uses AUR RPC v5 search endpoint.
88 /// - Returns the full result array supplied by AUR RPC without assuming an
89 /// upstream 200-result cap or unsupported pagination parameter.
90 /// - Use [`Aur::search_with_limit`] when the caller needs an explicit,
91 /// deterministic client-side result cap.
92 /// - Percent-encodes the query string for URL safety.
93 /// - Applies rate limiting for archlinux.org requests.
94 /// - Returns empty vector if no results found (not an error).
95 ///
96 /// # Errors
97 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
98 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
99 pub async fn search(&self, query: &str) -> Result<Vec<AurPackage>> {
100 search::search(self.client, query).await
101 }
102
103 /// What: Search AUR packages with an explicit caller-selected result cap.
104 ///
105 /// Inputs:
106 /// - `query`: Search query string.
107 /// - `maximum_results`: Non-zero maximum number of returned package rows.
108 ///
109 /// Output:
110 /// - At most `maximum_results` AUR package rows, in the RPC response order.
111 ///
112 /// Details:
113 /// - The cap is applied only after one normal AUR RPC response is fetched;
114 /// it does not claim server-side pagination or an upstream result limit.
115 /// - [`Aur::search`] remains uncapped so callers can make their own bounded
116 /// display/storage choice.
117 /// - The client cache retains the complete fetched array, so different
118 /// caller limits do not make cache contents depend on call order.
119 ///
120 /// # Errors
121 /// - Returns the same validation, network, and parsing errors as [`Aur::search`].
122 pub async fn search_with_limit(
123 &self,
124 query: &str,
125 maximum_results: std::num::NonZeroUsize,
126 ) -> Result<Vec<AurPackage>> {
127 search::search_with_limit(self.client, query, maximum_results).await
128 }
129
130 /// What: Fetch detailed information for one or more AUR packages.
131 ///
132 /// Inputs:
133 /// - `names`: Slice of package names to fetch info for.
134 ///
135 /// Output:
136 /// - `Result<Vec<AurPackageDetails>>` containing package details, or an error.
137 ///
138 /// Details:
139 /// - Uses AUR RPC v5 info endpoint.
140 /// - Fetches info for all packages in a single request (more efficient).
141 /// - Returns empty vector if no packages found (not an error).
142 /// - Applies rate limiting for archlinux.org requests.
143 ///
144 /// # Errors
145 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
146 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
147 pub async fn info(&self, names: &[&str]) -> Result<Vec<AurPackageDetails>> {
148 info::info(self.client, names).await
149 }
150
151 /// What: Fetch AUR package comments by scraping the AUR package page.
152 ///
153 /// Inputs:
154 /// - `pkgname`: Package name to fetch comments for.
155 ///
156 /// Output:
157 /// - `Result<Vec<AurComment>>` with parsed comments sorted by date (latest first); `Err` on failure.
158 ///
159 /// Details:
160 /// - Fetches HTML from `https://aur.archlinux.org/packages/<pkgname>`
161 /// - Uses `scraper` to parse HTML and extract comment elements
162 /// - Parses dates to Unix timestamps for sorting
163 /// - Sorts comments by date descending (latest first)
164 /// - Handles pinned comments (appear before "Latest Comments" heading)
165 ///
166 /// # Errors
167 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
168 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
169 /// - Returns `Err(ArchToolkitError::Parse)` if HTML parsing fails
170 pub async fn comments(&self, pkgname: &str) -> Result<Vec<AurComment>> {
171 comments::comments(self.client, pkgname).await
172 }
173
174 /// What: Fetch PKGBUILD content for an AUR package.
175 ///
176 /// Inputs:
177 /// - `package`: Package name to fetch PKGBUILD for.
178 ///
179 /// Output:
180 /// - `Result<String>` with PKGBUILD text when available; `Err` on network or lookup failure.
181 ///
182 /// Details:
183 /// - Fetches from `https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h={package}`
184 /// - Applies rate limiting (200ms minimum interval between requests)
185 /// - Uses timeout from client configuration
186 /// - Returns raw PKGBUILD text
187 ///
188 /// # Errors
189 /// - Returns `Err(ArchToolkitError::Network)` if the HTTP request fails
190 /// - Returns `Err(ArchToolkitError::InvalidInput)` if the URL is not from archlinux.org
191 /// - Returns `Err(ArchToolkitError::Parse)` if rate limiter mutex is poisoned
192 pub async fn pkgbuild(&self, package: &str) -> Result<String> {
193 pkgbuild::pkgbuild(self.client, package).await
194 }
195}
196
197#[cfg(feature = "aur")]
198use async_trait::async_trait;
199
200#[cfg(feature = "aur")]
201#[async_trait]
202impl AurApi for Aur<'_> {
203 /// What: Search for packages in the AUR by name.
204 ///
205 /// Inputs:
206 /// - `query`: Search query string
207 ///
208 /// Output:
209 /// - `Result<Vec<AurPackage>>` containing search results, or an error
210 ///
211 /// Details:
212 /// - Delegates to the underlying search module function
213 async fn search(&self, query: &str) -> Result<Vec<AurPackage>> {
214 search::search(self.client, query).await
215 }
216
217 /// What: Fetch detailed information for one or more AUR packages.
218 ///
219 /// Inputs:
220 /// - `names`: Slice of package names to fetch info for
221 ///
222 /// Output:
223 /// - `Result<Vec<AurPackageDetails>>` containing package details, or an error
224 ///
225 /// Details:
226 /// - Delegates to the underlying info module function
227 async fn info(&self, names: &[&str]) -> Result<Vec<AurPackageDetails>> {
228 info::info(self.client, names).await
229 }
230
231 /// What: Fetch AUR package comments.
232 ///
233 /// Inputs:
234 /// - `pkgname`: Package name to fetch comments for
235 ///
236 /// Output:
237 /// - `Result<Vec<AurComment>>` with parsed comments, or an error
238 ///
239 /// Details:
240 /// - Delegates to the underlying comments module function
241 async fn comments(&self, pkgname: &str) -> Result<Vec<AurComment>> {
242 comments::comments(self.client, pkgname).await
243 }
244
245 /// What: Fetch PKGBUILD content for an AUR package.
246 ///
247 /// Inputs:
248 /// - `package`: Package name to fetch PKGBUILD for
249 ///
250 /// Output:
251 /// - `Result<String>` with PKGBUILD text, or an error
252 ///
253 /// Details:
254 /// - Delegates to the underlying pkgbuild module function
255 async fn pkgbuild(&self, package: &str) -> Result<String> {
256 pkgbuild::pkgbuild(self.client, package).await
257 }
258}