use std::error::Error as _;
use std::io::Write;
use std::process::ExitCode;
use cargo_upgrades::{Error, UpgradesChecker, Workspace};
use yansi::{Color::Red, Condition, Paint, Style};
pub const COLOR: Condition = Condition::from(|| {
(Condition::stdout_is_tty() || Condition::clicolor()) && Condition::no_color()
});
pub const ERROR: Style = Red.bold();
mod flags {
pub const HELP: &str = CargoUpgrades::HELP_;
xflags::xflags! {
cmd cargo-upgrades {
optional --pre
optional --plumbing
optional --manifest-path path: String
}
}
}
#[cold]
fn fail(e: Error) -> ExitCode {
print_error(e, "");
ExitCode::FAILURE
}
fn run() -> Result<ExitCode, ExitCode> {
if !COLOR() {
yansi::disable();
}
let args = std::env::args_os().skip(1).skip_while(|arg| arg == "upgrades").collect();
let flags = flags::CargoUpgrades::from_vec(args).map_err(|e| {
if e.is_help() {
println!("{e}");
ExitCode::SUCCESS
} else {
eprint!("{}: {e}\n\n{}", "error".paint(ERROR), flags::HELP);
ExitCode::FAILURE
}
})?;
if flags.plumbing {
yansi::disable();
}
let u = std::thread::spawn(UpgradesChecker::new);
let workspace = Workspace::new(flags.manifest_path.as_deref()).map_err(fail)?;
let u = u.join().unwrap().map_err(fail)?;
let mut printed_anything = false;
for (package, deps) in u.outdated_dependencies(&workspace, flags.pre) {
if flags.plumbing {
let cwd = std::env::current_dir().unwrap_or_default();
for (dep, res) in deps {
printed_anything = true;
match res {
Ok(m) => println!(
"{}\t{}@{}\t{}",
package.manifest_path.strip_prefix(&cwd).unwrap_or(&package.manifest_path),
dep.name,
m.matches.map(|v| v.to_string()).as_deref().unwrap_or("*"),
m.latest
),
Err(e) => {
print_error(e, &dep.name);
},
}
}
continue;
}
if printed_anything {
println!();
}
println!("{}\n{}", package.name.bold(), package.manifest_path.dim());
let mut stdout = std::io::stdout().lock();
let mut tw = tabwriter::TabWriter::new(&mut stdout).ansi(yansi::is_enabled());
let mut header_printed = false;
for (dep, res) in deps {
match res {
Ok(m) => {
if !header_printed {
let _ = writeln!(&mut tw, "{}\t{}\t{}", "dependency".underline(), "current".underline(), "upgrade".underline());
header_printed = true;
}
let _ = writeln!(&mut tw, "{}\t{}\t{}",
dep.rename.as_deref().unwrap_or(&dep.name),
m.matches.map(|s| s.to_string()).as_deref().unwrap_or("nothing").yellow(),
m.latest.green()
);
},
Err(e) => {
print_error(e, dep.rename.as_deref().unwrap_or(&dep.name));
},
}
}
let _ = tw.flush();
printed_anything = true;
}
if printed_anything {
return Ok(ExitCode::from(7));
}
if flags.plumbing {
print!("# ");
}
println!("{}", "All dependencies match up-to-date releases.".green());
if !flags.plumbing {
println!("Run `cargo update` to update to latest release.");
}
Ok(ExitCode::SUCCESS)
}
fn main() -> ExitCode {
run().unwrap_or_else(|e| e)
}
fn print_error(error: Error, context: &str) {
eprintln!("{}: {context}{}{error}", "error".paint(ERROR), if !context.is_empty() {": "} else {""});
let mut source = error.source();
while let Some(error) = source {
eprintln!(" {error}");
source = error.source();
}
}