diff --git a/crates/nu-cmd-lang/src/core_commands/do_.rs b/crates/nu-cmd-lang/src/core_commands/do_.rs index 6043a8681a..36fb13cf10 100644 --- a/crates/nu-cmd-lang/src/core_commands/do_.rs +++ b/crates/nu-cmd-lang/src/core_commands/do_.rs @@ -117,7 +117,7 @@ impl Command for Do { None, ) }) - .map_err(|e| e.into_spanned(call.head)) + .err_span(call.head) }) .transpose()?; diff --git a/crates/nu-cmd-plugin/src/commands/plugin/add.rs b/crates/nu-cmd-plugin/src/commands/plugin/add.rs index 156b80f1f1..d615ed8de6 100644 --- a/crates/nu-cmd-plugin/src/commands/plugin/add.rs +++ b/crates/nu-cmd-plugin/src/commands/plugin/add.rs @@ -106,9 +106,7 @@ apparent the next time `nu` is next launched with that plugin cache file. let shell_expanded = shell .as_ref() - .map(|s| { - nu_path::canonicalize_with(&s.item, &cwd).map_err(|err| err.into_spanned(s.span)) - }) + .map(|s| nu_path::canonicalize_with(&s.item, &cwd).err_span(s.span)) .transpose()?; // Parse the plugin filename so it can be used to spawn the plugin diff --git a/crates/nu-cmd-plugin/src/util.rs b/crates/nu-cmd-plugin/src/util.rs index c728c12e89..e24e72eab8 100644 --- a/crates/nu-cmd-plugin/src/util.rs +++ b/crates/nu-cmd-plugin/src/util.rs @@ -30,7 +30,7 @@ pub(crate) fn modify_plugin_file( // Try to read the plugin file if it exists let mut contents = if fs::metadata(&plugin_cache_file_path).is_ok_and(|m| m.len() > 0) { PluginCacheFile::read_from( - File::open(&plugin_cache_file_path).map_err(|err| err.into_spanned(span))?, + File::open(&plugin_cache_file_path).err_span(span)?, Some(span), )? } else { @@ -42,7 +42,7 @@ pub(crate) fn modify_plugin_file( // Save the modified file on success contents.write_to( - File::create(&plugin_cache_file_path).map_err(|err| err.into_spanned(span))?, + File::create(&plugin_cache_file_path).err_span(span)?, Some(span), )?; diff --git a/crates/nu-command/src/filesystem/save.rs b/crates/nu-command/src/filesystem/save.rs index 30be3da818..852dbf529e 100644 --- a/crates/nu-command/src/filesystem/save.rs +++ b/crates/nu-command/src/filesystem/save.rs @@ -133,7 +133,7 @@ impl Command for Save { .spawn(move || stderr.drain()), }) .transpose() - .map_err(|e| e.into_spanned(span))?; + .err_span(span)?; let res = stream_to_file(stdout, file, span, progress); if let Some(h) = handler { diff --git a/crates/nu-command/src/filters/tee.rs b/crates/nu-command/src/filters/tee.rs index 490e66a7c9..5287f44512 100644 --- a/crates/nu-command/src/filters/tee.rs +++ b/crates/nu-command/src/filters/tee.rs @@ -125,8 +125,7 @@ use it in your pipeline."# if use_stderr { let stderr = stderr .map(|stderr| { - let iter = tee(stderr.stream, with_stream) - .map_err(|e| e.into_spanned(call.head))?; + let iter = tee(stderr.stream, with_stream).err_span(call.head)?; Ok::<_, ShellError>(RawStream::new( Box::new(iter.map(flatten_result)), stderr.ctrlc, @@ -146,8 +145,7 @@ use it in your pipeline."# } else { let stdout = stdout .map(|stdout| { - let iter = tee(stdout.stream, with_stream) - .map_err(|e| e.into_spanned(call.head))?; + let iter = tee(stdout.stream, with_stream).err_span(call.head)?; Ok::<_, ShellError>(RawStream::new( Box::new(iter.map(flatten_result)), stdout.ctrlc, @@ -189,7 +187,7 @@ use it in your pipeline."# // Make sure to drain any iterator produced to avoid unexpected behavior result.and_then(|data| data.drain()) }) - .map_err(|e| e.into_spanned(call.head))? + .err_span(call.head)? .map(move |result| result.unwrap_or_else(|err| Value::error(err, closure_span))) .into_pipeline_data_with_metadata(metadata, engine_state.ctrlc.clone()); diff --git a/crates/nu-command/src/system/complete.rs b/crates/nu-command/src/system/complete.rs index 48f04b4ff3..e12f65fb5a 100644 --- a/crates/nu-command/src/system/complete.rs +++ b/crates/nu-command/src/system/complete.rs @@ -62,7 +62,7 @@ impl Command for Complete { } }) .map(|handle| (handle, stderr_span)) - .map_err(|err| err.into_spanned(call.head)) + .err_span(call.head) }) .transpose()?; diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index 1363481311..85d3f19f54 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -495,7 +495,7 @@ impl ExternalCommand { Ok(()) }) - .map_err(|e| e.into_spanned(head))?; + .err_span(head)?; } } @@ -580,7 +580,7 @@ impl ExternalCommand { Ok(()) } }) - .map_err(|e| e.into_spanned(head))?; + .err_span(head)?; let exit_code_receiver = ValueReceiver::new(exit_code_rx); diff --git a/crates/nu-engine/src/command_prelude.rs b/crates/nu-engine/src/command_prelude.rs index 2764629683..089a2fb8fa 100644 --- a/crates/nu-engine/src/command_prelude.rs +++ b/crates/nu-engine/src/command_prelude.rs @@ -2,6 +2,7 @@ pub use crate::CallExt; pub use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, IntoSpanned, - PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, + record, Category, ErrSpan, Example, IntoInterruptiblePipelineData, IntoPipelineData, + IntoSpanned, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, + Value, }; diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 537d5c3f80..84121e5aee 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -3550,8 +3550,7 @@ pub fn parse_where(working_set: &mut StateWorkingSet, lite_command: &LiteCommand pub fn parse_register(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline { use nu_plugin::{get_signature, PersistentPlugin, PluginDeclaration}; use nu_protocol::{ - engine::Stack, IntoSpanned, PluginCacheItem, PluginIdentity, PluginSignature, - RegisteredPlugin, + engine::Stack, ErrSpan, PluginCacheItem, PluginIdentity, PluginSignature, RegisteredPlugin, }; let spans = &lite_command.parts; @@ -3694,8 +3693,7 @@ pub fn parse_register(working_set: &mut StateWorkingSet, lite_command: &LiteComm let path = path.path_buf(); // Create the plugin identity. This validates that the plugin name starts with `nu_plugin_` - let identity = - PluginIdentity::new(path, shell).map_err(|err| err.into_spanned(path_span))?; + let identity = PluginIdentity::new(path, shell).err_span(path_span)?; // Find garbage collection config let gc_config = working_set diff --git a/crates/nu-protocol/src/span.rs b/crates/nu-protocol/src/span.rs index db2885c1a4..7bc13997a1 100644 --- a/crates/nu-protocol/src/span.rs +++ b/crates/nu-protocol/src/span.rs @@ -147,3 +147,34 @@ pub fn span(spans: &[Span]) -> Span { Span::new(spans[0].start, end) } } + +/// An extension trait for `Result`, which adds a span to the error type. +pub trait ErrSpan { + type Result; + + /// Add the given span to the error type `E`, turning it into a `Spanned`. + /// + /// Some auto-conversion methods to `ShellError` from other error types are available on spanned + /// errors, to give users better information about where an error came from. For example, it is + /// preferred when working with `std::io::Error`: + /// + /// ```no_run + /// use nu_protocol::{ErrSpan, ShellError, Span}; + /// use std::io::Read; + /// + /// fn read_from(mut reader: impl Read, span: Span) -> Result, ShellError> { + /// let mut vec = vec![]; + /// reader.read_to_end(&mut vec).err_span(span)?; + /// Ok(vec) + /// } + /// ``` + fn err_span(self, span: Span) -> Self::Result; +} + +impl ErrSpan for Result { + type Result = Result>; + + fn err_span(self, span: Span) -> Self::Result { + self.map_err(|err| err.into_spanned(span)) + } +}