use anyhow::{Context, Result};
use stout_index::Database;
use stout_state::{InstalledPackages, Paths};
use clap::Args as ClapArgs;
use console::style;
#[derive(ClapArgs)]
pub struct Args {
pub formulas: Vec<String>,
#[arg(long, short = 'v')]
pub verbose: bool,
#[arg(long)]
pub json: bool,
#[arg(long)]
pub formula: bool,
#[arg(long)]
pub cask: bool,
#[arg(long)]
pub greedy: bool,
}
#[derive(Debug, serde::Serialize)]
struct OutdatedPackage {
name: String,
installed_version: String,
current_version: String,
pinned: bool,
}
pub async fn run(args: Args) -> Result<()> {
let paths = Paths::default();
let installed = InstalledPackages::load(&paths)?;
let db = Database::open(paths.index_db())
.context("Failed to open index. Run 'stout update' first.")?;
if !db.is_initialized()? {
eprintln!(
"{} Index not initialized. Run 'stout update' first.",
style("error:").red().bold()
);
std::process::exit(1);
}
let mut outdated: Vec<OutdatedPackage> = Vec::new();
let packages_to_check: Vec<String> = if args.formulas.is_empty() {
installed.names().map(|s| s.to_string()).collect()
} else {
args.formulas.clone()
};
for name in packages_to_check {
let pkg = match installed.get(&name) {
Some(p) => p,
None => continue,
};
if let Ok(Some(info)) = db.get_formula(&name) {
if info.version != pkg.version {
outdated.push(OutdatedPackage {
name: name.clone(),
installed_version: pkg.version.clone(),
current_version: info.version,
pinned: pkg.pinned,
});
}
}
}
if !args.greedy {
outdated.retain(|p| !p.pinned);
}
if args.json {
let json = serde_json::to_string_pretty(&outdated)?;
println!("{}", json);
} else if outdated.is_empty() {
if args.formulas.is_empty() {
println!("{}", style("All packages are up to date.").green());
} else {
println!("{}", style("Specified packages are up to date.").green());
}
} else {
for pkg in &outdated {
if args.verbose {
println!(
"{} {} -> {}{}",
style(&pkg.name).cyan(),
style(&pkg.installed_version).yellow(),
style(&pkg.current_version).green(),
if pkg.pinned {
style(" [pinned]").dim().to_string()
} else {
String::new()
}
);
} else {
print!("{}", pkg.name);
if pkg.pinned {
print!(" {}", style("[pinned]").dim());
}
println!();
}
}
println!(
"\n{} {} outdated package{}",
style("==>").blue().bold(),
outdated.len(),
if outdated.len() == 1 { "" } else { "s" }
);
}
Ok(())
}