use std::collections::HashSet;
use std::process::{Command, Stdio};
use crate::error::{ArchToolkitError, Result};
#[allow(clippy::implicit_hasher)]
pub fn refresh_installed_cache(cache: Option<&mut HashSet<String>>) -> Result<HashSet<String>> {
tracing::debug!("Running: pacman -Qq");
let output = Command::new("pacman")
.args(["-Qq"])
.env("LC_ALL", "C")
.env("LANG", "C")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output();
let packages = match output {
Ok(output) => {
if output.status.success() {
let text = String::from_utf8_lossy(&output.stdout);
let packages: HashSet<String> = text
.lines()
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
tracing::debug!(
"Successfully retrieved {} installed packages",
packages.len()
);
packages
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
tracing::error!(
"pacman -Qq failed with status {:?}: {}",
output.status.code(),
stderr
);
HashSet::new()
}
}
Err(e) => {
tracing::error!("Failed to execute pacman -Qq: {}", e);
HashSet::new()
}
};
if let Some(cache_ref) = cache {
cache_ref.clone_from(&packages);
}
Ok(packages)
}
#[cfg(feature = "index")]
#[allow(clippy::implicit_hasher)]
pub async fn refresh_installed_cache_async(
cache: Option<&mut HashSet<String>>,
) -> Result<HashSet<String>> {
let result = tokio::task::spawn_blocking(|| refresh_installed_cache(None))
.await
.map_err(|e| ArchToolkitError::Parse(format!("Blocking task failed: {e}")))?;
if let (Ok(packages), Some(cache_ref)) = (result.as_ref(), cache) {
cache_ref.clone_from(packages);
}
result
}
#[must_use]
#[allow(clippy::implicit_hasher)]
pub fn is_installed(name: &str, cache: Option<&HashSet<String>>) -> bool {
if let Some(cache_ref) = cache {
return cache_ref.contains(name);
}
tracing::debug!("Running: pacman -Q {}", name);
let output = Command::new("pacman")
.args(["-Q", name])
.env("LC_ALL", "C")
.env("LANG", "C")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output();
match output {
Ok(output) => output.status.success(),
Err(e) => {
tracing::error!("Failed to execute pacman -Q {}: {}", name, e);
false
}
}
}
pub fn get_installed_packages() -> Result<HashSet<String>> {
refresh_installed_cache(None)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn refresh_installed_cache_updates_cache() {
let mut cache = HashSet::new();
let _result = refresh_installed_cache(Some(&mut cache));
}
#[test]
fn refresh_installed_cache_without_cache() {
let result = refresh_installed_cache(None);
assert!(result.is_ok());
}
#[test]
fn is_installed_uses_cache() {
let cache = HashSet::from(["vim".to_string(), "git".to_string()]);
assert!(is_installed("vim", Some(&cache)));
assert!(is_installed("git", Some(&cache)));
assert!(!is_installed("nonexistent", Some(&cache)));
}
#[test]
fn is_installed_without_cache() {
let _result = is_installed("vim", None);
}
#[test]
fn get_installed_packages_returns_hashset() {
let result = get_installed_packages();
assert!(result.is_ok());
}
#[cfg(feature = "index")]
#[tokio::test]
async fn refresh_installed_cache_async_works() {
let mut cache = HashSet::new();
let result = refresh_installed_cache_async(Some(&mut cache)).await;
assert!(result.is_ok());
}
}