use colored::Colorize;
use std::io::{self, Write};
use std::process::Command;
#[derive(clap::Args)]
pub struct Args {
#[arg(short, long)]
pub yes: bool,
}
#[allow(clippy::let_unit_value)]
pub fn run(args: Args) {
if !args.yes && !confirm() {
println!("{}", "Aborted.".dimmed());
return;
}
println!(
"{} {}\n",
"Updating sluuz via".dimmed(),
"cargo install sluuz --force".bold()
);
let token = begin_self_replace();
match Command::new("cargo")
.args(["install", "sluuz", "--force"])
.status()
{
Ok(status) if status.success() => {
println!("\n{}", "✓ sluuz is up to date.".green());
}
Ok(status) => {
undo_self_replace(&token);
eprintln!("\n{}", "✗ update failed.".red());
if cfg!(windows) {
eprintln!(
"{}",
"If it still can't replace slu.exe, run this in a fresh terminal:".dimmed()
);
eprintln!(" {}", "cargo install sluuz --force".bold());
}
std::process::exit(status.code().unwrap_or(1));
}
Err(e) => {
undo_self_replace(&token);
eprintln!("{} {e}", "slu: could not run cargo:".red());
eprintln!(
"{}",
"is cargo installed and on your PATH? (https://rustup.rs)".dimmed()
);
std::process::exit(127);
}
}
}
fn confirm() -> bool {
print!(
"{} {} ",
"Update sluuz to the latest release via cargo?".bold(),
"[y/N]".dimmed()
);
io::stdout().flush().ok();
let mut input = String::new();
if io::stdin().read_line(&mut input).is_err() {
return false;
}
matches!(input.trim().to_lowercase().as_str(), "y" | "yes")
}
#[cfg(windows)]
type ReplaceToken = Option<(std::path::PathBuf, std::path::PathBuf)>;
#[cfg(not(windows))]
type ReplaceToken = ();
#[cfg(windows)]
fn begin_self_replace() -> ReplaceToken {
let exe = std::env::current_exe().ok()?;
let mut old_os = exe.clone().into_os_string();
old_os.push(".old");
let old = std::path::PathBuf::from(old_os);
let _ = std::fs::remove_file(&old); match std::fs::rename(&exe, &old) {
Ok(()) => Some((exe, old)),
Err(_) => None, }
}
#[cfg(not(windows))]
fn begin_self_replace() -> ReplaceToken {}
#[cfg(windows)]
fn undo_self_replace(token: &ReplaceToken) {
if let Some((exe, old)) = token {
if !exe.exists() {
let _ = std::fs::rename(old, exe);
}
}
}
#[cfg(not(windows))]
fn undo_self_replace(_token: &ReplaceToken) {}