diff options
| author | Michelle Tilley <michelle@michelletilley.net> | 2025-04-02 04:09:06 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-02 12:09:06 +0100 |
| commit | 653894d359f8dce8d15caa5aa2b5ffd0828f9add (patch) | |
| tree | e175efddc26416d09218c5eef163191021dbcd20 /crates | |
| parent | fix(zsh): fix an error introduced earilier with support for bracketed paste m... (diff) | |
| download | atuin-653894d359f8dce8d15caa5aa2b5ffd0828f9add.zip | |
feat: Binaries as subcommands (#2661)
* Run 'atuin-<subcmd>' if present when a given subcommand is not recognized
* Send errors to stderr
* Use String instead of OsString for external subcommands
* Remove unused import
* Move external subcommand handling up a level
* Clippy
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/atuin/src/command/external.rs | 72 | ||||
| -rw-r--r-- | crates/atuin/src/command/mod.rs | 6 |
2 files changed, 78 insertions, 0 deletions
diff --git a/crates/atuin/src/command/external.rs b/crates/atuin/src/command/external.rs new file mode 100644 index 00000000..03deacbf --- /dev/null +++ b/crates/atuin/src/command/external.rs @@ -0,0 +1,72 @@ +use std::fmt::Write as _; +use std::process::Command; +use std::{io, process}; + +use clap::CommandFactory; +use clap::builder::{StyledStr, Styles}; +use eyre::Result; + +use crate::Atuin; + +pub fn run(args: &[String]) -> Result<()> { + let subcommand = &args[0]; + let bin = format!("atuin-{subcommand}"); + let mut cmd = Command::new(&bin); + cmd.args(&args[1..]); + + let spawn_result = match cmd.spawn() { + Ok(child) => Ok(child), + Err(e) => match e.kind() { + io::ErrorKind::NotFound => { + let output = render_not_found(subcommand, &bin); + Err(output) + } + _ => Err(e.to_string().into()), + }, + }; + + match spawn_result { + Ok(mut child) => { + let status = child.wait()?; + if status.success() { + Ok(()) + } else { + process::exit(status.code().unwrap_or(1)); + } + } + Err(e) => { + eprintln!("{}", e.ansi()); + process::exit(1); + } + } +} + +fn render_not_found(subcommand: &str, bin: &str) -> StyledStr { + let mut output = StyledStr::new(); + let styles = Styles::styled(); + let mut atuin_cmd = Atuin::command(); + let usage = atuin_cmd.render_usage(); + + let error = styles.get_error(); + let invalid = styles.get_invalid(); + let literal = styles.get_literal(); + + let _ = write!(output, "{error}error:{error:#} "); + let _ = write!( + output, + "unrecognized subcommand '{invalid}{subcommand}{invalid:#}' " + ); + let _ = write!( + output, + "and no executable named '{invalid}{bin}{invalid:#}' found in your PATH" + ); + let _ = write!(output, "\n\n"); + let _ = write!(output, "{usage}"); + let _ = write!(output, "\n\n"); + let _ = write!( + output, + "For more information, try '{literal}--help{literal:#}'." + ); + + output +} diff --git a/crates/atuin/src/command/mod.rs b/crates/atuin/src/command/mod.rs index 09df430e..95813193 100644 --- a/crates/atuin/src/command/mod.rs +++ b/crates/atuin/src/command/mod.rs @@ -14,6 +14,8 @@ mod contributors; mod gen_completions; +mod external; + #[derive(Subcommand)] #[command(infer_subcommands = true)] pub enum AtuinCmd { @@ -33,6 +35,9 @@ pub enum AtuinCmd { /// Generate shell completions GenCompletions(gen_completions::Cmd), + + #[command(external_subcommand)] + External(Vec<String>), } impl AtuinCmd { @@ -60,6 +65,7 @@ impl AtuinCmd { Ok(()) } Self::GenCompletions(gen_completions) => gen_completions.run(), + Self::External(args) => external::run(&args), } } } |
