forked from extern/nushell
FEATURE: write better errors for error make
and complete the doc (#8511)
# Description this PR - refactors `ErrorMake::run` to avoid duplicate branches depending on the value of `--unspanned` - completes the examples 1. show a really simple `error make` call, without any command definition 2. show a complete error format with all possible fields 3. the command definition but with indentation and slightly better description - adds results to the first two examples - gives meaningful error messages for all known "bad" error formats, using the span of the error format or the span of `$format.label` to better explain why the format is bad # User-Facing Changes users have now the following help ```bash Examples: Create a simple custom error > error make {msg: "my custom error message"} Create a more complex custom error > error make { msg: "my custom error message" label: { text: "my custom label text" # not mandatory unless $.label exists start: 123 # not mandatory unless $.label.end is set end: 456 # not mandatory unless $.label.start is set } } Create a custom error for a custom command that shows the span of the argument > def foo [x] { let span = (metadata $x).span; error make { msg: "this is fishy" label: { text: "fish right here" start: $span.start end: $span.end } } } ``` and the following error messages when the error format is bad https://asciinema.org/a/568107 🥳 # Tests + Formatting - 🟢 `cargo fmt --all` - 🟢 `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` - 🔴 `cargo test --workspace` => the tests do not pass but they do not pass on latest `main` either => i should `cargo clean`, but that's an expensive operation on my machine... # After Submitting the documentation would have to be regenerated over on the website
This commit is contained in:
parent
a193b85123
commit
626410b2aa
@ -44,43 +44,66 @@ impl Command for ErrorMake {
|
|||||||
let arg: Value = call.req(engine_state, stack, 0)?;
|
let arg: Value = call.req(engine_state, stack, 0)?;
|
||||||
let unspanned = call.has_flag("unspanned");
|
let unspanned = call.has_flag("unspanned");
|
||||||
|
|
||||||
if unspanned {
|
let throw_error = if unspanned { None } else { Some(span) };
|
||||||
Err(make_error(&arg, None).unwrap_or_else(|| {
|
Err(make_error(&arg, throw_error).unwrap_or_else(|| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Creating error value not supported.".into(),
|
"Creating error value not supported.".into(),
|
||||||
"unsupported error format".into(),
|
"unsupported error format".into(),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
} else {
|
|
||||||
Err(make_error(&arg, Some(span)).unwrap_or_else(|| {
|
|
||||||
ShellError::GenericError(
|
|
||||||
"Creating error value not supported.".into(),
|
|
||||||
"unsupported error format".into(),
|
|
||||||
Some(span),
|
|
||||||
None,
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Create a custom error for a custom command",
|
description: "Create a simple custom error",
|
||||||
example: r#"def foo [x] {
|
example: r#"error make {msg: "my custom error message"}"#,
|
||||||
let span = (metadata $x).span;
|
result: Some(Value::Error {
|
||||||
error make {msg: "this is fishy", label: {text: "fish right here", start: $span.start, end: $span.end } }
|
error: Box::new(ShellError::GenericError(
|
||||||
}"#,
|
"my custom error message".to_string(),
|
||||||
result: None,
|
"".to_string(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Create a simple custom error for a custom command",
|
description: "Create a more complex custom error",
|
||||||
|
example: r#"error make {
|
||||||
|
msg: "my custom error message"
|
||||||
|
label: {
|
||||||
|
text: "my custom label text" # not mandatory unless $.label exists
|
||||||
|
start: 123 # not mandatory unless $.label.end is set
|
||||||
|
end: 456 # not mandatory unless $.label.start is set
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
result: Some(Value::Error {
|
||||||
|
error: Box::new(ShellError::GenericError(
|
||||||
|
"my custom error message".to_string(),
|
||||||
|
"my custom label text".to_string(),
|
||||||
|
Some(Span::new(123, 456)),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description:
|
||||||
|
"Create a custom error for a custom command that shows the span of the argument",
|
||||||
example: r#"def foo [x] {
|
example: r#"def foo [x] {
|
||||||
error make {msg: "this is fishy"}
|
let span = (metadata $x).span;
|
||||||
|
error make {
|
||||||
|
msg: "this is fishy"
|
||||||
|
label: {
|
||||||
|
text: "fish right here"
|
||||||
|
start: $span.start
|
||||||
|
end: $span.end
|
||||||
|
}
|
||||||
|
}
|
||||||
}"#,
|
}"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
@ -89,7 +112,7 @@ impl Command for ErrorMake {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
||||||
if let Value::Record { .. } = &value {
|
if let Value::Record { span, .. } = &value {
|
||||||
let msg = value.get_data_by_key("msg");
|
let msg = value.get_data_by_key("msg");
|
||||||
let label = value.get_data_by_key("label");
|
let label = value.get_data_by_key("label");
|
||||||
|
|
||||||
@ -99,6 +122,11 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|||||||
let label_end = label.get_data_by_key("end");
|
let label_end = label.get_data_by_key("end");
|
||||||
let label_text = label.get_data_by_key("text");
|
let label_text = label.get_data_by_key("text");
|
||||||
|
|
||||||
|
let label_span = match label.span() {
|
||||||
|
Ok(lspan) => Some(lspan),
|
||||||
|
Err(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
match (label_start, label_end, label_text) {
|
match (label_start, label_end, label_text) {
|
||||||
(
|
(
|
||||||
Some(Value::Int { val: start, .. }),
|
Some(Value::Int { val: start, .. }),
|
||||||
@ -126,6 +154,27 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)),
|
)),
|
||||||
|
(_, _, None) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.label.text`".into(),
|
||||||
|
label_span,
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
(Some(Value::Int { .. }), None, _) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.label.end`".into(),
|
||||||
|
label_span,
|
||||||
|
Some("required because `$.label.start` is set".to_string()),
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
|
(None, Some(Value::Int { .. }), _) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.label.start`".into(),
|
||||||
|
label_span,
|
||||||
|
Some("required because `$.label.end` is set".to_string()),
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,6 +185,13 @@ fn make_error(value: &Value, throw_span: Option<Span>) -> Option<ShellError> {
|
|||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)),
|
)),
|
||||||
|
(None, _) => Some(ShellError::GenericError(
|
||||||
|
"Unable to parse error format.".into(),
|
||||||
|
"missing required member `$.msg`".into(),
|
||||||
|
Some(*span),
|
||||||
|
None,
|
||||||
|
Vec::new(),
|
||||||
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user