use crate::types::dependency::DependencySource;
use std::collections::HashSet;
use std::hash::BuildHasher;
use std::process::{Command, Stdio};
pub fn determine_dependency_source<S: BuildHasher>(
name: &str,
installed: &HashSet<String, S>,
) -> (DependencySource, bool) {
if !installed.contains(name) {
let output = Command::new("pacman")
.args(["-Si", name])
.env("LC_ALL", "C")
.env("LANG", "C")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.output();
if let Ok(output) = output
&& output.status.success()
{
let text = String::from_utf8_lossy(&output.stdout);
for line in text.lines() {
if line.starts_with("Repository")
&& let Some(colon_pos) = line.find(':')
{
let repo = line[colon_pos + 1..].trim().to_lowercase();
let is_core = repo == "core";
return (DependencySource::Official { repo }, is_core);
}
}
return (
DependencySource::Official {
repo: "extra".to_string(),
},
false,
);
}
tracing::debug!(
"Package {} not found in official repos and not installed - will be marked as Missing (skipping AUR check)",
name
);
return (DependencySource::Aur, false);
}
let output = Command::new("pacman")
.args(["-Qi", name])
.env("LC_ALL", "C")
.env("LANG", "C")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output();
match output {
Ok(output) if output.status.success() => {
let text = String::from_utf8_lossy(&output.stdout);
for line in text.lines() {
if line.starts_with("Repository")
&& let Some(colon_pos) = line.find(':')
{
let repo = line[colon_pos + 1..].trim().to_lowercase();
let is_core = repo == "core";
if repo == "local" || repo.is_empty() {
return (DependencySource::Local, false);
}
return (DependencySource::Official { repo }, is_core);
}
}
}
_ => {
tracing::debug!(
"Could not determine repository for {}, assuming official",
name
);
}
}
let is_core = is_system_package(name);
(
DependencySource::Official {
repo: if is_core {
"core".to_string()
} else {
"extra".to_string()
},
},
is_core,
)
}
#[must_use]
pub fn is_system_package(name: &str) -> bool {
let system_packages = [
"glibc",
"linux",
"systemd",
"pacman",
"bash",
"coreutils",
"gcc",
"binutils",
"filesystem",
"util-linux",
"shadow",
"sed",
"grep",
];
system_packages.contains(&name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_system_package_detects_core() {
assert!(is_system_package("glibc"));
assert!(is_system_package("linux"));
assert!(is_system_package("systemd"));
assert!(is_system_package("pacman"));
assert!(is_system_package("bash"));
assert!(is_system_package("coreutils"));
assert!(is_system_package("gcc"));
assert!(is_system_package("binutils"));
assert!(is_system_package("filesystem"));
assert!(is_system_package("util-linux"));
assert!(is_system_package("shadow"));
assert!(is_system_package("sed"));
assert!(is_system_package("grep"));
assert!(!is_system_package("firefox"));
assert!(!is_system_package("vim"));
assert!(!is_system_package("nonexistent"));
assert!(!is_system_package(""));
}
#[test]
fn test_determine_dependency_source_installed_core() {
let sample_output = "Repository : core\nName : glibc\n";
let mut found_repo = None;
for line in sample_output.lines() {
if line.starts_with("Repository")
&& let Some(colon_pos) = line.find(':')
{
let repo = line[colon_pos + 1..].trim().to_lowercase();
let is_core = repo == "core";
found_repo = Some((repo, is_core));
break;
}
}
assert_eq!(found_repo, Some(("core".to_string(), true)));
}
#[test]
fn test_determine_dependency_source_installed_extra() {
let sample_output = "Repository : extra\nName : firefox\n";
let mut found_repo = None;
for line in sample_output.lines() {
if line.starts_with("Repository")
&& let Some(colon_pos) = line.find(':')
{
let repo = line[colon_pos + 1..].trim().to_lowercase();
let is_core = repo == "core";
found_repo = Some((repo, is_core));
break;
}
}
assert_eq!(found_repo, Some(("extra".to_string(), false)));
}
#[test]
fn test_determine_dependency_source_installed_local() {
let sample_output = "Repository : local\nName : custom-package\n";
let mut found_repo = None;
for line in sample_output.lines() {
if line.starts_with("Repository")
&& let Some(colon_pos) = line.find(':')
{
let repo = line[colon_pos + 1..].trim().to_lowercase();
if repo == "local" || repo.is_empty() {
found_repo = Some("local");
break;
}
}
}
assert_eq!(found_repo, Some("local"));
}
#[test]
fn test_determine_dependency_source_not_installed_official() {
let sample_output = "Repository : extra\nName : firefox\n";
let mut found_repo = None;
for line in sample_output.lines() {
if line.starts_with("Repository")
&& let Some(colon_pos) = line.find(':')
{
let repo = line[colon_pos + 1..].trim().to_lowercase();
let is_core = repo == "core";
found_repo = Some((repo, is_core));
break;
}
}
assert_eq!(found_repo, Some(("extra".to_string(), false)));
}
#[test]
fn test_determine_dependency_source_fallback() {
let is_core = is_system_package("glibc");
assert!(is_core);
let expected_repo = if is_core { "core" } else { "extra" };
assert_eq!(expected_repo, "core");
let is_core = is_system_package("firefox");
assert!(!is_core);
let expected_repo = if is_core { "core" } else { "extra" };
assert_eq!(expected_repo, "extra");
}
#[test]
fn test_parse_repository_field() {
let test_cases = vec![
("Repository : core", ("core", true)),
("Repository : extra", ("extra", false)),
("Repository : community", ("community", false)),
("Repository : local", ("local", false)),
("Repository: core", ("core", true)),
("Repository : extra", ("extra", false)),
];
for (input, (expected_repo, expected_is_core)) in test_cases {
if let Some(colon_pos) = input.find(':') {
let repo = input[colon_pos + 1..].trim().to_lowercase();
let is_core = repo == "core";
assert_eq!(repo, expected_repo);
assert_eq!(is_core, expected_is_core);
}
}
}
#[test]
#[ignore = "Requires pacman to be available"]
fn test_determine_dependency_source_integration() {
use crate::deps::get_installed_packages;
if let Ok(installed) = get_installed_packages()
&& installed.contains("pacman")
{
let (source, is_core) = determine_dependency_source("pacman", &installed);
match source {
DependencySource::Official { repo } => {
assert_eq!(repo, "core");
assert!(is_core);
}
_ => panic!("pacman should be from official core repository"),
}
}
}
}