Better generic errors for plugins (and perhaps scripts) (#12236)

# Description
This makes `LabeledError` much more capable of representing close to
everything a `miette::Diagnostic` can, including `ShellError`, and
allows plugins to generate multiple error spans, codes, help, etc.

`LabeledError` is now embeddable within `ShellError` as a transparent
variant.

This could also be used to improve `error make` and `try/catch` to
reflect `LabeledError` exactly in the future.

Also cleaned up some errors in existing plugins.

# User-Facing Changes
Breaking change for plugins. Nicer errors for users.
This commit is contained in:
Devyn Cairns
2024-03-21 04:27:21 -07:00
committed by GitHub
parent 8237d15683
commit efe25e3f58
42 changed files with 453 additions and 307 deletions

View File

@ -1,5 +1,7 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, RawStream, Type, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, LabeledError, PipelineData, PluginExample, PluginSignature, RawStream, Type, Value,
};
use crate::Example;

View File

@ -1,5 +1,5 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{Category, PluginSignature, Type, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, PluginSignature, Type, Value};
use crate::Example;
@ -27,12 +27,10 @@ impl SimplePluginCommand for Config {
let config = engine.get_plugin_config()?;
match config {
Some(config) => Ok(config.clone()),
None => Err(LabeledError {
label: "No config sent".into(),
msg: "Configuration for this plugin was not found in `$env.config.plugins.example`"
.into(),
span: Some(call.head),
}),
None => Err(LabeledError::new("No config sent").with_label(
"configuration for this plugin was not found in `$env.config.plugins.example`",
call.head,
)),
}
}
}

View File

@ -1,5 +1,5 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{Category, PluginSignature, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, PluginSignature, Value};
use crate::Example;

View File

@ -1,5 +1,5 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{Category, PluginSignature, SyntaxShape, Type, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Type, Value};
use crate::Example;
@ -42,11 +42,8 @@ impl SimplePluginCommand for Env {
// Get working directory
Ok(Value::string(engine.get_current_dir()?, call.head))
}
Some(value) => Err(LabeledError {
label: "Invalid arguments".into(),
msg: "--cwd can't be used with --set".into(),
span: Some(value.span()),
}),
Some(value) => Err(LabeledError::new("Invalid arguments")
.with_label("--cwd can't be used with --set", value.span())),
}
} else if let Some(value) = call.get_flag_value("set") {
// Set single env var

View File

@ -1,5 +1,7 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, LabeledError, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type,
};
use crate::Example;

View File

@ -1,7 +1,7 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, IntoInterruptiblePipelineData, PipelineData, PluginExample, PluginSignature,
SyntaxShape, Type, Value,
Category, IntoInterruptiblePipelineData, LabeledError, PipelineData, PluginExample,
PluginSignature, SyntaxShape, Type, Value,
};
use crate::Example;

View File

@ -1,5 +1,5 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{Category, PluginSignature, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, PluginSignature, Value};
use crate::Example;
@ -32,10 +32,7 @@ particularly useful.
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
Err(LabeledError {
label: "No subcommand provided".into(),
msg: "add --help to see a list of subcommands".into(),
span: Some(call.head),
})
Err(LabeledError::new("No subcommand provided")
.with_label("add --help to see a list of subcommands", call.head))
}
}

View File

@ -1,5 +1,5 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{Category, PluginExample, PluginSignature, SyntaxShape, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, PluginExample, PluginSignature, SyntaxShape, Value};
use crate::Example;

View File

@ -1,6 +1,7 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, ListStream, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type, Value,
Category, LabeledError, ListStream, PipelineData, PluginExample, PluginSignature, SyntaxShape,
Type, Value,
};
use crate::Example;

View File

@ -1,5 +1,7 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, Span, Type, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
use nu_protocol::{
Category, LabeledError, PipelineData, PluginExample, PluginSignature, Span, Type, Value,
};
use crate::Example;
@ -33,18 +35,15 @@ impl PluginCommand for Sum {
input: PipelineData,
) -> Result<PipelineData, LabeledError> {
let mut acc = IntOrFloat::Int(0);
let span = input.span();
for value in input {
if let Ok(n) = value.as_i64() {
acc.add_i64(n);
} else if let Ok(n) = value.as_f64() {
acc.add_f64(n);
} else {
return Err(LabeledError {
label: "Stream only accepts ints and floats".into(),
msg: format!("found {}", value.get_type()),
span,
});
return Err(LabeledError::new("Sum only accepts ints and floats")
.with_label(format!("found {} in input", value.get_type()), value.span())
.with_label("can't be used here", call.head));
}
}
Ok(PipelineData::Value(acc.to_value(call.head), None))

View File

@ -1,5 +1,5 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{Category, PluginSignature, SyntaxShape, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Value};
use crate::Example;
@ -31,10 +31,7 @@ impl SimplePluginCommand for Three {
) -> Result<Value, LabeledError> {
plugin.print_values(3, call, input)?;
Err(LabeledError {
label: "ERROR from plugin".into(),
msg: "error message pointing to call head span".into(),
span: Some(call.head),
})
Err(LabeledError::new("ERROR from plugin")
.with_label("error message pointing to call head span", call.head))
}
}

View File

@ -1,5 +1,5 @@
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
use nu_protocol::{record, Category, PluginSignature, SyntaxShape, Value};
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{record, Category, LabeledError, PluginSignature, SyntaxShape, Value};
use crate::Example;

View File

@ -1,5 +1,5 @@
use nu_plugin::{EvaluatedCall, LabeledError};
use nu_protocol::Value;
use nu_plugin::EvaluatedCall;
use nu_protocol::{LabeledError, Value};
pub struct Example;