From cb25d13028ec1cdf986a3567ea52562ea654a7b8 Mon Sep 17 00:00:00 2001 From: Michael Davis Date: Tue, 23 Jan 2024 13:36:53 -0500 Subject: [PATCH] Improve error handling for `which::which` failures Co-authored-by: Pascal Kuthe --- helix-dap/src/client.rs | 2 +- helix-dap/src/lib.rs | 2 ++ helix-loader/src/grammar.rs | 6 ++---- helix-lsp/src/client.rs | 2 +- helix-lsp/src/lib.rs | 2 ++ helix-stdx/src/env.rs | 28 +++++++++++++++++++++++++--- 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index 579811df7..18af13ae7 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -113,7 +113,7 @@ pub fn stdio( id: usize, ) -> Result<(Self, UnboundedReceiver)> { // Resolve path to the binary - let cmd = helix_stdx::env::which(cmd).map_err(|err| anyhow::anyhow!(err))?; + let cmd = helix_stdx::env::which(cmd)?; let process = Command::new(cmd) .args(args) diff --git a/helix-dap/src/lib.rs b/helix-dap/src/lib.rs index 21162cb86..d0229249d 100644 --- a/helix-dap/src/lib.rs +++ b/helix-dap/src/lib.rs @@ -19,6 +19,8 @@ pub enum Error { #[error("server closed the stream")] StreamClosed, #[error(transparent)] + ExecutableNotFound(#[from] helix_stdx::env::ExecutableNotFoundError), + #[error(transparent)] Other(#[from] anyhow::Error), } pub type Result = core::result::Result; diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs index 537e12828..7977c6df8 100644 --- a/helix-loader/src/grammar.rs +++ b/helix-loader/src/grammar.rs @@ -86,10 +86,8 @@ pub fn get_language(name: &str) -> Result { } fn ensure_git_is_available() -> Result<()> { - match helix_stdx::env::which("git") { - Ok(_cmd) => Ok(()), - Err(err) => Err(anyhow::anyhow!("'git' could not be found ({err})")), - } + helix_stdx::env::which("git")?; + Ok(()) } pub fn fetch_grammars() -> Result<()> { diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index f8c2912e7..fb32f6eb3 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -183,7 +183,7 @@ pub fn start( doc_path: Option<&std::path::PathBuf>, ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc)> { // Resolve path to the binary - let cmd = helix_stdx::env::which(cmd).map_err(|err| anyhow::anyhow!(err))?; + let cmd = helix_stdx::env::which(cmd)?; let process = Command::new(cmd) .envs(server_environment) diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index c99ec217b..53b2712d0 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -44,6 +44,8 @@ pub enum Error { #[error("Unhandled")] Unhandled, #[error(transparent)] + ExecutableNotFound(#[from] helix_stdx::env::ExecutableNotFoundError), + #[error(transparent)] Other(#[from] anyhow::Error), } diff --git a/helix-stdx/src/env.rs b/helix-stdx/src/env.rs index 3676727f2..90a0aee87 100644 --- a/helix-stdx/src/env.rs +++ b/helix-stdx/src/env.rs @@ -1,6 +1,5 @@ -pub use which::which; - use std::{ + ffi::OsStr, path::{Path, PathBuf}, sync::RwLock, }; @@ -36,10 +35,33 @@ pub fn env_var_is_set(env_var_name: &str) -> bool { std::env::var_os(env_var_name).is_some() } -pub fn binary_exists(binary_name: &str) -> bool { +pub fn binary_exists>(binary_name: T) -> bool { which::which(binary_name).is_ok() } +pub fn which>( + binary_name: T, +) -> Result { + which::which(binary_name.as_ref()).map_err(|err| ExecutableNotFoundError { + command: binary_name.as_ref().to_string_lossy().into_owned(), + inner: err, + }) +} + +#[derive(Debug)] +pub struct ExecutableNotFoundError { + command: String, + inner: which::Error, +} + +impl std::fmt::Display for ExecutableNotFoundError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "command '{}' not found: {}", self.command, self.inner) + } +} + +impl std::error::Error for ExecutableNotFoundError {} + #[cfg(test)] mod tests { use super::{current_working_dir, set_current_working_dir};