use crate::cache::Cache;
use crate::error::Result;
use crate::install::InstallState;
use crate::version::is_same_or_newer;
use console::style;
use std::collections::HashMap;
fn print_disabled(disabled: &[(&str, &str, &str)]) {
if disabled.is_empty() {
return;
}
println!(
"\n{} {} disabled {}:",
style("✗").red().bold(),
disabled.len(),
if disabled.len() == 1 {
"package"
} else {
"packages"
}
);
for (name, version, reason) in disabled {
println!(
" {} {} {}",
style(*name).red(),
style(format!("@{}", version)).dim(),
style(*reason).dim()
);
}
}
fn print_deprecated(deprecated: &[(&str, &str, &str)]) {
if deprecated.is_empty() {
return;
}
println!(
"\n{} {} deprecated {}:",
style("!").yellow().bold(),
deprecated.len(),
if deprecated.len() == 1 {
"package"
} else {
"packages"
}
);
for (name, version, reason) in deprecated {
println!(
" {} {} {}",
style(*name).yellow(),
style(format!("@{}", version)).dim(),
style(*reason).dim()
);
}
}
fn print_outdated(outdated: &[(&str, &str, String)]) {
if outdated.is_empty() {
return;
}
println!(
"\n{} {} outdated {}:",
style("↑").cyan().bold(),
outdated.len(),
if outdated.len() == 1 {
"package"
} else {
"packages"
}
);
for (name, installed_ver, latest_ver) in outdated {
println!(
" {} {} → {}",
style(*name).cyan(),
style(*installed_ver).dim(),
style(latest_ver).green()
);
}
}
fn print_unknown(unknown: &[(&str, &str)]) {
if unknown.is_empty() {
return;
}
println!(
"\n{} {} {} not in any known tap:",
style("?").dim().bold(),
unknown.len(),
if unknown.len() == 1 {
"package"
} else {
"packages"
}
);
for (name, version) in unknown {
println!(
" {} {}",
style(*name).dim(),
style(format!("@{}", version)).dim()
);
}
}
pub async fn audit(cache: &Cache) -> Result<()> {
let state = InstallState::new()?;
state.sync_from_cellar().await.ok();
let installed = state.load().await?;
if installed.is_empty() {
println!("no packages installed");
return Ok(());
}
cache.ensure_fresh().await?;
let formulae = cache.load_all_formulae().await?;
let formula_index: HashMap<_, _> = formulae.iter().map(|f| (f.name.as_str(), f)).collect();
let mut deprecated = Vec::new();
let mut disabled = Vec::new();
let mut outdated = Vec::new();
let mut unknown = Vec::new();
for (name, pkg) in &installed {
match formula_index.get(name.as_str()) {
Some(formula) => {
if formula.disabled {
let reason = formula
.disable_reason
.as_deref()
.unwrap_or("no reason given");
disabled.push((name.as_str(), pkg.version.as_str(), reason));
} else if formula.deprecated {
let reason = formula
.deprecation_reason
.as_deref()
.unwrap_or("no reason given");
deprecated.push((name.as_str(), pkg.version.as_str(), reason));
}
let latest = formula.full_version();
if !is_same_or_newer(&pkg.version, &latest) {
outdated.push((name.as_str(), pkg.version.as_str(), latest));
}
}
None => {
unknown.push((name.as_str(), pkg.version.as_str()));
}
}
}
let total_issues = disabled.len() + deprecated.len();
if total_issues == 0 && outdated.is_empty() && unknown.is_empty() {
println!(
"{} {} installed packages — no issues found",
style("✓").green(),
installed.len()
);
return Ok(());
}
print_disabled(&disabled);
print_deprecated(&deprecated);
print_outdated(&outdated);
print_unknown(&unknown);
println!("\n{} installed, {} issues", installed.len(), total_issues);
Ok(())
}