diff --git a/crates/nu-command/src/filesystem/open.rs b/crates/nu-command/src/filesystem/open.rs index 7cbce3902b..db00f35d6d 100644 --- a/crates/nu-command/src/filesystem/open.rs +++ b/crates/nu-command/src/filesystem/open.rs @@ -91,6 +91,21 @@ impl Command for Open { file: path.item.to_string(), span, }, + // that particular error in `nu_engine::glob_from` doesn't have a span attached + // to it, so let's add it + ShellError::GenericError { + error, + msg, + span: _, + help, + inner, + } if error.as_str() == "Permission denied" => ShellError::GenericError { + error, + msg, + span: Some(arg_span), + help, + inner, + }, _ => err, })? .1 diff --git a/crates/nu-engine/src/glob_from.rs b/crates/nu-engine/src/glob_from.rs index 376b3c2baf..3765ab7c7b 100644 --- a/crates/nu-engine/src/glob_from.rs +++ b/crates/nu-engine/src/glob_from.rs @@ -3,6 +3,7 @@ use nu_path::{canonicalize_with, expand_path_with}; use nu_protocol::{NuGlob, ShellError, Span, Spanned}; use std::{ fs, + io::ErrorKind, path::{Component, Path, PathBuf}, }; @@ -70,20 +71,32 @@ pub fn glob_from( if is_symlink { (path.parent().map(|parent| parent.to_path_buf()), path) } else { - let path = if let Ok(p) = canonicalize_with(path.clone(), cwd) { - if nu_glob::is_glob(p.to_string_lossy().as_ref()) { + let path = match canonicalize_with(path.clone(), cwd) { + Ok(p) if nu_glob::is_glob(p.to_string_lossy().as_ref()) => { // our path might contain glob metacharacters too. // in such case, we need to escape our path to make // glob work successfully PathBuf::from(nu_glob::Pattern::escape(&p.to_string_lossy())) - } else { - p } - } else { - return Err(ShellError::DirectoryNotFound { - dir: path.to_string_lossy().to_string(), - span: pattern.span, - }); + Ok(p) => p, + Err(err) => { + return match err.kind() { + ErrorKind::PermissionDenied => Err(ShellError::GenericError { + error: "Permission denied".into(), + msg: err.to_string(), + span: None, + help: None, + inner: vec![], + }), + // Previously, all these errors were treated as "directory not found." + // Now, permission denied errors are handled separately. + // TODO: Refine handling of I/O errors for more precise responses. + _ => Err(ShellError::DirectoryNotFound { + dir: path.to_string_lossy().to_string(), + span: pattern.span, + }), + }; + } }; (path.parent().map(|parent| parent.to_path_buf()), path) }