mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
Better error handling using do
(#5890)
* adds `capture-errors` flag for `do` * adds `get-type` core command to get type * fmt * add tests in example * fmt * fix tests * manually revert previous changes related to `get-type` * adds method to check for error name using `into string` * fix clippy
This commit is contained in:
parent
6ee13126f7
commit
a0db4ce747
8 changed files with 62 additions and 4 deletions
|
@ -2,8 +2,8 @@ use nu_engine::CallExt;
|
|||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Value,
|
||||
into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||
Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
|
||||
|
@ -247,6 +247,15 @@ pub fn action(
|
|||
val: input.into_string(", ", config),
|
||||
span,
|
||||
},
|
||||
Value::Error { error } => Value::String {
|
||||
val: {
|
||||
match into_code(error) {
|
||||
Some(code) => code,
|
||||
None => "".to_string(),
|
||||
}
|
||||
},
|
||||
span,
|
||||
},
|
||||
Value::Nothing { .. } => Value::String {
|
||||
val: "".to_string(),
|
||||
span,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Value};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Do;
|
||||
|
@ -23,6 +25,11 @@ impl Command for Do {
|
|||
"ignore errors as the block runs",
|
||||
Some('i'),
|
||||
)
|
||||
.switch(
|
||||
"capture-errors",
|
||||
"capture errors as the block runs and return it",
|
||||
Some('c'),
|
||||
)
|
||||
.rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
@ -37,6 +44,7 @@ impl Command for Do {
|
|||
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
|
||||
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
||||
let ignore_errors = call.has_flag("ignore-errors");
|
||||
let capture_errors = call.has_flag("capture-errors");
|
||||
|
||||
let mut stack = stack.captures_to_stack(&block.captures);
|
||||
let block = engine_state.get_block(block.block_id);
|
||||
|
@ -85,7 +93,7 @@ impl Command for Do {
|
|||
block,
|
||||
input,
|
||||
call.redirect_stdout,
|
||||
ignore_errors,
|
||||
ignore_errors || capture_errors,
|
||||
);
|
||||
|
||||
if ignore_errors {
|
||||
|
@ -93,6 +101,11 @@ impl Command for Do {
|
|||
Ok(x) => Ok(x),
|
||||
Err(_) => Ok(PipelineData::new(call.head)),
|
||||
}
|
||||
} else if capture_errors {
|
||||
match result {
|
||||
Ok(x) => Ok(x),
|
||||
Err(err) => Ok((Value::Error { error: err }).into_pipeline_data()),
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
|
|
13
crates/nu-command/tests/commands/do_.rs
Normal file
13
crates/nu-command/tests/commands/do_.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use nu_test_support::{nu, pipeline};
|
||||
|
||||
#[test]
|
||||
fn capture_errors_works() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
do -c {$env.use} | describe
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "error");
|
||||
}
|
|
@ -9,6 +9,7 @@ mod cp;
|
|||
mod date;
|
||||
mod def;
|
||||
mod default;
|
||||
mod do_;
|
||||
mod drop;
|
||||
mod each;
|
||||
mod echo;
|
||||
|
|
|
@ -171,3 +171,15 @@ fn from_nothing() {
|
|||
|
||||
assert_eq!(actual.out, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_error() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
do -c {$env.use} | into string
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "nu::shell::name_not_found");
|
||||
}
|
||||
|
|
|
@ -2670,6 +2670,7 @@ pub fn parse_shape_name(
|
|||
b"record" => SyntaxShape::Record,
|
||||
b"list" => SyntaxShape::List(Box::new(SyntaxShape::Any)),
|
||||
b"table" => SyntaxShape::Table,
|
||||
b"error" => SyntaxShape::Error,
|
||||
_ => {
|
||||
if bytes.contains(&b'@') {
|
||||
let str = String::from_utf8_lossy(bytes);
|
||||
|
|
|
@ -724,6 +724,10 @@ impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn into_code(err: &ShellError) -> Option<String> {
|
||||
err.code().map(|code| code.to_string())
|
||||
}
|
||||
|
||||
pub fn did_you_mean(possibilities: &[String], tried: &str) -> Option<String> {
|
||||
let mut possible_matches: Vec<_> = possibilities
|
||||
.iter()
|
||||
|
|
|
@ -92,6 +92,9 @@ pub enum SyntaxShape {
|
|||
/// A record value
|
||||
Record,
|
||||
|
||||
/// An error value
|
||||
Error,
|
||||
|
||||
/// A custom shape with custom completion logic
|
||||
Custom(Box<SyntaxShape>, DeclId),
|
||||
}
|
||||
|
@ -112,6 +115,7 @@ impl SyntaxShape {
|
|||
SyntaxShape::Filesize => Type::Filesize,
|
||||
SyntaxShape::FullCellPath => Type::Any,
|
||||
SyntaxShape::GlobPattern => Type::String,
|
||||
SyntaxShape::Error => Type::Error,
|
||||
SyntaxShape::ImportPattern => Type::Any,
|
||||
SyntaxShape::Int => Type::Int,
|
||||
SyntaxShape::List(x) => {
|
||||
|
@ -168,6 +172,7 @@ impl Display for SyntaxShape {
|
|||
SyntaxShape::Signature => write!(f, "signature"),
|
||||
SyntaxShape::Expression => write!(f, "expression"),
|
||||
SyntaxShape::Boolean => write!(f, "bool"),
|
||||
SyntaxShape::Error => write!(f, "error"),
|
||||
SyntaxShape::Custom(x, _) => write!(f, "custom<{}>", x),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue