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:
pwygab 2022-06-30 09:01:34 +08:00 committed by GitHub
parent 6ee13126f7
commit a0db4ce747
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 4 deletions

View file

@ -2,8 +2,8 @@ use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack},
Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
SyntaxShape, Value, Span, SyntaxShape, Value,
}; };
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml) // 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), val: input.into_string(", ", config),
span, span,
}, },
Value::Error { error } => Value::String {
val: {
match into_code(error) {
Some(code) => code,
None => "".to_string(),
}
},
span,
},
Value::Nothing { .. } => Value::String { Value::Nothing { .. } => Value::String {
val: "".to_string(), val: "".to_string(),
span, span,

View file

@ -1,7 +1,9 @@
use nu_engine::{eval_block, CallExt}; use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack}; 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)] #[derive(Clone)]
pub struct Do; pub struct Do;
@ -23,6 +25,11 @@ impl Command for Do {
"ignore errors as the block runs", "ignore errors as the block runs",
Some('i'), 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") .rest("rest", SyntaxShape::Any, "the parameter(s) for the block")
.category(Category::Core) .category(Category::Core)
} }
@ -37,6 +44,7 @@ impl Command for Do {
let block: CaptureBlock = call.req(engine_state, stack, 0)?; let block: CaptureBlock = call.req(engine_state, stack, 0)?;
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?; let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
let ignore_errors = call.has_flag("ignore-errors"); 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 mut stack = stack.captures_to_stack(&block.captures);
let block = engine_state.get_block(block.block_id); let block = engine_state.get_block(block.block_id);
@ -85,7 +93,7 @@ impl Command for Do {
block, block,
input, input,
call.redirect_stdout, call.redirect_stdout,
ignore_errors, ignore_errors || capture_errors,
); );
if ignore_errors { if ignore_errors {
@ -93,6 +101,11 @@ impl Command for Do {
Ok(x) => Ok(x), Ok(x) => Ok(x),
Err(_) => Ok(PipelineData::new(call.head)), 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 { } else {
result result
} }

View 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");
}

View file

@ -9,6 +9,7 @@ mod cp;
mod date; mod date;
mod def; mod def;
mod default; mod default;
mod do_;
mod drop; mod drop;
mod each; mod each;
mod echo; mod echo;

View file

@ -171,3 +171,15 @@ fn from_nothing() {
assert_eq!(actual.out, ""); 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");
}

View file

@ -2670,6 +2670,7 @@ pub fn parse_shape_name(
b"record" => SyntaxShape::Record, b"record" => SyntaxShape::Record,
b"list" => SyntaxShape::List(Box::new(SyntaxShape::Any)), b"list" => SyntaxShape::List(Box::new(SyntaxShape::Any)),
b"table" => SyntaxShape::Table, b"table" => SyntaxShape::Table,
b"error" => SyntaxShape::Error,
_ => { _ => {
if bytes.contains(&b'@') { if bytes.contains(&b'@') {
let str = String::from_utf8_lossy(bytes); let str = String::from_utf8_lossy(bytes);

View file

@ -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> { pub fn did_you_mean(possibilities: &[String], tried: &str) -> Option<String> {
let mut possible_matches: Vec<_> = possibilities let mut possible_matches: Vec<_> = possibilities
.iter() .iter()

View file

@ -92,6 +92,9 @@ pub enum SyntaxShape {
/// A record value /// A record value
Record, Record,
/// An error value
Error,
/// A custom shape with custom completion logic /// A custom shape with custom completion logic
Custom(Box<SyntaxShape>, DeclId), Custom(Box<SyntaxShape>, DeclId),
} }
@ -112,6 +115,7 @@ impl SyntaxShape {
SyntaxShape::Filesize => Type::Filesize, SyntaxShape::Filesize => Type::Filesize,
SyntaxShape::FullCellPath => Type::Any, SyntaxShape::FullCellPath => Type::Any,
SyntaxShape::GlobPattern => Type::String, SyntaxShape::GlobPattern => Type::String,
SyntaxShape::Error => Type::Error,
SyntaxShape::ImportPattern => Type::Any, SyntaxShape::ImportPattern => Type::Any,
SyntaxShape::Int => Type::Int, SyntaxShape::Int => Type::Int,
SyntaxShape::List(x) => { SyntaxShape::List(x) => {
@ -168,6 +172,7 @@ impl Display for SyntaxShape {
SyntaxShape::Signature => write!(f, "signature"), SyntaxShape::Signature => write!(f, "signature"),
SyntaxShape::Expression => write!(f, "expression"), SyntaxShape::Expression => write!(f, "expression"),
SyntaxShape::Boolean => write!(f, "bool"), SyntaxShape::Boolean => write!(f, "bool"),
SyntaxShape::Error => write!(f, "error"),
SyntaxShape::Custom(x, _) => write!(f, "custom<{}>", x), SyntaxShape::Custom(x, _) => write!(f, "custom<{}>", x),
} }
} }