Refactor: Construct IoError from std::io::Error instead of std::io::ErrorKind (#15777)

This commit is contained in:
Piepmatz 2025-05-18 14:52:40 +02:00 committed by GitHub
parent c4dcfdb77b
commit 833471241a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
80 changed files with 408 additions and 299 deletions

1
Cargo.lock generated
View File

@ -3996,6 +3996,7 @@ dependencies = [
"thiserror 2.0.12",
"typetag",
"web-time",
"windows 0.56.0",
"windows-sys 0.48.0",
]

View File

@ -1,5 +1,8 @@
use nu_engine::command_prelude::*;
use nu_protocol::{HistoryFileFormat, shell_error::io::IoError};
use nu_protocol::{
HistoryFileFormat,
shell_error::{self, io::IoError},
};
use reedline::{
FileBackedHistory, History as ReedlineHistory, HistoryItem, SearchDirection, SearchQuery,
SqliteBackedHistory,
@ -94,7 +97,7 @@ impl Command for History {
})
})
.ok_or(IoError::new(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
head,
history_path,
))?
@ -110,7 +113,7 @@ impl Command for History {
})
})
.ok_or(IoError::new(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
head,
history_path,
))?

View File

@ -287,7 +287,7 @@ fn backup(path: &Path, span: Span) -> Result<Option<PathBuf>, ShellError> {
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(e) => {
return Err(IoError::new_internal(
e.kind(),
e,
"Could not get metadata",
nu_protocol::location!(),
)
@ -297,7 +297,7 @@ fn backup(path: &Path, span: Span) -> Result<Option<PathBuf>, ShellError> {
let bak_path = find_backup_path(path, span)?;
std::fs::copy(path, &bak_path).map_err(|err| {
IoError::new_internal(
err.kind(),
err.not_found_as(NotFound::File),
"Could not copy backup",
nu_protocol::location!(),
)

View File

@ -42,7 +42,7 @@ impl Command for KeybindingsListen {
Err(e) => {
terminal::disable_raw_mode().map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not disable raw mode",
nu_protocol::location!(),
)
@ -71,18 +71,10 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
let config = engine_state.get_config();
stdout().flush().map_err(|err| {
IoError::new_internal(
err.kind(),
"Could not flush stdout",
nu_protocol::location!(),
)
IoError::new_internal(err, "Could not flush stdout", nu_protocol::location!())
})?;
terminal::enable_raw_mode().map_err(|err| {
IoError::new_internal(
err.kind(),
"Could not enable raw mode",
nu_protocol::location!(),
)
IoError::new_internal(err, "Could not enable raw mode", nu_protocol::location!())
})?;
if config.use_kitty_protocol {
@ -114,7 +106,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
loop {
let event = crossterm::event::read().map_err(|err| {
IoError::new_internal(err.kind(), "Could not read event", nu_protocol::location!())
IoError::new_internal(err, "Could not read event", nu_protocol::location!())
})?;
if event == Event::Key(KeyCode::Esc.into()) {
break;
@ -136,7 +128,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
};
stdout.queue(crossterm::style::Print(o)).map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not print output record",
nu_protocol::location!(),
)
@ -144,14 +136,10 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
stdout
.queue(crossterm::style::Print("\r\n"))
.map_err(|err| {
IoError::new_internal(
err.kind(),
"Could not print linebreak",
nu_protocol::location!(),
)
IoError::new_internal(err, "Could not print linebreak", nu_protocol::location!())
})?;
stdout.flush().map_err(|err| {
IoError::new_internal(err.kind(), "Could not flush", nu_protocol::location!())
IoError::new_internal(err, "Could not flush", nu_protocol::location!())
})?;
}
@ -163,11 +151,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
}
terminal::disable_raw_mode().map_err(|err| {
IoError::new_internal(
err.kind(),
"Could not disable raw mode",
nu_protocol::location!(),
)
IoError::new_internal(err, "Could not disable raw mode", nu_protocol::location!())
})?;
Ok(Value::nothing(Span::unknown()))

View File

@ -80,7 +80,7 @@ pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Span
report_shell_error(
engine_state,
&ShellError::Io(IoError::new_internal_with_path(
err.kind(),
err,
"Could not open plugin registry file",
nu_protocol::location!(),
plugin_path,
@ -323,7 +323,7 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
if let Err(err) = std::fs::File::create(&new_plugin_file_path)
.map_err(|err| {
IoError::new_internal_with_path(
err.kind(),
err,
"Could not create new plugin file",
nu_protocol::location!(),
new_plugin_file_path.clone(),

View File

@ -28,7 +28,7 @@ pub fn evaluate_file(
let file_path = canonicalize_with(&path, cwd).map_err(|err| {
IoError::new_internal_with_path(
err.kind().not_found_as(NotFound::File),
err.not_found_as(NotFound::File),
"Could not access file",
nu_protocol::location!(),
PathBuf::from(&path),
@ -47,7 +47,7 @@ pub fn evaluate_file(
let file = std::fs::read(&file_path).map_err(|err| {
IoError::new_internal_with_path(
err.kind().not_found_as(NotFound::File),
err.not_found_as(NotFound::File),
"Could not read file",
nu_protocol::location!(),
file_path.clone(),

View File

@ -22,6 +22,7 @@ use nu_color_config::StyleComputer;
use nu_engine::env_to_strings;
use nu_engine::exit::cleanup_exit;
use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::shell_error;
use nu_protocol::shell_error::io::IoError;
use nu_protocol::{
HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
@ -854,7 +855,7 @@ fn do_auto_cd(
report_shell_error(
engine_state,
&ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::DirectoryNotFound,
span,
PathBuf::from(&path),
"Cannot change directory",
@ -868,7 +869,7 @@ fn do_auto_cd(
report_shell_error(
engine_state,
&ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::PermissionDenied,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::PermissionDenied),
span,
PathBuf::from(path),
"Cannot change directory",

View File

@ -134,7 +134,7 @@ fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
let mut byte = [0];
if reader
.read(&mut byte[..])
.map_err(|err| IoError::new(err.kind(), head, None))?
.map_err(|err| IoError::new(err, head, None))?
> 0
{
// Format the byte as bits

View File

@ -107,14 +107,14 @@ impl Command for Do {
let mut buf = Vec::new();
stdout.read_to_end(&mut buf).map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not read stdout to end",
nu_protocol::location!(),
)
})?;
Ok::<_, ShellError>(buf)
})
.map_err(|err| IoError::new(err.kind(), head, None))
.map_err(|err| IoError::new(err, head, None))
})
.transpose()?;
@ -126,7 +126,7 @@ impl Command for Do {
let mut buf = String::new();
stderr
.read_to_string(&mut buf)
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
buf
}
};

View File

@ -88,13 +88,19 @@ apparent the next time `nu` is next launched with that plugin registry file.
let filename_expanded = nu_path::locate_in_dirs(&filename.item, &cwd, || {
get_plugin_dirs(engine_state, stack)
})
.map_err(|err| IoError::new(err.kind(), filename.span, PathBuf::from(filename.item)))?;
.map_err(|err| {
IoError::new(
err.not_found_as(NotFound::File),
filename.span,
PathBuf::from(filename.item),
)
})?;
let shell_expanded = shell
.as_ref()
.map(|s| {
nu_path::canonicalize_with(&s.item, &cwd)
.map_err(|err| IoError::new(err.kind(), s.span, None))
.map_err(|err| IoError::new(err, s.span, None))
})
.transpose()?;

View File

@ -1,6 +1,10 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_protocol::{PluginRegistryFile, engine::StateWorkingSet, shell_error::io::IoError};
use nu_protocol::{
PluginRegistryFile,
engine::StateWorkingSet,
shell_error::{self, io::IoError},
};
use std::{
fs::{self, File},
path::PathBuf,
@ -46,12 +50,12 @@ pub(crate) fn read_plugin_file(
if fs::metadata(&plugin_registry_file_path).is_ok_and(|m| m.len() > 0) {
PluginRegistryFile::read_from(
File::open(&plugin_registry_file_path)
.map_err(|err| IoError::new(err.kind(), file_span, plugin_registry_file_path))?,
.map_err(|err| IoError::new(err, file_span, plugin_registry_file_path))?,
Some(file_span),
)
} else if let Some(path) = custom_path {
Err(ShellError::Io(IoError::new(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
path.span,
PathBuf::from(&path.item),
)))
@ -75,9 +79,8 @@ pub(crate) fn modify_plugin_file(
// Try to read the plugin file if it exists
let mut contents = if fs::metadata(&plugin_registry_file_path).is_ok_and(|m| m.len() > 0) {
PluginRegistryFile::read_from(
File::open(&plugin_registry_file_path).map_err(|err| {
IoError::new(err.kind(), file_span, plugin_registry_file_path.clone())
})?,
File::open(&plugin_registry_file_path)
.map_err(|err| IoError::new(err, file_span, plugin_registry_file_path.clone()))?,
Some(file_span),
)?
} else {
@ -90,7 +93,7 @@ pub(crate) fn modify_plugin_file(
// Save the modified file on success
contents.write_to(
File::create(&plugin_registry_file_path)
.map_err(|err| IoError::new(err.kind(), file_span, plugin_registry_file_path))?,
.map_err(|err| IoError::new(err, file_span, plugin_registry_file_path))?,
Some(span),
)?;

View File

@ -77,7 +77,7 @@ impl Command for BytesEndsWith {
Ok(&[]) => break,
Ok(buf) => buf,
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) => return Err(IoError::new(e.kind(), span, None).into()),
Err(e) => return Err(IoError::new(e, span, None).into()),
};
let len = buf.len();
if len >= cap {

View File

@ -72,7 +72,7 @@ impl Command for BytesStartsWith {
reader
.take(pattern.len() as u64)
.read_to_end(&mut start)
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
Ok(Value::bool(start == pattern, head).into_pipeline_data())
} else {

View File

@ -41,12 +41,11 @@ impl SQLiteDatabase {
}
pub fn try_from_path(path: &Path, span: Span, signals: Signals) -> Result<Self, ShellError> {
let mut file =
File::open(path).map_err(|e| IoError::new(e.kind(), span, PathBuf::from(path)))?;
let mut file = File::open(path).map_err(|e| IoError::new(e, span, PathBuf::from(path)))?;
let mut buf: [u8; 16] = [0; 16];
file.read_exact(&mut buf)
.map_err(|e| ShellError::Io(IoError::new(e.kind(), span, PathBuf::from(path))))
.map_err(|e| ShellError::Io(IoError::new(e, span, PathBuf::from(path))))
.and_then(|_| {
if buf == SQLITE_MAGIC_BYTES {
Ok(SQLiteDatabase::new(path, signals))

View File

@ -115,7 +115,7 @@ pub(super) fn start_editor(
let child = child.map_err(|err| {
IoError::new_with_additional_context(
err.kind(),
err,
call.head,
None,
"Could not spawn foreground child",

View File

@ -59,7 +59,7 @@ impl Command for ConfigReset {
));
if let Err(err) = std::fs::rename(nu_config.clone(), &backup_path) {
return Err(ShellError::Io(IoError::new_with_additional_context(
err.kind().not_found_as(NotFound::Directory),
err.not_found_as(NotFound::Directory),
span,
PathBuf::from(backup_path),
"config.nu could not be backed up",
@ -69,7 +69,7 @@ impl Command for ConfigReset {
if let Ok(mut file) = std::fs::File::create(&nu_config) {
if let Err(err) = writeln!(&mut file, "{config_file}") {
return Err(ShellError::Io(IoError::new_with_additional_context(
err.kind().not_found_as(NotFound::File),
err.not_found_as(NotFound::File),
span,
PathBuf::from(nu_config),
"config.nu could not be written to",
@ -86,7 +86,7 @@ impl Command for ConfigReset {
backup_path.push(format!("oldenv-{}.nu", Local::now().format("%F-%H-%M-%S"),));
if let Err(err) = std::fs::rename(env_config.clone(), &backup_path) {
return Err(ShellError::Io(IoError::new_with_additional_context(
err.kind().not_found_as(NotFound::Directory),
err.not_found_as(NotFound::Directory),
span,
PathBuf::from(backup_path),
"env.nu could not be backed up",
@ -96,7 +96,7 @@ impl Command for ConfigReset {
if let Ok(mut file) = std::fs::File::create(&env_config) {
if let Err(err) = writeln!(&mut file, "{config_file}") {
return Err(ShellError::Io(IoError::new_with_additional_context(
err.kind().not_found_as(NotFound::File),
err.not_found_as(NotFound::File),
span,
PathBuf::from(env_config),
"env.nu could not be written to",

View File

@ -2,7 +2,11 @@ use nu_engine::{
command_prelude::*, find_in_dirs_env, get_dirs_var_from_call, get_eval_block_with_early_return,
redirect_env,
};
use nu_protocol::{BlockId, engine::CommandType, shell_error::io::IoError};
use nu_protocol::{
BlockId,
engine::CommandType,
shell_error::{self, io::IoError},
};
use std::path::PathBuf;
/// Source a file for environment variables.
@ -66,7 +70,7 @@ impl Command for SourceEnv {
PathBuf::from(&path)
} else {
return Err(ShellError::Io(IoError::new(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
source_filename.span,
PathBuf::from(source_filename.item),
)));

View File

@ -53,7 +53,7 @@ impl Command for JobKill {
jobs.kill_and_remove(id).map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"Failed to kill the requested job",
nu_protocol::location!(),
))

View File

@ -121,7 +121,7 @@ impl Command for JobSpawn {
Err(err) => {
jobs.remove_job(id);
Err(ShellError::Io(IoError::new_with_additional_context(
err.kind(),
err,
call.head,
None,
"Failed to spawn thread for job",

View File

@ -3,7 +3,6 @@ use nu_protocol::{
JobId,
engine::{FrozenJob, Job, ThreadJob},
process::check_ok,
shell_error,
};
use nu_system::{ForegroundWaitStatus, kill_by_pid};
@ -123,7 +122,7 @@ fn unfreeze_job(
if !thread_job.try_add_pid(pid) {
kill_by_pid(pid.into()).map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"job was interrupted; could not kill foreground process",
nu_protocol::location!(),
))
@ -163,7 +162,7 @@ fn unfreeze_job(
Ok(ForegroundWaitStatus::Finished(status)) => check_ok(status, false, span),
Err(err) => Err(ShellError::Io(IoError::new_internal(
shell_error::io::ErrorKind::Std(err.kind()),
err,
"Failed to unfreeze foreground process",
nu_protocol::location!(),
))),

View File

@ -77,7 +77,7 @@ impl Command for Cd {
if let Ok(path) = nu_path::canonicalize_with(path_no_whitespace, &cwd) {
if !path.is_dir() {
return Err(shell_error::io::IoError::new(
shell_error::io::ErrorKind::Std(
shell_error::io::ErrorKind::from_std(
std::io::ErrorKind::NotADirectory,
),
v.span,
@ -106,7 +106,9 @@ impl Command for Cd {
};
if !path.is_dir() {
return Err(shell_error::io::IoError::new(
shell_error::io::ErrorKind::Std(std::io::ErrorKind::NotADirectory),
shell_error::io::ErrorKind::from_std(
std::io::ErrorKind::NotADirectory,
),
v.span,
path,
)
@ -132,9 +134,12 @@ impl Command for Cd {
stack.set_cwd(path)?;
Ok(PipelineData::empty())
}
PermissionResult::PermissionDenied => {
Err(IoError::new(std::io::ErrorKind::PermissionDenied, call.head, path).into())
}
PermissionResult::PermissionDenied => Err(IoError::new(
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::PermissionDenied),
call.head,
path,
)
.into()),
}
}

View File

@ -5,7 +5,10 @@ use nu_engine::glob_from;
use nu_engine::{command_prelude::*, env::current_dir};
use nu_glob::MatchOptions;
use nu_path::{expand_path_with, expand_to_real_path};
use nu_protocol::{DataSource, NuGlob, PipelineMetadata, Signals, shell_error::io::IoError};
use nu_protocol::{
DataSource, NuGlob, PipelineMetadata, Signals,
shell_error::{self, io::IoError},
};
use pathdiff::diff_paths;
use rayon::prelude::*;
#[cfg(unix)]
@ -252,7 +255,7 @@ fn ls_for_one_pattern(
// it makes no sense to list an empty string.
if path.item.as_ref().is_empty() {
return Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::NotFound),
path.span,
PathBuf::from(path.item.to_string()),
"empty string('') directory or file does not exist",
@ -357,7 +360,7 @@ fn ls_for_one_pattern(
let count = std::thread::available_parallelism()
.map_err(|err| {
IoError::new_with_additional_context(
err.kind(),
err,
call_span,
None,
"Could not get available parallelism",
@ -793,6 +796,7 @@ fn unix_time_to_local_date_time(secs: i64) -> Option<DateTime<Local>> {
mod windows_helper {
use super::*;
use nu_protocol::shell_error;
use std::os::windows::prelude::OsStrExt;
use windows::Win32::Foundation::FILETIME;
use windows::Win32::Storage::FileSystem::{
@ -928,7 +932,7 @@ mod windows_helper {
Ok(find_data)
}
Err(e) => Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::Other,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
span,
PathBuf::from(filename),
format!("Could not read metadata: {e}"),
@ -973,11 +977,11 @@ fn read_dir(
let signals_clone = signals.clone();
let items = f
.read_dir()
.map_err(|err| IoError::new(err.kind(), span, f.clone()))?
.map_err(|err| IoError::new(err, span, f.clone()))?
.map(move |d| {
signals_clone.check(span)?;
d.map(|r| r.path())
.map_err(|err| IoError::new(err.kind(), span, f.clone()))
.map_err(|err| IoError::new(err, span, f.clone()))
.map_err(ShellError::from)
});
if !use_threads {

View File

@ -121,7 +121,7 @@ impl Command for Open {
if permission_denied(path) {
let err = IoError::new(
std::io::ErrorKind::PermissionDenied,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::PermissionDenied),
arg_span,
PathBuf::from(path),
);
@ -162,14 +162,18 @@ impl Command for Open {
// At least under windows this check ensures that we don't get a
// permission denied error on directories
return Err(ShellError::Io(IoError::new(
shell_error::io::ErrorKind::Std(std::io::ErrorKind::IsADirectory),
#[allow(
deprecated,
reason = "we don't have a IsADirectory variant here, so we provide one"
)]
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::IsADirectory),
arg_span,
PathBuf::from(path),
)));
}
let file = std::fs::File::open(path)
.map_err(|err| IoError::new(err.kind(), arg_span, PathBuf::from(path)))?;
.map_err(|err| IoError::new(err, arg_span, PathBuf::from(path)))?;
// No content_type by default - Is added later if no converter is found
let stream = PipelineData::ByteStream(

View File

@ -304,7 +304,7 @@ fn rm(
&& matches!(
e,
ShellError::Io(IoError {
kind: shell_error::io::ErrorKind::Std(std::io::ErrorKind::NotFound),
kind: shell_error::io::ErrorKind::Std(std::io::ErrorKind::NotFound, ..),
..
})
))
@ -420,7 +420,7 @@ fn rm(
};
if let Err(e) = result {
Err(ShellError::Io(IoError::new(e.kind(), span, f)))
Err(ShellError::Io(IoError::new(e, span, f)))
} else if verbose {
let msg = if interactive && !confirmed {
"not deleted"

View File

@ -130,7 +130,7 @@ impl Command for Save {
io::copy(&mut tee, &mut io::stderr())
}
}
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
}
Ok(())
}
@ -428,20 +428,24 @@ fn open_file(path: &Path, span: Span, append: bool) -> Result<File, ShellError>
(true, true) => std::fs::OpenOptions::new()
.append(true)
.open(path)
.map_err(|err| err.kind().into()),
.map_err(|err| err.into()),
_ => {
// This is a temporary solution until `std::fs::File::create` is fixed on Windows (rust-lang/rust#134893)
// A TOCTOU problem exists here, which may cause wrong error message to be shown
#[cfg(target_os = "windows")]
if path.is_dir() {
Err(nu_protocol::shell_error::io::ErrorKind::Std(
#[allow(
deprecated,
reason = "we don't get a IsADirectory error, so we need to provide it"
)]
Err(nu_protocol::shell_error::io::ErrorKind::from_std(
std::io::ErrorKind::IsADirectory,
))
} else {
std::fs::File::create(path).map_err(|err| err.kind().into())
std::fs::File::create(path).map_err(|err| err.into())
}
#[cfg(not(target_os = "windows"))]
std::fs::File::create(path).map_err(|err| err.kind().into())
std::fs::File::create(path).map_err(|err| err.into())
}
};

View File

@ -1,6 +1,9 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_protocol::{NuGlob, shell_error::io::IoError};
use nu_protocol::{
NuGlob,
shell_error::{self, io::IoError},
};
use std::path::PathBuf;
use uu_cp::{BackupMode, CopyMode, UpdateMode};
@ -198,7 +201,7 @@ impl Command for UCp {
.collect();
if exp_files.is_empty() {
return Err(ShellError::Io(IoError::new(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
p.span,
PathBuf::from(p.item.to_string()),
)));

View File

@ -1,7 +1,10 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with;
use nu_protocol::{NuGlob, shell_error::io::IoError};
use nu_protocol::{
NuGlob,
shell_error::{self, io::IoError},
};
use std::{ffi::OsString, path::PathBuf};
use uu_mv::{BackupMode, UpdateMode};
@ -139,7 +142,7 @@ impl Command for UMv {
.collect();
if exp_files.is_empty() {
return Err(ShellError::Io(IoError::new(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
p.span,
PathBuf::from(p.item.to_string()),
)));

View File

@ -227,7 +227,7 @@ impl Command for UTouch {
TouchError::ReferenceFileInaccessible(reference_path, io_err) => {
let span = reference_span.expect("touch should've been given a reference file");
ShellError::Io(IoError::new_with_additional_context(
io_err.kind(),
io_err,
span,
reference_path,
"failed to read metadata",

View File

@ -86,7 +86,7 @@ impl Command for Watch {
Ok(p) => p,
Err(err) => {
return Err(ShellError::Io(IoError::new(
err.kind(),
err,
path_arg.span,
PathBuf::from(path_no_whitespace),
)));

View File

@ -37,7 +37,7 @@ pub fn empty(
.bytes()
.next()
.transpose()
.map_err(|err| IoError::new(err.kind(), span, None))?
.map_err(|err| IoError::new(err, span, None))?
.is_none();
if negate {
Ok(Value::bool(!is_empty, head).into_pipeline_data())

View File

@ -182,7 +182,7 @@ fn first_helper(
let mut byte = [0u8];
if reader
.read(&mut byte)
.map_err(|err| IoError::new(err.kind(), span, None))?
.map_err(|err| IoError::new(err, span, None))?
> 0
{
Ok(Value::int(byte[0] as i64, head).into_pipeline_data())

View File

@ -137,7 +137,7 @@ interleave
}
})
.map(|_| ())
.map_err(|err| IoError::new(err.kind(), head, None).into())
.map_err(|err| IoError::new(err, head, None).into())
})
})?;

View File

@ -166,7 +166,7 @@ impl Command for Last {
let mut buf = VecDeque::with_capacity(rows + TAKE as usize);
loop {
let taken = std::io::copy(&mut (&mut reader).take(TAKE), &mut buf)
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
if buf.len() > rows {
buf.drain(..(buf.len() - rows));
}

View File

@ -1,6 +1,8 @@
use nu_engine::{command_prelude::*, get_eval_block_with_early_return};
#[cfg(feature = "os")]
use nu_protocol::process::ChildPipe;
#[cfg(test)]
use nu_protocol::shell_error;
use nu_protocol::{
ByteStream, ByteStreamSource, OutDest, PipelineMetadata, Signals,
byte_stream::copy_with_signals, engine::Closure, report_shell_error, shell_error::io::IoError,
@ -440,7 +442,7 @@ fn spawn_tee(
eval_block(PipelineData::ByteStream(stream, info.metadata))
})
.map_err(|err| {
IoError::new_with_additional_context(err.kind(), info.span, None, "Could not spawn tee")
IoError::new_with_additional_context(err, info.span, None, "Could not spawn tee")
})?;
Ok(TeeThread { sender, thread })
@ -481,13 +483,8 @@ fn copy_on_thread(
Ok(())
})
.map_err(|err| {
IoError::new_with_additional_context(
err.kind(),
span,
None,
"Could not spawn stderr copier",
)
.into()
IoError::new_with_additional_context(err, span, None, "Could not spawn stderr copier")
.into()
})
}
@ -532,7 +529,7 @@ fn tee_forwards_errors_back_immediately() {
let slow_input = (0..100).inspect(|_| std::thread::sleep(Duration::from_millis(1)));
let iter = tee(slow_input, |_| {
Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::Other,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
Span::test_data(),
None,
"test",
@ -564,7 +561,7 @@ fn tee_waits_for_the_other_thread() {
std::thread::sleep(Duration::from_millis(10));
waited_clone.store(true, Ordering::Relaxed);
Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::Other,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
Span::test_data(),
None,
"test",

View File

@ -134,7 +134,7 @@ fn read_json_lines(
.lines()
.filter(|line| line.as_ref().is_ok_and(|line| !line.trim().is_empty()) || line.is_err())
.map(move |line| {
let line = line.map_err(|err| IoError::new(err.kind(), span, None))?;
let line = line.map_err(|err| IoError::new(err, span, None))?;
if strict {
convert_string_to_value_strict(&line, span)
} else {

View File

@ -8,7 +8,7 @@ use std::{iter, sync::Arc};
fn make_csv_error(error: csv::Error, format_name: &str, head: Span) -> ShellError {
if let csv::ErrorKind::Io(error) = error.kind() {
IoError::new(error.kind(), head, None).into()
IoError::new(error, head, None).into()
} else {
ShellError::GenericError {
error: format!("Failed to generate {format_name} data"),

View File

@ -152,7 +152,7 @@ impl From<WriteError> for ShellError {
help: None,
inner: vec![],
},
WriteError::Io(err, span) => ShellError::Io(IoError::new(err.kind(), span, None)),
WriteError::Io(err, span) => ShellError::Io(IoError::new(err, span, None)),
WriteError::Shell(err) => *err,
}
}

View File

@ -95,7 +95,7 @@ impl Command for ToMsgpackz {
serialize_types,
)?;
out.flush()
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
.map_err(|err| IoError::new(err, call.head, None))?;
drop(out);
Ok(Value::binary(out_buf, call.head).into_pipeline_data())

View File

@ -55,13 +55,8 @@ impl Command for Source {
let cwd = engine_state.cwd_as_string(Some(stack))?;
let pb = std::path::PathBuf::from(block_id_name);
let parent = pb.parent().unwrap_or(std::path::Path::new(""));
let file_path = canonicalize_with(pb.as_path(), cwd).map_err(|err| {
IoError::new(
err.kind().not_found_as(NotFound::File),
call.head,
pb.clone(),
)
})?;
let file_path = canonicalize_with(pb.as_path(), cwd)
.map_err(|err| IoError::new(err.not_found_as(NotFound::File), call.head, pb.clone()))?;
// Note: We intentionally left out PROCESS_PATH since it's supposed to
// to work like argv[0] in C, which is the name of the program being executed.

View File

@ -374,9 +374,7 @@ fn send_multipart_request(
let mut builder = MultipartWriter::new();
let err = |e: std::io::Error| {
ShellErrorOrRequestError::ShellError(
IoError::new_with_additional_context(e.kind(), span, None, e).into(),
)
ShellErrorOrRequestError::ShellError(IoError::new(e, span, None).into())
};
for (col, val) in val.into_owned() {
@ -466,12 +464,7 @@ fn send_cancellable_request(
let _ = tx.send(ret); // may fail if the user has cancelled the operation
})
.map_err(|err| {
IoError::new_with_additional_context(
err.kind(),
span,
None,
"Could not spawn HTTP requester",
)
IoError::new_with_additional_context(err, span, None, "Could not spawn HTTP requester")
})
.map_err(ShellError::from)?;
@ -529,12 +522,7 @@ fn send_cancellable_request_bytes(
let _ = tx.send(ret);
})
.map_err(|err| {
IoError::new_with_additional_context(
err.kind(),
span,
None,
"Could not spawn HTTP requester",
)
IoError::new_with_additional_context(err, span, None, "Could not spawn HTTP requester")
})
.map_err(ShellError::from)?;
@ -685,7 +673,7 @@ fn handle_response_error(span: Span, requested_url: &str, response_err: Error) -
break 'io generic_network_failure();
};
ShellError::Io(IoError::new(io_error.kind(), span, None))
ShellError::Io(IoError::new(io_error, span, None))
}
_ => generic_network_failure(),
}

View File

@ -132,7 +132,7 @@ fn get_free_port(
}
Err(IoError::new_with_additional_context(
last_err.expect("range not empty, validated before").kind(),
last_err.expect("range not empty, validated before"),
range_span,
None,
"Every port has been tried, but no valid one was found",

View File

@ -153,7 +153,7 @@ fn exists(path: &Path, span: Span, args: &Arguments) -> Value {
Value::bool(
match exists {
Ok(exists) => exists,
Err(err) => return Value::error(IoError::new(err.kind(), span, path).into(), span),
Err(err) => return Value::error(IoError::new(err, span, path).into(), span),
},
span,
)

View File

@ -1,6 +1,9 @@
use nu_engine::command_prelude::*;
use nu_path::expand_path_with;
use nu_protocol::{engine::StateWorkingSet, shell_error::io::IoError};
use nu_protocol::{
engine::StateWorkingSet,
shell_error::{self, io::IoError},
};
#[derive(Clone)]
pub struct PathSelf;
@ -56,7 +59,7 @@ impl Command for PathSelf {
let cwd = working_set.permanent_state.cwd(None)?;
let current_file = working_set.files.top().ok_or_else(|| {
IoError::new_with_additional_context(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
call.head,
None,
"Couldn't find current file",
@ -67,7 +70,7 @@ impl Command for PathSelf {
let dir = expand_path_with(
current_file.parent().ok_or_else(|| {
IoError::new_with_additional_context(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
call.head,
current_file.to_owned(),
"Couldn't find current file's parent.",

View File

@ -108,7 +108,7 @@ fn path_type(path: &Path, span: Span, args: &Arguments) -> Value {
match path.symlink_metadata() {
Ok(metadata) => Value::string(get_file_type(&metadata), span),
Err(err) if err.kind() == io::ErrorKind::NotFound => Value::nothing(span),
Err(err) => Value::error(IoError::new(err.kind(), span, None).into(), span),
Err(err) => Value::error(IoError::new(err, span, None).into(), span),
}
}

View File

@ -77,7 +77,7 @@ impl FileInfo {
long,
})
}
Err(e) => Err(IoError::new(e.kind(), tag, path).into()),
Err(e) => Err(IoError::new(e, tag, path).into()),
}
}
}

View File

@ -7,7 +7,7 @@ use crossterm::{
};
use itertools::Itertools;
use nu_engine::command_prelude::*;
use nu_protocol::shell_error::io::IoError;
use nu_protocol::shell_error::{self, io::IoError};
use std::{io::Write, time::Duration};
@ -116,7 +116,9 @@ impl Command for Input {
crossterm::terminal::disable_raw_mode()
.map_err(&from_io_error)?;
return Err(IoError::new(
std::io::ErrorKind::Interrupted,
shell_error::io::ErrorKind::from_std(
std::io::ErrorKind::Interrupted,
),
call.head,
None,
)
@ -156,7 +158,7 @@ impl Command for Input {
terminal::Clear(ClearType::CurrentLine),
cursor::MoveToColumn(0),
)
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
.map_err(|err| IoError::new(err, call.head, None))?;
if let Some(prompt) = &prompt {
execute!(std::io::stdout(), Print(prompt.to_string()))
.map_err(&from_io_error)?;

View File

@ -84,7 +84,7 @@ There are 4 `key_type` variants:
let add_raw = call.has_flag(engine_state, stack, "raw")?;
let config = engine_state.get_config();
terminal::enable_raw_mode().map_err(|err| IoError::new(err.kind(), head, None))?;
terminal::enable_raw_mode().map_err(|err| IoError::new(err, head, None))?;
if config.use_kitty_protocol {
if let Ok(false) = crossterm::terminal::supports_keyboard_enhancement() {
@ -123,7 +123,7 @@ There are 4 `key_type` variants:
})?;
let event = parse_event(head, &event, &event_type_filter, add_raw);
if let Some(event) = event {
terminal::disable_raw_mode().map_err(|err| IoError::new(err.kind(), head, None))?;
terminal::disable_raw_mode().map_err(|err| IoError::new(err, head, None))?;
if config.use_kitty_protocol {
let _ = execute!(
std::io::stdout(),
@ -230,17 +230,17 @@ impl EventTypeFilter {
fn enable_events(&self, span: Span) -> Result<DeferredConsoleRestore, ShellError> {
if self.listen_mouse {
crossterm::execute!(stdout(), EnableMouseCapture)
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
}
if self.listen_paste {
crossterm::execute!(stdout(), EnableBracketedPaste)
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
}
if self.listen_focus {
crossterm::execute!(stdout(), crossterm::event::EnableFocusChange)
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
}
Ok(DeferredConsoleRestore {

View File

@ -142,12 +142,7 @@ impl Command for InputList {
.report(false)
.interact_on_opt(&Term::stderr())
.map_err(|dialoguer::Error::IO(err)| {
IoError::new_with_additional_context(
err.kind(),
call.head,
None,
INTERACT_ERROR,
)
IoError::new_with_additional_context(err, call.head, None, INTERACT_ERROR)
})?,
)
} else if fuzzy {
@ -164,12 +159,7 @@ impl Command for InputList {
.report(false)
.interact_on_opt(&Term::stderr())
.map_err(|dialoguer::Error::IO(err)| {
IoError::new_with_additional_context(
err.kind(),
call.head,
None,
INTERACT_ERROR,
)
IoError::new_with_additional_context(err, call.head, None, INTERACT_ERROR)
})?,
)
} else {
@ -185,12 +175,7 @@ impl Command for InputList {
.report(false)
.interact_on_opt(&Term::stderr())
.map_err(|dialoguer::Error::IO(err)| {
IoError::new_with_additional_context(
err.kind(),
call.head,
None,
INTERACT_ERROR,
)
IoError::new_with_additional_context(err, call.head, None, INTERACT_ERROR)
})?,
)
};

View File

@ -99,19 +99,17 @@ The `prefix` is not included in the output."
let prefix = prefix.unwrap_or_default();
let terminator: Option<Vec<u8>> = call.get_flag(engine_state, stack, "terminator")?;
crossterm::terminal::enable_raw_mode()
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
crossterm::terminal::enable_raw_mode().map_err(|err| IoError::new(err, call.head, None))?;
scopeguard::defer! {
let _ = crossterm::terminal::disable_raw_mode();
}
// clear terminal events
while crossterm::event::poll(Duration::from_secs(0))
.map_err(|err| IoError::new(err.kind(), call.head, None))?
.map_err(|err| IoError::new(err, call.head, None))?
{
// If there's an event, read it to remove it from the queue
let _ = crossterm::event::read()
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
let _ = crossterm::event::read().map_err(|err| IoError::new(err, call.head, None))?;
}
let mut b = [0u8; 1];
@ -122,17 +120,17 @@ The `prefix` is not included in the output."
let mut stdout = std::io::stdout().lock();
stdout
.write_all(&query)
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
.map_err(|err| IoError::new(err, call.head, None))?;
stdout
.flush()
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
.map_err(|err| IoError::new(err, call.head, None))?;
}
// Validate and skip prefix
for bc in prefix {
stdin
.read_exact(&mut b)
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
.map_err(|err| IoError::new(err, call.head, None))?;
if b[0] != bc {
return Err(ShellError::GenericError {
error: "Input did not begin with expected sequence".into(),
@ -151,7 +149,7 @@ The `prefix` is not included in the output."
loop {
stdin
.read_exact(&mut b)
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
.map_err(|err| IoError::new(err, call.head, None))?;
if b[0] == CTRL_C {
return Err(ShellError::InterruptedByUser {
@ -173,7 +171,7 @@ The `prefix` is not included in the output."
loop {
stdin
.read_exact(&mut b)
.map_err(|err| IoError::new(err.kind(), call.head, None))?;
.map_err(|err| IoError::new(err, call.head, None))?;
if b[0] == CTRL_C {
break;

View File

@ -255,7 +255,7 @@ fn parse_file_script(
match std::fs::read(path) {
Ok(contents) => parse_script(working_set, Some(&filename), &contents, is_debug, call_head),
Err(err) => Err(ShellError::Io(IoError::new(
err.kind().not_found_as(NotFound::File),
err.not_found_as(NotFound::File),
path_span,
PathBuf::from(path),
))),

View File

@ -93,7 +93,7 @@ fn registry_query(
let reg_hive = get_reg_hive(engine_state, stack, call)?;
let reg_key = reg_hive
.open_subkey(registry_key.item)
.map_err(|err| IoError::new(err.kind(), *registry_key_span, None))?;
.map_err(|err| IoError::new(err, *registry_key_span, None))?;
if registry_value.is_none() {
let mut reg_values = vec![];

View File

@ -205,11 +205,11 @@ impl Command for External {
let stderr = stack.stderr();
let merged_stream = if matches!(stdout, OutDest::Pipe) && matches!(stderr, OutDest::Pipe) {
let (reader, writer) =
os_pipe::pipe().map_err(|err| IoError::new(err.kind(), call.head, None))?;
os_pipe::pipe().map_err(|err| IoError::new(err, call.head, None))?;
command.stdout(
writer
.try_clone()
.map_err(|err| IoError::new(err.kind(), call.head, None))?,
.map_err(|err| IoError::new(err, call.head, None))?,
);
command.stderr(writer);
Some(reader)
@ -220,8 +220,7 @@ impl Command for External {
command.stdout(Stdio::null());
} else {
command.stdout(
Stdio::try_from(stdout)
.map_err(|err| IoError::new(err.kind(), call.head, None))?,
Stdio::try_from(stdout).map_err(|err| IoError::new(err, call.head, None))?,
);
}
@ -231,8 +230,7 @@ impl Command for External {
command.stderr(Stdio::null());
} else {
command.stderr(
Stdio::try_from(stderr)
.map_err(|err| IoError::new(err.kind(), call.head, None))?,
Stdio::try_from(stderr).map_err(|err| IoError::new(err, call.head, None))?,
);
}
@ -280,7 +278,7 @@ impl Command for External {
let mut child = child.map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not spawn foreground child",
nu_protocol::location!(),
)
@ -290,7 +288,7 @@ impl Command for External {
if !thread_job.try_add_pid(child.pid()) {
kill_by_pid(child.pid().into()).map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"Could not spawn external stdin worker",
nu_protocol::location!(),
))
@ -310,7 +308,7 @@ impl Command for External {
})
.map_err(|err| {
IoError::new_with_additional_context(
err.kind(),
err,
call.head,
None,
"Could not spawn external stdin worker",
@ -498,7 +496,7 @@ fn write_pipeline_data(
} else if let PipelineData::Value(Value::Binary { val, .. }, ..) = data {
writer.write_all(&val).map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not write pipeline data",
nu_protocol::location!(),
)
@ -518,7 +516,7 @@ fn write_pipeline_data(
let bytes = value.coerce_into_binary()?;
writer.write_all(&bytes).map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not write pipeline data",
nu_protocol::location!(),
)

View File

@ -518,7 +518,7 @@ fn pretty_hex_stream(stream: ByteStream, span: Span) -> ByteStream {
(&mut reader)
.take(cfg.width as u64)
.read_to_end(&mut read_buf)
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
if !read_buf.is_empty() {
nu_pretty_hex::hex_write(&mut write_buf, &read_buf, cfg, Some(true))

View File

@ -60,5 +60,5 @@ fn self_path_runtime() {
fn self_path_repl() {
let actual = nu!("const foo = path self; $foo");
assert!(!actual.status.success());
assert!(actual.err.contains("nu::shell::io::not_found"));
assert!(actual.err.contains("nu::shell::io::file_not_found"));
}

View File

@ -6,6 +6,8 @@ use nu_test_support::playground::Playground;
use rstest::rstest;
#[cfg(not(windows))]
use std::fs;
#[cfg(windows)]
use std::{fs::OpenOptions, os::windows::fs::OpenOptionsExt};
#[test]
fn removes_a_file() {
@ -562,3 +564,26 @@ fn rm_with_tilde() {
assert!(!files_exist_at(&["~tilde"], dirs.test()));
})
}
#[test]
#[cfg(windows)]
fn rm_already_in_use() {
Playground::setup("rm_already_in_use", |dirs, sandbox| {
sandbox.with_files(&[EmptyFile("i_will_be_used.txt")]);
let file_path = dirs.root().join("rm_already_in_use/i_will_be_used.txt");
let _file = OpenOptions::new()
.read(true)
.write(false)
.share_mode(0) // deny all sharing
.open(file_path)
.unwrap();
let outcome = nu!(
cwd: dirs.root(),
"rm rm_already_in_use/i_will_be_used.txt"
);
assert!(outcome.err.contains("nu::shell::io::already_in_use"));
})
}

View File

@ -4,7 +4,7 @@ use nu_protocol::{
ShellError, Span, Type, Value, VarId,
ast::Expr,
engine::{Call, EngineState, Stack, StateWorkingSet},
shell_error::io::{ErrorKindExt, IoError, NotFound},
shell_error::io::{IoError, IoErrorExt, NotFound},
};
use std::{
collections::HashMap,
@ -221,7 +221,7 @@ pub fn current_dir(engine_state: &EngineState, stack: &Stack) -> Result<PathBuf,
// be an absolute path already.
canonicalize_with(&cwd, ".").map_err(|err| {
ShellError::Io(IoError::new_internal_with_path(
err.kind().not_found_as(NotFound::Directory),
err.not_found_as(NotFound::Directory),
"Could not canonicalize current dir",
nu_protocol::location!(),
PathBuf::from(cwd),
@ -241,7 +241,7 @@ pub fn current_dir_const(working_set: &StateWorkingSet) -> Result<PathBuf, Shell
// be an absolute path already.
canonicalize_with(&cwd, ".").map_err(|err| {
ShellError::Io(IoError::new_internal_with_path(
err.kind().not_found_as(NotFound::Directory),
err.not_found_as(NotFound::Directory),
"Could not canonicalize current dir",
nu_protocol::location!(),
PathBuf::from(cwd),

View File

@ -1529,7 +1529,7 @@ fn open_file(ctx: &EvalContext<'_>, path: &Value, append: bool) -> Result<Arc<Fi
let file = options
.create(true)
.open(&path_expanded)
.map_err(|err| IoError::new(err.kind(), path.span(), path_expanded))?;
.map_err(|err| IoError::new(err, path.span(), path_expanded))?;
Ok(Arc::new(file))
}

View File

@ -81,7 +81,7 @@ pub fn glob_from(
}
Ok(p) => p,
Err(err) => {
return Err(IoError::new(err.kind(), pattern_span, path).into());
return Err(IoError::new(err, pattern_span, path).into());
}
};
(path.parent().map(|parent| parent.to_path_buf()), path)

View File

@ -87,7 +87,7 @@ impl CommunicationMode {
.and_then(|name| ListenerOptions::new().name(name).create_sync())
.map_err(|err| {
IoError::new_internal(
err.kind(),
err,
format!(
"Could not interpret local socket name {:?}",
name.to_string_lossy()
@ -117,7 +117,7 @@ impl CommunicationMode {
.and_then(|name| ls::Stream::connect(name))
.map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
format!(
"Could not interpret local socket name {:?}",
name.to_string_lossy()
@ -190,7 +190,7 @@ impl PreparedServerCommunication {
.set_nonblocking(ListenerNonblockingMode::Accept)
.map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not set non-blocking mode accept for listener",
nu_protocol::location!(),
)
@ -204,7 +204,7 @@ impl PreparedServerCommunication {
// good measure. Had an issue without this on macOS.
stream.set_nonblocking(false).map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not disable non-blocking mode for listener",
nu_protocol::location!(),
)
@ -217,7 +217,7 @@ impl PreparedServerCommunication {
// `WouldBlock` is ok, just means it's not ready yet, but some other
// kind of error should be reported
return Err(ShellError::Io(IoError::new_internal(
err.kind(),
err,
"Accepting new data from listener failed",
nu_protocol::location!(),
)));

View File

@ -82,7 +82,7 @@ where
fn flush(&self) -> Result<(), ShellError> {
self.0.lock().flush().map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"PluginWrite could not flush",
nu_protocol::location!(),
))
@ -112,7 +112,7 @@ where
})?;
lock.flush().map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"PluginWrite could not flush",
nu_protocol::location!(),
))
@ -340,7 +340,7 @@ where
writer.write_all(std::iter::from_fn(move || match reader.read(buf) {
Ok(0) => None,
Ok(len) => Some(Ok(buf[..len].to_vec())),
Err(err) => Some(Err(ShellError::from(IoError::new(err.kind(), span, None)))),
Err(err) => Some(Err(ShellError::from(IoError::new(err, span, None)))),
}))?;
Ok(())
}
@ -368,7 +368,7 @@ where
})
.map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not spawn plugin stream background writer",
nu_protocol::location!(),
)

View File

@ -246,7 +246,7 @@ fn read_pipeline_data_byte_stream() -> Result<(), ShellError> {
ByteStreamSource::Read(mut read) => {
let mut buf = Vec::new();
read.read_to_end(&mut buf)
.map_err(|err| IoError::new(err.kind(), test_span, None))?;
.map_err(|err| IoError::new(err, test_span, None))?;
let iter = buf.chunks_exact(out_pattern.len());
assert_eq!(iter.len(), iterations);
for chunk in iter {

View File

@ -1,5 +1,8 @@
use nu_plugin_protocol::{PluginInput, PluginOutput};
use nu_protocol::{ShellError, location, shell_error::io::IoError};
use nu_protocol::{
ShellError, location,
shell_error::{self, io::IoError},
};
use serde::Deserialize;
use crate::{Encoder, PluginEncoder};
@ -28,7 +31,7 @@ impl Encoder<PluginInput> for JsonSerializer {
serde_json::to_writer(&mut *writer, plugin_input).map_err(json_encode_err)?;
writer.write_all(b"\n").map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"Failed to write final line break",
location!(),
))
@ -55,7 +58,7 @@ impl Encoder<PluginOutput> for JsonSerializer {
serde_json::to_writer(&mut *writer, plugin_output).map_err(json_encode_err)?;
writer.write_all(b"\n").map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"JsonSerializer could not encode linebreak",
nu_protocol::location!(),
))
@ -77,7 +80,7 @@ impl Encoder<PluginOutput> for JsonSerializer {
fn json_encode_err(err: serde_json::Error) -> ShellError {
if err.is_io() {
ShellError::Io(IoError::new_internal(
err.io_error_kind().expect("is io"),
shell_error::io::ErrorKind::from_std(err.io_error_kind().expect("is io")),
"Could not encode with json",
nu_protocol::location!(),
))
@ -94,7 +97,7 @@ fn json_decode_err<T>(err: serde_json::Error) -> Result<Option<T>, ShellError> {
Ok(None)
} else if err.is_io() {
Err(ShellError::Io(IoError::new_internal(
err.io_error_kind().expect("is io"),
shell_error::io::ErrorKind::from_std(err.io_error_kind().expect("is io")),
"Could not decode with json",
nu_protocol::location!(),
)))

View File

@ -1,7 +1,10 @@
use std::io::ErrorKind;
use nu_plugin_protocol::{PluginInput, PluginOutput};
use nu_protocol::{ShellError, shell_error::io::IoError};
use nu_protocol::{
ShellError,
shell_error::{self, io::IoError},
};
use serde::Deserialize;
use crate::{Encoder, PluginEncoder};
@ -66,7 +69,7 @@ fn rmp_encode_err(err: rmp_serde::encode::Error) -> ShellError {
// I/O error
ShellError::Io(IoError::new_internal(
// TODO: get a better kind here
std::io::ErrorKind::Other,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
"Could not encode with rmp",
nu_protocol::location!(),
))
@ -92,7 +95,7 @@ fn rmp_decode_err<T>(err: rmp_serde::decode::Error) -> Result<Option<T>, ShellEr
// I/O error
Err(ShellError::Io(IoError::new_internal(
// TODO: get a better kind here
std::io::ErrorKind::Other,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
"Could not decode with rmp",
nu_protocol::location!(),
)))

View File

@ -379,7 +379,7 @@ macro_rules! generate_tests {
.with_help("some help")
.with_label("msg", Span::new(2, 30))
.with_inner(ShellError::Io(IoError::new(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::NotFound),
Span::test_data(),
None,
)));

View File

@ -20,6 +20,7 @@ use nu_protocol::{
Spanned, Value,
ast::{Math, Operator},
engine::Closure,
shell_error,
};
use serde::{Deserialize, Serialize};
use std::{
@ -87,7 +88,7 @@ fn manager_consume_all_exits_after_streams_and_interfaces_are_dropped() -> Resul
fn test_io_error() -> ShellError {
ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::Other,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
Span::test_data(),
None,
"test io error",

View File

@ -190,11 +190,7 @@ impl PersistentPlugin {
// Start the plugin garbage collector
let gc = PluginGc::new(mutable.gc_config.clone(), &self).map_err(|err| {
IoError::new_internal(
err.kind(),
"Could not start plugin gc",
nu_protocol::location!(),
)
IoError::new_internal(err, "Could not start plugin gc", nu_protocol::location!())
})?;
let pid = child.id();

View File

@ -66,7 +66,7 @@ pub(crate) fn spawn_fake_plugin(
.spawn(move || manager.consume_all(output_read).expect("Plugin read error"))
.map_err(|err| {
IoError::new_internal(
err.kind(),
err,
format!("Could not spawn fake plugin interface reader ({name})"),
nu_protocol::location!(),
)
@ -87,7 +87,7 @@ pub(crate) fn spawn_fake_plugin(
})
.map_err(|err| {
IoError::new_internal(
err.kind(),
err,
format!("Could not spawn fake plugin runner ({name})"),
nu_protocol::location!(),
)

View File

@ -1048,7 +1048,7 @@ impl ForegroundGuard {
// This should always succeed, frankly, but handle the error just in case
setpgid(Pid::from_raw(0), Pid::from_raw(0)).map_err(|err| {
nu_protocol::shell_error::io::IoError::new_internal(
std::io::Error::from(err).kind(),
std::io::Error::from(err),
"Could not set pgid",
nu_protocol::location!(),
)

View File

@ -12,7 +12,7 @@ use nu_plugin_protocol::{
use nu_protocol::{
BlockId, ByteStreamType, Config, CustomValue, IntoInterruptiblePipelineData, LabeledError,
PipelineData, PluginSignature, ShellError, Signals, Span, Spanned, Value, VarId,
engine::Closure,
engine::Closure, shell_error,
};
use std::{
collections::HashMap,
@ -91,7 +91,7 @@ fn manager_consume_all_exits_after_streams_and_interfaces_are_dropped() -> Resul
fn test_io_error() -> ShellError {
ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::Other,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
Span::test_data(),
None,
"test io error",

View File

@ -51,6 +51,7 @@ nix = { workspace = true, default-features = false, features = ["signal"] }
[target.'cfg(windows)'.dependencies]
dirs-sys = { workspace = true }
windows-sys = { workspace = true }
windows = { workspace = true }
[features]
default = ["os"]

View File

@ -353,7 +353,7 @@ impl EngineState {
let cwd = self.cwd(Some(stack))?;
std::env::set_current_dir(cwd).map_err(|err| {
IoError::new_internal(err.kind(), "Could not set current dir", crate::location!())
IoError::new_internal(err, "Could not set current dir", crate::location!())
})?;
if let Some(config) = stack.config.take() {
@ -546,7 +546,7 @@ impl EngineState {
Ok(PluginRegistryFile::default())
} else {
Err(ShellError::Io(IoError::new_internal_with_path(
err.kind(),
err,
"Failed to open plugin file",
crate::location!(),
PathBuf::from(plugin_path),
@ -563,7 +563,7 @@ impl EngineState {
// Write it to the same path
let plugin_file = File::create(plugin_path.as_path()).map_err(|err| {
IoError::new_internal_with_path(
err.kind(),
err,
"Failed to write plugin file",
crate::location!(),
PathBuf::from(plugin_path),

View File

@ -143,11 +143,11 @@ impl LabeledError {
/// [`ShellError`] implements `miette::Diagnostic`:
///
/// ```rust
/// # use nu_protocol::{ShellError, LabeledError, shell_error::io::IoError, Span};
/// # use nu_protocol::{ShellError, LabeledError, shell_error::{self, io::IoError}, Span};
/// #
/// let error = LabeledError::from_diagnostic(
/// &ShellError::Io(IoError::new_with_additional_context(
/// std::io::ErrorKind::Other,
/// shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
/// Span::test_data(),
/// None,
/// "some error"

View File

@ -38,7 +38,7 @@ use super::{ShellError, location::Location};
///
/// # let span = Span::test_data();
/// let path = PathBuf::from("/some/missing/file");
/// let error = IoError::new(std::io::ErrorKind::NotFound, span, path);
/// let error = IoError::new(ErrorKind::FileNotFound, span, path);
/// println!("Error: {:?}", error);
/// ```
///
@ -47,7 +47,7 @@ use super::{ShellError, location::Location};
/// # use nu_protocol::shell_error::io::{IoError, ErrorKind};
// #
/// let error = IoError::new_internal(
/// std::io::ErrorKind::UnexpectedEof,
/// ErrorKind::from_std(std::io::ErrorKind::UnexpectedEof),
/// "Failed to read data from buffer",
/// nu_protocol::location!()
/// );
@ -93,7 +93,7 @@ pub struct IoError {
/// and is part of [`std::io::Error`].
/// If a kind cannot be represented by it, consider adding a new variant to [`ErrorKind`].
///
/// Only in very rare cases should [`std::io::ErrorKind::Other`] be used, make sure you provide
/// Only in very rare cases should [`std::io::ErrorKind::other()`] be used, make sure you provide
/// `additional_context` to get useful errors in these cases.
pub kind: ErrorKind,
@ -125,15 +125,63 @@ pub struct IoError {
pub location: Option<String>,
}
/// Prevents other crates from constructing certain enum variants directly.
///
/// This type is only used to block construction while still allowing pattern matching.
/// It's not meant to be used for anything else.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Sealed;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Diagnostic)]
pub enum ErrorKind {
Std(std::io::ErrorKind),
/// [`std::io::ErrorKind`] from the standard library.
///
/// This variant wraps a standard library error kind and extends our own error enum with it.
/// The hidden field prevents other crates, even our own, from constructing this directly.
/// Most of the time, you already have a full [`std::io::Error`], so just pass that directly to
/// [`IoError::new`] or [`IoError::new_with_additional_context`].
/// This allows us to inspect the raw os error of `std::io::Error`s.
///
/// Matching is still easy:
///
/// ```rust
/// # use nu_protocol::shell_error::io::ErrorKind;
/// #
/// # let err_kind = ErrorKind::from_std(std::io::ErrorKind::NotFound);
/// match err_kind {
/// ErrorKind::Std(std::io::ErrorKind::NotFound, ..) => { /* ... */ }
/// _ => {}
/// }
/// ```
///
/// If you want to provide an [`std::io::ErrorKind`] manually, use [`ErrorKind::from_std`].
#[allow(private_interfaces)]
Std(std::io::ErrorKind, Sealed),
NotAFile,
/// The file or directory is in use by another program.
///
/// On Windows, this maps to
/// [`ERROR_SHARING_VIOLATION`](windows::Win32::Foundation::ERROR_SHARING_VIOLATION) and
/// prevents access like deletion or modification.
AlreadyInUse,
// use these variants in cases where we know precisely whether a file or directory was expected
FileNotFound,
DirectoryNotFound,
}
impl ErrorKind {
/// Construct an [`ErrorKind`] from a [`std::io::ErrorKind`] without a full [`std::io::Error`].
///
/// In most cases, you should use [`IoError::new`] and pass the full [`std::io::Error`] instead.
/// This method is only meant for cases where we provide our own io error kinds.
pub fn from_std(kind: std::io::ErrorKind) -> Self {
Self::Std(kind, Sealed)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error, Diagnostic)]
#[error("{0}")]
pub struct AdditionalContext(String);
@ -237,10 +285,10 @@ impl IoError {
///
/// # Examples
/// ```rust
/// use nu_protocol::shell_error::io::IoError;
/// use nu_protocol::shell_error::{self, io::IoError};
///
/// let error = IoError::new_internal(
/// std::io::ErrorKind::UnexpectedEof,
/// shell_error::io::ErrorKind::from_std(std::io::ErrorKind::UnexpectedEof),
/// "Failed to read from buffer",
/// nu_protocol::location!(),
/// );
@ -268,11 +316,11 @@ impl IoError {
///
/// # Examples
/// ```rust
/// use nu_protocol::shell_error::io::IoError;
/// use nu_protocol::shell_error::{self, io::IoError};
/// use std::path::PathBuf;
///
/// let error = IoError::new_internal_with_path(
/// std::io::ErrorKind::NotFound,
/// shell_error::io::ErrorKind::FileNotFound,
/// "Could not find special file",
/// nu_protocol::location!(),
/// PathBuf::from("/some/file"),
@ -297,14 +345,37 @@ impl IoError {
///
/// This method is particularly useful when you need to handle multiple I/O errors which all
/// take the same span and path.
/// Instead of calling `.map_err(|err| IoError::new(err.kind(), span, path))` every time, you
/// Instead of calling `.map_err(|err| IoError::new(err, span, path))` every time, you
/// can create the factory closure once and pass that into `.map_err`.
pub fn factory<'p, P>(span: Span, path: P) -> impl Fn(std::io::Error) -> Self + use<'p, P>
where
P: Into<Option<&'p Path>>,
{
let path = path.into();
move |err: std::io::Error| IoError::new(err.kind(), span, path.map(PathBuf::from))
move |err: std::io::Error| IoError::new(err, span, path.map(PathBuf::from))
}
}
impl From<std::io::Error> for ErrorKind {
fn from(err: std::io::Error) -> Self {
(&err).into()
}
}
impl From<&std::io::Error> for ErrorKind {
fn from(err: &std::io::Error) -> Self {
#[cfg(windows)]
if let Some(raw_os_error) = err.raw_os_error() {
use windows::Win32::Foundation;
#[allow(clippy::single_match, reason = "in the future we can expand here")]
match Foundation::WIN32_ERROR(raw_os_error as u32) {
Foundation::ERROR_SHARING_VIOLATION => return ErrorKind::AlreadyInUse,
_ => {}
}
}
ErrorKind::Std(err.kind(), Sealed)
}
}
@ -312,7 +383,7 @@ impl StdError for IoError {}
impl Display for IoError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.kind {
ErrorKind::Std(std::io::ErrorKind::NotFound) => write!(f, "Not found"),
ErrorKind::Std(std::io::ErrorKind::NotFound, _) => write!(f, "Not found"),
ErrorKind::FileNotFound => write!(f, "File not found"),
ErrorKind::DirectoryNotFound => write!(f, "Directory not found"),
_ => write!(f, "I/O error"),
@ -323,13 +394,14 @@ impl Display for IoError {
impl Display for ErrorKind {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::Std(std::io::ErrorKind::NotFound) => write!(f, "Not found"),
ErrorKind::Std(error_kind) => {
ErrorKind::Std(std::io::ErrorKind::NotFound, _) => write!(f, "Not found"),
ErrorKind::Std(error_kind, _) => {
let msg = error_kind.to_string();
let (first, rest) = msg.split_at(1);
write!(f, "{}{}", first.to_uppercase(), rest)
}
ErrorKind::NotAFile => write!(f, "Not a file"),
ErrorKind::AlreadyInUse => write!(f, "Already in use"),
ErrorKind::FileNotFound => write!(f, "File not found"),
ErrorKind::DirectoryNotFound => write!(f, "Directory not found"),
}
@ -342,7 +414,7 @@ impl Diagnostic for IoError {
fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
let mut code = String::from("nu::shell::io::");
match self.kind {
ErrorKind::Std(error_kind) => match error_kind {
ErrorKind::Std(error_kind, _) => match error_kind {
std::io::ErrorKind::NotFound => code.push_str("not_found"),
std::io::ErrorKind::PermissionDenied => code.push_str("permission_denied"),
std::io::ErrorKind::ConnectionRefused => code.push_str("connection_refused"),
@ -366,6 +438,7 @@ impl Diagnostic for IoError {
kind => code.push_str(&kind.to_string().to_lowercase().replace(" ", "_")),
},
ErrorKind::NotAFile => code.push_str("not_a_file"),
ErrorKind::AlreadyInUse => code.push_str("already_in_use"),
ErrorKind::FileNotFound => code.push_str("file_not_found"),
ErrorKind::DirectoryNotFound => code.push_str("directory_not_found"),
}
@ -378,7 +451,10 @@ impl Diagnostic for IoError {
let path = format!("'{}'", path.display());
match self.kind {
ErrorKind::NotAFile => format!("{path} is not a file"),
ErrorKind::Std(std::io::ErrorKind::NotFound)
ErrorKind::AlreadyInUse => {
format!("{path} is already being used by another program")
}
ErrorKind::Std(std::io::ErrorKind::NotFound, _)
| ErrorKind::FileNotFound
| ErrorKind::DirectoryNotFound => format!("{path} does not exist"),
_ => format!("The error occurred at {path}"),
@ -430,16 +506,10 @@ impl From<IoError> for std::io::Error {
}
}
impl From<std::io::ErrorKind> for ErrorKind {
fn from(value: std::io::ErrorKind) -> Self {
ErrorKind::Std(value)
}
}
impl From<ErrorKind> for std::io::ErrorKind {
fn from(value: ErrorKind) -> Self {
match value {
ErrorKind::Std(error_kind) => error_kind,
ErrorKind::Std(error_kind, _) => error_kind,
_ => std::io::ErrorKind::Other,
}
}
@ -455,8 +525,8 @@ pub enum NotFound {
Directory,
}
/// Extension trait for working with [`std::io::ErrorKind`].
pub trait ErrorKindExt {
/// Extension trait for working with [`std::io::Error`].
pub trait IoErrorExt {
/// Map [`NotFound`](std::io::ErrorKind) variants into more precise variants.
///
/// The OS doesn't know when an entity was not found whether it was meant to be a file or a
@ -469,7 +539,7 @@ pub trait ErrorKindExt {
/// If the file isn't found, return [`FileNotFound`](ErrorKind::FileNotFound).
/// ```rust
/// # use nu_protocol::{
/// # shell_error::io::{ErrorKind, ErrorKindExt, IoError, NotFound},
/// # shell_error::io::{ErrorKind, IoErrorExt, IoError, NotFound},
/// # ShellError, Span,
/// # };
/// # use std::{fs, path::PathBuf};
@ -479,7 +549,7 @@ pub trait ErrorKindExt {
/// let a_file = PathBuf::from("scripts/ellie.nu");
/// let ellie = fs::read_to_string(&a_file).map_err(|err| {
/// ShellError::Io(IoError::new(
/// err.kind().not_found_as(NotFound::File),
/// err.not_found_as(NotFound::File),
/// span,
/// a_file,
/// ))
@ -498,21 +568,46 @@ pub trait ErrorKindExt {
fn not_found_as(self, kind: NotFound) -> ErrorKind;
}
impl ErrorKindExt for std::io::ErrorKind {
impl IoErrorExt for ErrorKind {
fn not_found_as(self, kind: NotFound) -> ErrorKind {
match (kind, self) {
(NotFound::File, Self::NotFound) => ErrorKind::FileNotFound,
(NotFound::Directory, Self::NotFound) => ErrorKind::DirectoryNotFound,
_ => ErrorKind::Std(self),
}
}
}
impl ErrorKindExt for ErrorKind {
fn not_found_as(self, kind: NotFound) -> ErrorKind {
match self {
Self::Std(std_kind) => std_kind.not_found_as(kind),
(NotFound::File, Self::Std(std::io::ErrorKind::NotFound, _)) => ErrorKind::FileNotFound,
(NotFound::Directory, Self::Std(std::io::ErrorKind::NotFound, _)) => {
ErrorKind::DirectoryNotFound
}
_ => self,
}
}
}
impl IoErrorExt for std::io::Error {
fn not_found_as(self, kind: NotFound) -> ErrorKind {
ErrorKind::from(self).not_found_as(kind)
}
}
impl IoErrorExt for &std::io::Error {
fn not_found_as(self, kind: NotFound) -> ErrorKind {
ErrorKind::from(self).not_found_as(kind)
}
}
#[cfg(test)]
mod assert_not_impl {
use super::*;
/// Assertion that `ErrorKind` does not implement `From<std::io::ErrorKind>`.
///
/// This implementation exists only in tests to make sure that no crate,
/// including ours, accidentally adds a `From<std::io::ErrorKind>` impl for `ErrorKind`.
/// If someone tries, it will fail due to conflicting implementations.
///
/// We want to force usage of [`IoError::new`] with a full [`std::io::Error`] instead of
/// allowing conversion from just an [`std::io::ErrorKind`].
/// That way, we can properly inspect and classify uncategorized I/O errors.
impl From<std::io::ErrorKind> for ErrorKind {
fn from(_: std::io::ErrorKind) -> Self {
unimplemented!("ErrorKind should not implement From<std::io::ErrorKind>")
}
}
}

View File

@ -246,7 +246,7 @@ impl ByteStream {
if let Some(mut reader) = self.reader() {
// Copy the number of skipped bytes into the sink before proceeding
io::copy(&mut (&mut reader).take(n), &mut io::sink())
.map_err(|err| IoError::new(err.kind(), span, None))?;
.map_err(|err| IoError::new(err, span, None))?;
Ok(
ByteStream::read(reader, span, Signals::empty(), ByteStreamType::Binary)
.with_known_size(known_size),
@ -367,7 +367,7 @@ impl ByteStream {
/// binary.
#[cfg(feature = "os")]
pub fn stdin(span: Span) -> Result<Self, ShellError> {
let stdin = os_pipe::dup_stdin().map_err(|err| IoError::new(err.kind(), span, None))?;
let stdin = os_pipe::dup_stdin().map_err(|err| IoError::new(err, span, None))?;
let source = ByteStreamSource::File(convert_file(stdin));
Ok(Self::new(
source,
@ -853,7 +853,7 @@ impl Iterator for Lines {
trim_end_newline(&mut string);
Some(Ok(string))
}
Err(e) => Some(Err(IoError::new(e.kind(), self.span, None).into())),
Err(err) => Some(Err(IoError::new(err, self.span, None).into())),
}
}
}
@ -1052,7 +1052,7 @@ impl Iterator for SplitRead {
self.internal.next().map(|r| {
r.map_err(|err| {
ShellError::Io(IoError::new_internal(
err.kind(),
err,
"Could not get next value for SplitRead",
crate::location!(),
))
@ -1094,7 +1094,7 @@ impl Chunks {
fn next_string(&mut self) -> Result<Option<String>, (Vec<u8>, ShellError)> {
let from_io_error = |err: std::io::Error| match ShellErrorBridge::try_from(err) {
Ok(err) => err.0,
Err(err) => IoError::new(err.kind(), self.span, None).into(),
Err(err) => IoError::new(err, self.span, None).into(),
};
// Get some data from the reader
@ -1177,11 +1177,7 @@ impl Iterator for Chunks {
Ok(buf) => buf,
Err(err) => {
self.error = true;
return Some(Err(ShellError::Io(IoError::new(
err.kind(),
self.span,
None,
))));
return Some(Err(ShellError::Io(IoError::new(err, self.span, None))));
}
};
if !buf.is_empty() {

View File

@ -222,14 +222,14 @@ impl PipelineData {
let bytes = value_to_bytes(value)?;
dest.write_all(&bytes).map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not write PipelineData to dest",
crate::location!(),
)
})?;
dest.flush().map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not flush PipelineData to dest",
crate::location!(),
)
@ -241,14 +241,14 @@ impl PipelineData {
let bytes = value_to_bytes(value)?;
dest.write_all(&bytes).map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not write PipelineData to dest",
crate::location!(),
)
})?;
dest.write_all(b"\n").map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not write linebreak after PipelineData to dest",
crate::location!(),
)
@ -256,7 +256,7 @@ impl PipelineData {
}
dest.flush().map_err(|err| {
IoError::new_internal(
err.kind(),
err,
"Could not flush PipelineData to dest",
crate::location!(),
)
@ -775,11 +775,9 @@ where
let io_error_map = |err: std::io::Error, location: Location| {
let context = format!("Writing to {} failed", destination_name);
match span {
None => IoError::new_internal(err.kind(), context, location),
Some(span) if span == Span::unknown() => {
IoError::new_internal(err.kind(), context, location)
}
Some(span) => IoError::new_with_additional_context(err.kind(), span, None, context),
None => IoError::new_internal(err, context, location),
Some(span) if span == Span::unknown() => IoError::new_internal(err, context, location),
Some(span) => IoError::new_with_additional_context(err, span, None, context),
}
};

View File

@ -81,7 +81,7 @@ impl ExitStatusFuture {
}
Ok(Ok(status)) => Ok(status),
Ok(Err(err)) => Err(ShellError::Io(IoError::new_with_additional_context(
err.kind(),
err,
span,
None,
"failed to get exit code",
@ -276,7 +276,7 @@ impl ChildProcess {
})
.map_err(|err| {
IoError::new_with_additional_context(
err.kind(),
err,
span,
None,
"Could now spawn exit status waiter",
@ -325,7 +325,7 @@ impl ChildProcess {
}
let bytes = if let Some(stdout) = self.stdout {
collect_bytes(stdout).map_err(|err| IoError::new(err.kind(), self.span, None))?
collect_bytes(stdout).map_err(|err| IoError::new(err, self.span, None))?
} else {
Vec::new()
};

View File

@ -10,7 +10,8 @@ use nu_utils::perf;
use nu_plugin::{EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, Example, LabeledError, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, Value, shell_error::io::IoError,
SyntaxShape, Type, Value,
shell_error::{self, io::IoError},
};
use std::{fs::File, io::BufReader, num::NonZeroUsize, path::PathBuf, sync::Arc};
@ -193,7 +194,7 @@ fn command(
)),
},
None => Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::Other),
spanned_file.span,
PathBuf::from(spanned_file.item),
"File without extension",

View File

@ -16,7 +16,8 @@ use log::debug;
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, Example, LabeledError, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, shell_error::io::IoError,
SyntaxShape, Type,
shell_error::{self, io::IoError},
};
use polars::error::PolarsError;
@ -212,7 +213,7 @@ fn command(
)),
},
None => Err(ShellError::Io(IoError::new_with_additional_context(
std::io::ErrorKind::NotFound,
shell_error::io::ErrorKind::FileNotFound,
resource.span,
Some(PathBuf::from(resource.path)),
"File without extension",

View File

@ -5,7 +5,7 @@ use nu_protocol::{
DeclId, ShellError, Span, Value, VarId,
engine::{EngineState, Stack, StateWorkingSet},
report_shell_error,
shell_error::io::{ErrorKindExt, IoError, NotFound},
shell_error::io::{IoError, IoErrorExt, NotFound},
};
use reedline::Completer;
use serde_json::{Value as JsonValue, json};
@ -58,7 +58,7 @@ fn read_in_file<'a>(
let file = std::fs::read(file_path)
.map_err(|err| {
ShellError::Io(IoError::new_with_additional_context(
err.kind().not_found_as(NotFound::File),
err.not_found_as(NotFound::File),
Span::unknown(),
PathBuf::from(file_path),
"Could not read file",

View File

@ -417,7 +417,7 @@ fn main() -> Result<()> {
let filename = canonicalize_with(&plugin_filename.item, &init_cwd)
.map_err(|err| {
nu_protocol::shell_error::io::IoError::new(
err.kind(),
err,
plugin_filename.span,
PathBuf::from(&plugin_filename.item),
)