mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 06:55:36 +02:00
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:
@ -3,8 +3,8 @@ use crate::query_web::QueryWeb;
|
||||
use crate::query_xml::QueryXml;
|
||||
|
||||
use nu_engine::documentation::get_flags_section;
|
||||
use nu_plugin::{EvaluatedCall, LabeledError, Plugin, PluginCommand, SimplePluginCommand};
|
||||
use nu_protocol::{Category, PluginSignature, Value};
|
||||
use nu_plugin::{EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand};
|
||||
use nu_protocol::{Category, LabeledError, PluginSignature, Value};
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -1,6 +1,8 @@
|
||||
use gjson::Value as gjValue;
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_protocol::{Category, PluginSignature, Record, Span, Spanned, SyntaxShape, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
Category, LabeledError, PluginSignature, Record, Span, Spanned, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
use crate::Query;
|
||||
|
||||
@ -39,22 +41,15 @@ pub fn execute_json_query(
|
||||
let input_string = match input.coerce_str() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return Err(LabeledError {
|
||||
span: Some(call.head),
|
||||
msg: e.to_string(),
|
||||
label: "problem with input data".to_string(),
|
||||
})
|
||||
return Err(LabeledError::new("Problem with input data").with_inner(e));
|
||||
}
|
||||
};
|
||||
|
||||
let query_string = match &query {
|
||||
Some(v) => &v.item,
|
||||
None => {
|
||||
return Err(LabeledError {
|
||||
msg: "problem with input data".to_string(),
|
||||
label: "problem with input data".to_string(),
|
||||
span: Some(call.head),
|
||||
})
|
||||
return Err(LabeledError::new("Problem with input data")
|
||||
.with_label("query string missing", call.head));
|
||||
}
|
||||
};
|
||||
|
||||
@ -62,11 +57,9 @@ pub fn execute_json_query(
|
||||
let is_valid_json = gjson::valid(&input_string);
|
||||
|
||||
if !is_valid_json {
|
||||
return Err(LabeledError {
|
||||
msg: "invalid json".to_string(),
|
||||
label: "invalid json".to_string(),
|
||||
span: Some(call.head),
|
||||
});
|
||||
return Err(
|
||||
LabeledError::new("Invalid JSON").with_label("this is not valid JSON", call.head)
|
||||
);
|
||||
}
|
||||
|
||||
let val: gjValue = gjson::get(&input_string, query_string);
|
||||
|
@ -1,6 +1,9 @@
|
||||
use crate::{web_tables::WebTable, Query};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_protocol::{Category, PluginExample, PluginSignature, Record, Span, SyntaxShape, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
Category, LabeledError, PluginExample, PluginSignature, Record, Span, Spanned, SyntaxShape,
|
||||
Value,
|
||||
};
|
||||
use scraper::{Html, Selector as ScraperSelector};
|
||||
|
||||
pub struct QueryWeb;
|
||||
@ -99,10 +102,7 @@ impl Default for Selector {
|
||||
|
||||
pub fn parse_selector_params(call: &EvaluatedCall, input: &Value) -> Result<Value, LabeledError> {
|
||||
let head = call.head;
|
||||
let query: String = match call.get_flag("query")? {
|
||||
Some(q2) => q2,
|
||||
None => "".to_string(),
|
||||
};
|
||||
let query: Option<Spanned<String>> = call.get_flag("query")?;
|
||||
let as_html = call.has_flag("as-html")?;
|
||||
let attribute = call.get_flag("attribute")?.unwrap_or_default();
|
||||
let as_table: Value = call
|
||||
@ -111,16 +111,20 @@ pub fn parse_selector_params(call: &EvaluatedCall, input: &Value) -> Result<Valu
|
||||
|
||||
let inspect = call.has_flag("inspect")?;
|
||||
|
||||
if !&query.is_empty() && ScraperSelector::parse(&query).is_err() {
|
||||
return Err(LabeledError {
|
||||
msg: "Cannot parse this query as a valid css selector".to_string(),
|
||||
label: "Parse error".to_string(),
|
||||
span: Some(head),
|
||||
});
|
||||
if let Some(query) = &query {
|
||||
if let Err(err) = ScraperSelector::parse(&query.item) {
|
||||
return Err(LabeledError::new("CSS query parse error")
|
||||
.with_label(err.to_string(), query.span)
|
||||
.with_help("cannot parse this query as a valid CSS selector"));
|
||||
}
|
||||
} else {
|
||||
return Err(
|
||||
LabeledError::new("Missing query argument").with_label("add --query here", call.head)
|
||||
);
|
||||
}
|
||||
|
||||
let selector = Selector {
|
||||
query,
|
||||
query: query.map(|q| q.item).unwrap_or_default(),
|
||||
as_html,
|
||||
attribute,
|
||||
as_table,
|
||||
@ -130,11 +134,8 @@ pub fn parse_selector_params(call: &EvaluatedCall, input: &Value) -> Result<Valu
|
||||
let span = input.span();
|
||||
match input {
|
||||
Value::String { val, .. } => Ok(begin_selector_query(val.to_string(), selector, span)),
|
||||
_ => Err(LabeledError {
|
||||
label: "requires text input".to_string(),
|
||||
msg: "Expected text from pipeline".to_string(),
|
||||
span: Some(span),
|
||||
}),
|
||||
_ => Err(LabeledError::new("Requires text input")
|
||||
.with_label("expected text from pipeline", span)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_protocol::{record, Category, PluginSignature, Record, Span, Spanned, SyntaxShape, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
record, Category, LabeledError, PluginSignature, Record, Span, Spanned, SyntaxShape, Value,
|
||||
};
|
||||
use sxd_document::parser;
|
||||
use sxd_xpath::{Context, Factory};
|
||||
|
||||
@ -38,11 +40,9 @@ pub fn execute_xpath_query(
|
||||
let (query_string, span) = match &query {
|
||||
Some(v) => (&v.item, v.span),
|
||||
None => {
|
||||
return Err(LabeledError {
|
||||
msg: "problem with input data".to_string(),
|
||||
label: "problem with input data".to_string(),
|
||||
span: Some(call.head),
|
||||
})
|
||||
return Err(
|
||||
LabeledError::new("problem with input data").with_label("query missing", call.head)
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@ -50,12 +50,10 @@ pub fn execute_xpath_query(
|
||||
let input_string = input.coerce_str()?;
|
||||
let package = parser::parse(&input_string);
|
||||
|
||||
if package.is_err() {
|
||||
return Err(LabeledError {
|
||||
label: "invalid xml document".to_string(),
|
||||
msg: "invalid xml document".to_string(),
|
||||
span: Some(call.head),
|
||||
});
|
||||
if let Err(err) = package {
|
||||
return Err(
|
||||
LabeledError::new("Invalid XML document").with_label(err.to_string(), input.span())
|
||||
);
|
||||
}
|
||||
|
||||
let package = package.expect("invalid xml document");
|
||||
@ -107,29 +105,20 @@ pub fn execute_xpath_query(
|
||||
|
||||
Ok(Value::list(records, call.head))
|
||||
}
|
||||
Err(_) => Err(LabeledError {
|
||||
label: "xpath query error".to_string(),
|
||||
msg: "xpath query error".to_string(),
|
||||
span: Some(call.head),
|
||||
}),
|
||||
Err(err) => {
|
||||
Err(LabeledError::new("xpath query error").with_label(err.to_string(), call.head))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_xpath(xpath_str: &str, span: Span) -> Result<sxd_xpath::XPath, LabeledError> {
|
||||
let factory = Factory::new();
|
||||
|
||||
if let Ok(xpath) = factory.build(xpath_str) {
|
||||
xpath.ok_or_else(|| LabeledError {
|
||||
label: "invalid xpath query".to_string(),
|
||||
msg: "invalid xpath query".to_string(),
|
||||
span: Some(span),
|
||||
})
|
||||
} else {
|
||||
Err(LabeledError {
|
||||
label: "expected valid xpath query".to_string(),
|
||||
msg: "expected valid xpath query".to_string(),
|
||||
span: Some(span),
|
||||
})
|
||||
match factory.build(xpath_str) {
|
||||
Ok(xpath) => xpath.ok_or_else(|| {
|
||||
LabeledError::new("invalid xpath query").with_label("the query must not be empty", span)
|
||||
}),
|
||||
Err(err) => Err(LabeledError::new("invalid xpath query").with_label(err.to_string(), span)),
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user