pacsea/util/
srcinfo.rs

1//! .SRCINFO fetching utilities for AUR packages.
2//!
3//! This module provides functions for fetching .SRCINFO files from the AUR,
4//! with support for both synchronous (curl) and asynchronous (reqwest) fetching.
5
6use crate::util::{curl, percent_encode};
7
8/// What: Fetch .SRCINFO content for an AUR package synchronously using curl.
9///
10/// Inputs:
11/// - `name`: AUR package name.
12/// - `timeout_seconds`: Optional timeout in seconds (None = no timeout).
13///
14/// Output:
15/// - Returns .SRCINFO content as a string, or an error if fetch fails.
16///
17/// # Errors
18/// - Returns `Err` when network request fails (curl execution error)
19/// - Returns `Err` when .SRCINFO cannot be fetched from AUR
20/// - Returns `Err` when response is empty or contains HTML error page
21/// - Returns `Err` when response does not appear to be valid .SRCINFO format
22///
23/// Details:
24/// - Downloads .SRCINFO from AUR cgit repository.
25/// - Validates that the response is not empty, not HTML, and contains .SRCINFO format markers.
26pub fn fetch_srcinfo(name: &str, timeout_seconds: Option<u64>) -> Result<String, String> {
27    let url = format!(
28        "https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h={}",
29        percent_encode(name)
30    );
31    tracing::debug!("Fetching .SRCINFO from: {}", url);
32
33    let text = if let Some(timeout) = timeout_seconds {
34        let timeout_str = timeout.to_string();
35        curl::curl_text_with_args(&url, &["--max-time", &timeout_str])
36            .map_err(|e| format!("curl failed: {e}"))?
37    } else {
38        curl::curl_text(&url).map_err(|e| format!("curl failed: {e}"))?
39    };
40
41    if text.trim().is_empty() {
42        return Err("Empty .SRCINFO content".to_string());
43    }
44
45    // Check if we got an HTML error page instead of .SRCINFO content
46    if text.trim_start().starts_with("<html") || text.trim_start().starts_with("<!DOCTYPE") {
47        return Err("Received HTML error page instead of .SRCINFO".to_string());
48    }
49
50    // Validate that it looks like .SRCINFO format (should have pkgbase or pkgname)
51    if !text.contains("pkgbase =") && !text.contains("pkgname =") {
52        return Err("Response does not appear to be valid .SRCINFO format".to_string());
53    }
54
55    Ok(text)
56}
57
58/// What: Fetch .SRCINFO content for an AUR package using async HTTP.
59///
60/// Inputs:
61/// - `client`: Reqwest HTTP client.
62/// - `name`: AUR package name.
63///
64/// Output:
65/// - Returns .SRCINFO content as a string, or an error if fetch fails.
66///
67/// # Errors
68/// - Returns `Err` when HTTP request fails (network error or client error)
69/// - Returns `Err` when HTTP response status is not successful
70/// - Returns `Err` when response body cannot be read
71/// - Returns `Err` when response is empty or contains HTML error page
72///
73/// Details:
74/// - Uses reqwest for async fetching with built-in timeout handling.
75/// - Validates that the response is not empty and not HTML.
76pub async fn fetch_srcinfo_async(client: &reqwest::Client, name: &str) -> Result<String, String> {
77    let url = format!(
78        "https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h={}",
79        percent_encode(name)
80    );
81    tracing::debug!("Fetching .SRCINFO from: {}", url);
82
83    let response = client
84        .get(&url)
85        .send()
86        .await
87        .map_err(|e| format!("HTTP request failed: {e}"))?;
88
89    if !response.status().is_success() {
90        return Err(format!(
91            "HTTP request failed with status: {}",
92            response.status()
93        ));
94    }
95
96    let text = response
97        .text()
98        .await
99        .map_err(|e| format!("Failed to read response body: {e}"))?;
100
101    if text.trim().is_empty() {
102        return Err("Empty .SRCINFO content".to_string());
103    }
104
105    // Check if we got an HTML error page instead of .SRCINFO content
106    if text.trim_start().starts_with("<html") || text.trim_start().starts_with("<!DOCTYPE") {
107        return Err("Received HTML error page instead of .SRCINFO".to_string());
108    }
109
110    Ok(text)
111}