diff --git a/src/shell/commands/executable.rs b/src/shell/commands/executable.rs index 19e5d50..5ce54a2 100644 --- a/src/shell/commands/executable.rs +++ b/src/shell/commands/executable.rs @@ -4,11 +4,17 @@ use crate::shell::types::ShellState; use crate::ExecuteResult; use crate::ShellCommand; use crate::ShellCommandContext; -use anyhow::bail; use anyhow::Result; use futures::future::LocalBoxFuture; use futures::FutureExt; +/// Errors for executable commands. +#[derive(Debug, PartialEq)] +enum ExecutableCommandError { + CommandNotFound, + CommandEmpty, +} + /// Command that resolves the command name and /// executes it in a separate process. pub struct ExecutableCommand { @@ -34,8 +40,14 @@ impl ShellCommand for ExecutableCommand { Ok(std::env::current_exe()?) }) { Ok(command_path) => command_path, - Err(err) => { - let _ = stderr.write_line(&err.to_string()); + Err(ExecutableCommandError::CommandNotFound) => { + let _ = stderr + .write_line(&format!("{}: command not found", command_name)); + // Use the Exit status that is used in bash: https://www.gnu.org/software/bash/manual/bash.html#Exit-Status + return ExecuteResult::Continue(127, Vec::new(), Vec::new()); + } + Err(ExecutableCommandError::CommandEmpty) => { + let _ = stderr.write_line("command name was empty"); return ExecuteResult::Continue(1, Vec::new(), Vec::new()); } }; @@ -90,9 +102,9 @@ fn resolve_command_path( command_name: &str, state: &ShellState, current_exe: impl FnOnce() -> Result, -) -> Result { +) -> Result { if command_name.is_empty() { - bail!("command name was empty"); + return Err(ExecutableCommandError::CommandEmpty); } // Special handling to use the current executable for deno. @@ -174,8 +186,7 @@ fn resolve_command_path( } } } - - bail!("{}: command not found", command_name) + Err(ExecutableCommandError::CommandNotFound) } #[cfg(test)] @@ -200,4 +211,21 @@ mod local_test { .unwrap(); assert_eq!(path, PathBuf::from("/bin/deno.exe")); } + + #[test] + fn should_error_on_unknown_command() { + let state = ShellState::new( + Default::default(), + &std::env::current_dir().unwrap(), + Default::default(), + ); + // Command not found + let result = + resolve_command_path("foobar", &state, || Ok(PathBuf::from("/bin/deno"))); + assert_eq!(result, Err(ExecutableCommandError::CommandNotFound)); + // Command empty + let result = + resolve_command_path("", &state, || Ok(PathBuf::from("/bin/deno"))); + assert_eq!(result, Err(ExecutableCommandError::CommandEmpty)); + } } diff --git a/src/shell/test.rs b/src/shell/test.rs index e52646f..794a50e 100644 --- a/src/shell/test.rs +++ b/src/shell/test.rs @@ -113,7 +113,7 @@ async fn commands() { .command("deno eval 'console.log(1)'") .env_var("PATH", "") .assert_stderr("deno: command not found\n") - .assert_exit_code(1) + .assert_exit_code(127) .run() .await;