#[cfg(not(windows))]
use std::collections::HashSet;
#[cfg(not(windows))]
use super::OfficialPkg;
#[cfg(not(windows))]
use super::distro::{artix_repo_names, blackarch_repo_names, cachyos_repo_names, eos_repo_names};
#[cfg(not(windows))]
pub async fn fetch_official_pkg_names()
-> Result<Vec<OfficialPkg>, Box<dyn std::error::Error + Send + Sync>> {
let core = tokio::task::spawn_blocking(|| crate::util::pacman::run_pacman(&["-Sl", "core"]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
let extra = tokio::task::spawn_blocking(|| crate::util::pacman::run_pacman(&["-Sl", "extra"]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
let multilib =
tokio::task::spawn_blocking(|| crate::util::pacman::run_pacman(&["-Sl", "multilib"]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
let mut eos_pairs: Vec<(&str, String)> = Vec::new();
for &repo in eos_repo_names() {
let body =
tokio::task::spawn_blocking(move || crate::util::pacman::run_pacman(&["-Sl", repo]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
eos_pairs.push((repo, body));
}
let mut cach_pairs: Vec<(&str, String)> = Vec::new();
for &repo in cachyos_repo_names() {
let body =
tokio::task::spawn_blocking(move || crate::util::pacman::run_pacman(&["-Sl", repo]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
cach_pairs.push((repo, body));
}
let mut artix_pairs: Vec<(&str, String)> = Vec::new();
for &repo in artix_repo_names() {
let body =
tokio::task::spawn_blocking(move || crate::util::pacman::run_pacman(&["-Sl", repo]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
artix_pairs.push((repo, body));
}
let mut blackarch_pairs: Vec<(&str, String)> = Vec::new();
for &repo in blackarch_repo_names() {
let body =
tokio::task::spawn_blocking(move || crate::util::pacman::run_pacman(&["-Sl", repo]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
blackarch_pairs.push((repo, body));
}
let mut sl_lower_done = HashSet::<String>::new();
for x in ["core", "extra", "multilib"] {
sl_lower_done.insert(x.to_string());
}
for &r in eos_repo_names() {
sl_lower_done.insert(r.to_lowercase());
}
for &r in cachyos_repo_names() {
sl_lower_done.insert(r.to_lowercase());
}
for &r in artix_repo_names() {
sl_lower_done.insert(r.to_lowercase());
}
for &r in blackarch_repo_names() {
sl_lower_done.insert(r.to_lowercase());
}
let extra_repo_names = crate::logic::repos::repos_conf_repo_names_for_index_sl(&sl_lower_done);
let mut repos_conf_pairs: Vec<(String, String)> = Vec::with_capacity(extra_repo_names.len());
for name in extra_repo_names {
let nm = name.clone();
let body =
tokio::task::spawn_blocking(move || crate::util::pacman::run_pacman(&["-Sl", &nm]))
.await
.ok()
.and_then(Result::ok)
.unwrap_or_default();
repos_conf_pairs.push((name, body));
}
let mut pkgs: Vec<OfficialPkg> = Vec::new();
for (repo, text) in [("core", core), ("extra", extra), ("multilib", multilib)]
.into_iter()
.chain(eos_pairs.into_iter())
.chain(cach_pairs.into_iter())
.chain(artix_pairs.into_iter())
.chain(blackarch_pairs.into_iter())
{
for line in text.lines() {
let mut it = line.split_whitespace();
let r = it.next();
let n = it.next();
let v = it.next();
let Some(r) = r else {
continue;
};
let Some(n) = n else {
continue;
};
if r != repo {
continue;
}
pkgs.push(OfficialPkg {
name: n.to_string(),
repo: r.to_string(),
arch: String::new(),
version: v.unwrap_or("").to_string(),
description: String::new(),
});
}
}
for (repo, text) in repos_conf_pairs {
for line in text.lines() {
let mut it = line.split_whitespace();
let r = it.next();
let n = it.next();
let v = it.next();
let Some(r) = r else {
continue;
};
let Some(n) = n else {
continue;
};
if r != repo.as_str() {
continue;
}
pkgs.push(OfficialPkg {
name: n.to_string(),
repo: r.to_string(),
arch: String::new(),
version: v.unwrap_or("").to_string(),
description: String::new(),
});
}
}
pkgs.sort_by(|a, b| a.repo.cmp(&b.repo).then(a.name.cmp(&b.name)));
pkgs.dedup_by(|a, b| a.repo == b.repo && a.name == b.name);
Ok(pkgs)
}
#[cfg(all(test, not(target_os = "windows")))]
mod tests {
#[tokio::test]
#[allow(clippy::await_holding_lock)]
async fn fetch_parses_sl_and_dedups_by_repo_and_name() {
let _guard = crate::global_test_mutex_lock();
let old_path = std::env::var("PATH").unwrap_or_default();
let mut root = std::env::temp_dir();
root.push(format!(
"pacsea_fake_pacman_sl_{}_{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("System time is before UNIX epoch")
.as_nanos()
));
std::fs::create_dir_all(&root).expect("Failed to create test root directory");
let mut bin = root.clone();
bin.push("bin");
std::fs::create_dir_all(&bin).expect("Failed to create test bin directory");
let mut script = bin.clone();
script.push("pacman");
let body = r#"#!/usr/bin/env bash
set -e
if [[ "$1" == "-Sl" ]]; then
repo="$2"
case "$repo" in
core)
echo "core foo 1.0"
echo "core foo 1.0" # duplicate
echo "extra should_not_be_kept 9.9" # different repo, filtered out
;;
extra)
echo "extra foo 1.1"
echo "extra baz 3.0"
;;
*) ;;
esac
exit 0
fi
exit 0
"#;
std::fs::write(&script, body).expect("Failed to write test pacman script");
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perm = std::fs::metadata(&script)
.expect("Failed to read test pacman script metadata")
.permissions();
perm.set_mode(0o755);
std::fs::set_permissions(&script, perm)
.expect("Failed to set test pacman script permissions");
}
let new_path = format!("{}:{old_path}", bin.to_string_lossy());
unsafe { std::env::set_var("PATH", &new_path) };
let pkgs = super::fetch_official_pkg_names()
.await
.expect("Failed to fetch official package names in test");
unsafe { std::env::set_var("PATH", &old_path) };
let _ = std::fs::remove_dir_all(&root);
assert_eq!(pkgs.len(), 3);
let mut tuples: Vec<(String, String, String)> = pkgs
.into_iter()
.map(|p| (p.repo, p.name, p.version))
.collect();
tuples.sort();
assert_eq!(
tuples,
vec![
("core".to_string(), "foo".to_string(), "1.0".to_string()),
("extra".to_string(), "baz".to_string(), "3.0".to_string()),
("extra".to_string(), "foo".to_string(), "1.1".to_string()),
]
);
}
}