#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
use std::path::PathBuf;
use std::process::ExitCode;
use bmux_env::{
InstallMode, InstallOutcome, InstallParams, PrintFormat, ShellKind, SlotOutputFormat,
UninstallOutcome, cmd_exec, cmd_install, cmd_print, cmd_shell, cmd_slot_doctor, cmd_slot_list,
cmd_slot_paths, cmd_slot_show, cmd_uninstall,
};
use clap::{Parser, Subcommand, ValueEnum};
#[derive(Parser, Debug)]
#[command(name = "bmux-env", version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand, Debug)]
enum Command {
Shell {
#[arg(long, value_enum, default_value_t = ShellKindArg::Auto)]
shell: ShellKindArg,
},
Exec {
slot: String,
#[arg(trailing_var_arg = true, required = true)]
argv: Vec<String>,
},
Print {
#[arg(long, value_enum, default_value_t = PrintFormatArg::Shell)]
format: PrintFormatArg,
},
List {
#[arg(long, value_enum, default_value_t = SlotOutputFormatArg::Toml)]
format: SlotOutputFormatArg,
},
Show {
name: Option<String>,
#[arg(long, value_enum, default_value_t = SlotOutputFormatArg::Toml)]
format: SlotOutputFormatArg,
},
Paths { name: Option<String> },
Doctor,
Install {
name: String,
binary: PathBuf,
#[arg(long)]
no_inherit_base: bool,
#[arg(long, value_enum, default_value_t = InstallModeArg::Symlink)]
mode: InstallModeArg,
#[arg(long)]
bin_dir: Option<PathBuf>,
#[arg(long, value_enum, default_value_t = SlotOutputFormatArg::Toml)]
format: SlotOutputFormatArg,
#[arg(long)]
dry_run: bool,
#[arg(long)]
overwrite: bool,
#[arg(long, short = 'y')]
yes: bool,
},
Uninstall {
name: String,
#[arg(long)]
purge: bool,
#[arg(long)]
bin_dir: Option<PathBuf>,
},
}
#[derive(Copy, Clone, Debug, ValueEnum)]
enum ShellKindArg {
Auto,
Bash,
Zsh,
Fish,
Nushell,
Powershell,
Posix,
}
impl From<ShellKindArg> for ShellKind {
fn from(v: ShellKindArg) -> Self {
match v {
ShellKindArg::Auto => Self::Auto,
ShellKindArg::Bash => Self::Bash,
ShellKindArg::Zsh => Self::Zsh,
ShellKindArg::Fish => Self::Fish,
ShellKindArg::Nushell => Self::Nushell,
ShellKindArg::Powershell => Self::Powershell,
ShellKindArg::Posix => Self::Posix,
}
}
}
#[derive(Copy, Clone, Debug, ValueEnum)]
enum PrintFormatArg {
Shell,
Json,
Nix,
Fish,
}
impl From<PrintFormatArg> for PrintFormat {
fn from(v: PrintFormatArg) -> Self {
match v {
PrintFormatArg::Shell => Self::Shell,
PrintFormatArg::Json => Self::Json,
PrintFormatArg::Nix => Self::Nix,
PrintFormatArg::Fish => Self::Fish,
}
}
}
#[derive(Copy, Clone, Debug, ValueEnum)]
enum SlotOutputFormatArg {
Toml,
Json,
Nix,
}
impl From<SlotOutputFormatArg> for SlotOutputFormat {
fn from(v: SlotOutputFormatArg) -> Self {
match v {
SlotOutputFormatArg::Toml => Self::Toml,
SlotOutputFormatArg::Json => Self::Json,
SlotOutputFormatArg::Nix => Self::Nix,
}
}
}
#[derive(Copy, Clone, Debug, ValueEnum)]
enum InstallModeArg {
Symlink,
Copy,
}
impl From<InstallModeArg> for InstallMode {
fn from(v: InstallModeArg) -> Self {
match v {
InstallModeArg::Symlink => Self::Symlink,
InstallModeArg::Copy => Self::Copy,
}
}
}
fn main() -> ExitCode {
match run() {
Ok(code) => ExitCode::from(code),
Err(err) => {
eprintln!("bmux-env: {err:#}");
ExitCode::from(1)
}
}
}
fn run() -> anyhow::Result<u8> {
let cli = Cli::parse();
let mut stdout = std::io::stdout().lock();
match cli.command {
Command::Shell { shell } => {
cmd_shell(&mut stdout, shell.into())?;
Ok(0)
}
Command::Exec { slot, argv } => {
cmd_exec(&slot, &argv)?;
Ok(0)
}
Command::Print { format } => {
cmd_print(&mut stdout, format.into())?;
Ok(0)
}
Command::List { format } => {
cmd_slot_list(&mut stdout, format.into())?;
Ok(0)
}
Command::Show { name, format } => {
cmd_slot_show(&mut stdout, name.as_deref(), None, format.into())?;
Ok(0)
}
Command::Paths { name } => {
cmd_slot_paths(&mut stdout, name.as_deref(), None)?;
Ok(0)
}
Command::Doctor => {
let ok = cmd_slot_doctor(&mut stdout)?;
Ok(if ok { 0 } else { 1 })
}
Command::Install {
name,
binary,
no_inherit_base,
mode,
bin_dir,
format,
dry_run,
overwrite,
yes,
} => {
let params = InstallParams {
name,
binary,
inherit_base: !no_inherit_base,
mode: mode.into(),
bin_dir,
format: format.into(),
dry_run,
overwrite,
yes,
};
match cmd_install(&mut stdout, ¶ms)? {
InstallOutcome::Written | InstallOutcome::DryRun => Ok(0),
InstallOutcome::RefusedReadOnly => Ok(77),
InstallOutcome::RefusedDuplicate | InstallOutcome::RefusedCancelled => Ok(1),
}
}
Command::Uninstall {
name,
purge,
bin_dir,
} => match cmd_uninstall(&mut stdout, &name, purge, bin_dir.as_deref())? {
UninstallOutcome::Removed => Ok(0),
UninstallOutcome::RefusedReadOnly => Ok(77),
},
}
}