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}