use crate::registry::HoistRegistry;
use anyhow::Result;
use clap::{ArgAction, Parser, Subcommand};
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[arg(long, short, action = ArgAction::Count, default_value = "0")]
pub verbosity: u8,
#[arg(long, short)]
pub quiet: bool,
#[clap(subcommand)]
pub command: Option<Command>,
}
#[derive(Subcommand, Debug)]
pub enum Command {
Hoist {
bins: Option<Vec<String>>,
#[clap(short, long)]
binaries: Option<Vec<String>>,
},
List,
#[clap(alias = "find")]
Search {
binary: String,
},
Nuke,
#[clap(alias = "install")]
Register {
bins: Option<Vec<String>>,
#[clap(short, long)]
binaries: Option<Vec<String>>,
},
}
pub fn run() -> Result<()> {
let Args {
verbosity,
quiet,
command,
} = Args::parse();
crate::telemetry::init_tracing_subscriber(verbosity)?;
HoistRegistry::create_pre_hook(true, false)?;
match command {
None => HoistRegistry::install(None, Vec::new(), quiet),
Some(c) => match c {
Command::Hoist { binaries, bins } => {
HoistRegistry::hoist(crate::utils::merge_and_dedup_vecs(binaries, bins), quiet)
}
Command::Search { binary } => HoistRegistry::find(binary),
Command::List => HoistRegistry::list(false),
Command::Register { binaries, bins } => HoistRegistry::install(
None,
crate::utils::merge_and_dedup_vecs(binaries, bins),
quiet,
),
Command::Nuke => HoistRegistry::nuke(false),
},
}
}
#[cfg(test)]
mod tests {
use assert_cmd::Command;
use rand::{distributions::Alphanumeric, Rng};
use serial_test::serial;
use std::path::PathBuf;
use tempfile::TempDir;
const HOIST_BIN: &str = "cargo-hoist";
#[test]
#[serial]
fn test_cli_no_args() {
let tempdir = tempfile::tempdir().unwrap();
let _ = setup_test_dir(&tempdir);
let mut cmd = Command::cargo_bin(HOIST_BIN).unwrap();
let assert = cmd.assert();
assert.success().stdout("");
}
#[test]
#[serial]
fn test_cli_nuke() {
let tempdir = tempfile::tempdir().unwrap();
let _ = setup_test_dir(&tempdir);
let mut cmd = Command::cargo_bin(HOIST_BIN).unwrap();
cmd.arg("nuke").assert().success().stdout("");
}
#[test]
#[serial]
fn test_cli_install() {
let tempdir = tempfile::tempdir().unwrap();
let _ = setup_test_dir(&tempdir);
let mut cmd = Command::cargo_bin(HOIST_BIN).unwrap();
cmd.arg("install").assert().success().stdout("");
}
#[test]
#[serial]
fn test_cli_list() {
let tempdir = tempfile::tempdir().unwrap();
let _ = setup_test_dir(&tempdir);
let mut cmd = Command::cargo_bin(HOIST_BIN).unwrap();
cmd.arg("list").assert().success();
}
#[test]
#[serial]
fn test_cli_unrecognized_subcommand() {
let tempdir = tempfile::tempdir().unwrap();
let _ = setup_test_dir(&tempdir);
let mut cmd = Command::cargo_bin(HOIST_BIN).unwrap();
let assert = cmd.arg("foobar").assert();
assert.failure().code(2).stderr(
r#"error: unrecognized subcommand 'foobar'
Usage: cargo-hoist [OPTIONS] [COMMAND]
For more information, try '--help'.
"#,
);
}
fn setup_test_dir(tempdir: &TempDir) -> PathBuf {
let s: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(7)
.map(char::from)
.collect();
let test_tempdir = tempdir.path().join(s);
std::fs::create_dir(&test_tempdir).unwrap();
let backup = match std::env::current_dir() {
Ok(d) => {
if d.join("target/debug/cargo-hoist").exists() {
std::fs::copy(
d.join("target/debug/cargo-hoist"),
test_tempdir.join("cargo-hoist"),
)
.unwrap();
false
} else {
true
}
}
Err(_) => true,
};
if backup {
let _ = std::process::Command::new("cargo")
.args(["install", "--path", "."])
.current_dir(&test_tempdir)
.output()
.unwrap();
}
std::env::set_current_dir(&test_tempdir).unwrap();
test_tempdir
}
}