mirror of
https://github.com/nushell/nushell.git
synced 2024-11-07 17:14:23 +01: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:
parent
8237d15683
commit
efe25e3f58
@ -2,7 +2,8 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
Category, Example, LabeledError, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -258,13 +259,10 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
||||
}
|
||||
|
||||
// correct return: everything present
|
||||
ShellError::GenericError {
|
||||
error: msg,
|
||||
msg: text,
|
||||
span: Some(Span::new(span_start as usize, span_end as usize)),
|
||||
help,
|
||||
inner: vec![],
|
||||
}
|
||||
let mut error =
|
||||
LabeledError::new(msg).with_label(text, Span::new(span_start as usize, span_end as usize));
|
||||
error.help = help;
|
||||
error.into()
|
||||
}
|
||||
|
||||
fn get_span_sides(span: &Record, span_span: Span, side: &str) -> Result<i64, ShellError> {
|
||||
|
@ -16,9 +16,9 @@
|
||||
//! invoked by Nushell.
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use nu_plugin::{EvaluatedCall, LabeledError, MsgPackSerializer, serve_plugin};
|
||||
//! use nu_plugin::{EvaluatedCall, MsgPackSerializer, serve_plugin};
|
||||
//! use nu_plugin::{Plugin, PluginCommand, SimplePluginCommand, EngineInterface};
|
||||
//! use nu_protocol::{PluginSignature, Value};
|
||||
//! use nu_protocol::{PluginSignature, LabeledError, Value};
|
||||
//!
|
||||
//! struct MyPlugin;
|
||||
//! struct MyCommand;
|
||||
@ -64,7 +64,7 @@ mod util;
|
||||
pub use plugin::{
|
||||
serve_plugin, EngineInterface, Plugin, PluginCommand, PluginEncoder, SimplePluginCommand,
|
||||
};
|
||||
pub use protocol::{EvaluatedCall, LabeledError};
|
||||
pub use protocol::EvaluatedCall;
|
||||
pub use serializers::{json::JsonSerializer, msgpack::MsgPackSerializer};
|
||||
|
||||
// Used by other nu crates.
|
||||
|
@ -1,6 +1,6 @@
|
||||
use nu_protocol::{PipelineData, PluginSignature, Value};
|
||||
use nu_protocol::{LabeledError, PipelineData, PluginSignature, Value};
|
||||
|
||||
use crate::{EngineInterface, EvaluatedCall, LabeledError, Plugin};
|
||||
use crate::{EngineInterface, EvaluatedCall, Plugin};
|
||||
|
||||
/// The API for a Nushell plugin command
|
||||
///
|
||||
@ -18,7 +18,7 @@ use crate::{EngineInterface, EvaluatedCall, LabeledError, Plugin};
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// # use nu_plugin::*;
|
||||
/// # use nu_protocol::{PluginSignature, PipelineData, Type, Value};
|
||||
/// # use nu_protocol::{PluginSignature, PipelineData, Type, Value, LabeledError};
|
||||
/// struct LowercasePlugin;
|
||||
/// struct Lowercase;
|
||||
///
|
||||
@ -108,7 +108,7 @@ pub trait PluginCommand: Sync {
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// # use nu_plugin::*;
|
||||
/// # use nu_protocol::{PluginSignature, Type, Value};
|
||||
/// # use nu_protocol::{PluginSignature, Type, Value, LabeledError};
|
||||
/// struct HelloPlugin;
|
||||
/// struct Hello;
|
||||
///
|
||||
|
@ -6,7 +6,7 @@ use std::{
|
||||
};
|
||||
|
||||
use nu_protocol::{
|
||||
engine::Closure, Config, IntoInterruptiblePipelineData, ListStream, PipelineData,
|
||||
engine::Closure, Config, IntoInterruptiblePipelineData, LabeledError, ListStream, PipelineData,
|
||||
PluginSignature, ShellError, Spanned, Value,
|
||||
};
|
||||
|
||||
@ -16,7 +16,7 @@ use crate::{
|
||||
PluginCall, PluginCallId, PluginCallResponse, PluginCustomValue, PluginInput, PluginOption,
|
||||
ProtocolInfo,
|
||||
},
|
||||
LabeledError, PluginOutput,
|
||||
PluginOutput,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -4,8 +4,8 @@ use std::{
|
||||
};
|
||||
|
||||
use nu_protocol::{
|
||||
engine::Closure, Config, CustomValue, IntoInterruptiblePipelineData, PipelineData,
|
||||
PluginExample, PluginSignature, ShellError, Span, Spanned, Value,
|
||||
engine::Closure, Config, CustomValue, IntoInterruptiblePipelineData, LabeledError,
|
||||
PipelineData, PluginExample, PluginSignature, ShellError, Span, Spanned, Value,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -16,7 +16,7 @@ use crate::{
|
||||
ListStreamInfo, PipelineDataHeader, PluginCall, PluginCustomValue, PluginInput, Protocol,
|
||||
ProtocolInfo, RawStreamInfo, StreamData, StreamMessage,
|
||||
},
|
||||
EvaluatedCall, LabeledError, PluginCallResponse, PluginOutput,
|
||||
EvaluatedCall, PluginCallResponse, PluginOutput,
|
||||
};
|
||||
|
||||
use super::{EngineInterfaceManager, ReceivedPluginCall};
|
||||
@ -738,11 +738,7 @@ fn interface_write_response_with_stream() -> Result<(), ShellError> {
|
||||
fn interface_write_response_with_error() -> Result<(), ShellError> {
|
||||
let test = TestCase::new();
|
||||
let interface = test.engine().interface_for_context(35);
|
||||
let labeled_error = LabeledError {
|
||||
label: "this is an error".into(),
|
||||
msg: "a test error".into(),
|
||||
span: None,
|
||||
};
|
||||
let labeled_error = LabeledError::new("this is an error").with_help("a test error");
|
||||
interface
|
||||
.write_response(Err(labeled_error.clone()))?
|
||||
.write()?;
|
||||
|
@ -1,4 +1,5 @@
|
||||
use nu_engine::documentation::get_flags_section;
|
||||
use nu_protocol::LabeledError;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
@ -13,9 +14,7 @@ use std::sync::mpsc::TrySendError;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
|
||||
use crate::plugin::interface::{EngineInterfaceManager, ReceivedPluginCall};
|
||||
use crate::protocol::{
|
||||
CallInfo, CustomValueOp, LabeledError, PluginCustomValue, PluginInput, PluginOutput,
|
||||
};
|
||||
use crate::protocol::{CallInfo, CustomValueOp, PluginCustomValue, PluginInput, PluginOutput};
|
||||
use crate::EncodingType;
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -230,7 +229,7 @@ where
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// # use nu_plugin::*;
|
||||
/// # use nu_protocol::{PluginSignature, Type, Value};
|
||||
/// # use nu_protocol::{PluginSignature, LabeledError, Type, Value};
|
||||
/// struct HelloPlugin;
|
||||
/// struct Hello;
|
||||
///
|
||||
@ -537,11 +536,12 @@ pub fn serve_plugin(plugin: &impl Plugin, encoder: impl PluginEncoder + 'static)
|
||||
let result = if let Some(command) = commands.get(&name) {
|
||||
command.run(plugin, &engine, &call, input)
|
||||
} else {
|
||||
Err(LabeledError {
|
||||
label: format!("Plugin command not found: `{name}`"),
|
||||
msg: format!("plugin `{plugin_name}` doesn't have this command"),
|
||||
span: Some(call.head),
|
||||
})
|
||||
Err(
|
||||
LabeledError::new(format!("Plugin command not found: `{name}`")).with_label(
|
||||
format!("plugin `{plugin_name}` doesn't have this command"),
|
||||
call.head,
|
||||
),
|
||||
)
|
||||
};
|
||||
let write_result = engine
|
||||
.write_response(result)
|
||||
|
@ -12,8 +12,8 @@ use std::collections::HashMap;
|
||||
|
||||
pub use evaluated_call::EvaluatedCall;
|
||||
use nu_protocol::{
|
||||
ast::Operator, engine::Closure, Config, PipelineData, PluginSignature, RawStream, ShellError,
|
||||
Span, Spanned, Value,
|
||||
ast::Operator, engine::Closure, Config, LabeledError, PipelineData, PluginSignature, RawStream,
|
||||
ShellError, Span, Spanned, Value,
|
||||
};
|
||||
pub use plugin_custom_value::PluginCustomValue;
|
||||
#[cfg(test)]
|
||||
@ -289,73 +289,6 @@ pub enum StreamMessage {
|
||||
Ack(StreamId),
|
||||
}
|
||||
|
||||
/// An error message with debugging information that can be passed to Nushell from the plugin
|
||||
///
|
||||
/// The `LabeledError` struct is a structured error message that can be returned from
|
||||
/// a [Plugin](crate::Plugin)'s [`run`](crate::Plugin::run()) method. It contains
|
||||
/// the error message along with optional [Span] data to support highlighting in the
|
||||
/// shell.
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
|
||||
pub struct LabeledError {
|
||||
/// The name of the error
|
||||
pub label: String,
|
||||
/// A detailed error description
|
||||
pub msg: String,
|
||||
/// The [Span] in which the error occurred
|
||||
pub span: Option<Span>,
|
||||
}
|
||||
|
||||
impl From<LabeledError> for ShellError {
|
||||
fn from(error: LabeledError) -> Self {
|
||||
if error.span.is_some() {
|
||||
ShellError::GenericError {
|
||||
error: error.label,
|
||||
msg: error.msg,
|
||||
span: error.span,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
}
|
||||
} else {
|
||||
ShellError::GenericError {
|
||||
error: error.label,
|
||||
msg: "".into(),
|
||||
span: None,
|
||||
help: (!error.msg.is_empty()).then_some(error.msg),
|
||||
inner: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShellError> for LabeledError {
|
||||
fn from(error: ShellError) -> Self {
|
||||
use miette::Diagnostic;
|
||||
// This is not perfect - we can only take the first labeled span as that's all we have
|
||||
// space for.
|
||||
if let Some(labeled_span) = error.labels().and_then(|mut iter| iter.nth(0)) {
|
||||
let offset = labeled_span.offset();
|
||||
let span = Span::new(offset, offset + labeled_span.len());
|
||||
LabeledError {
|
||||
label: error.to_string(),
|
||||
msg: labeled_span
|
||||
.label()
|
||||
.map(|label| label.to_owned())
|
||||
.unwrap_or_else(|| "".into()),
|
||||
span: Some(span),
|
||||
}
|
||||
} else {
|
||||
LabeledError {
|
||||
label: error.to_string(),
|
||||
msg: error
|
||||
.help()
|
||||
.map(|help| help.to_string())
|
||||
.unwrap_or_else(|| "".into()),
|
||||
span: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Response to a [`PluginCall`]. The type parameter determines the output type for pipeline data.
|
||||
///
|
||||
/// Note: exported for internal use, not public.
|
||||
|
@ -1,11 +1,11 @@
|
||||
macro_rules! generate_tests {
|
||||
($encoder:expr) => {
|
||||
use crate::protocol::{
|
||||
CallInfo, CustomValueOp, EvaluatedCall, LabeledError, PipelineDataHeader, PluginCall,
|
||||
CallInfo, CustomValueOp, EvaluatedCall, PipelineDataHeader, PluginCall,
|
||||
PluginCallResponse, PluginCustomValue, PluginInput, PluginOption, PluginOutput,
|
||||
StreamData, StreamMessage,
|
||||
};
|
||||
use nu_protocol::{PluginSignature, Span, Spanned, SyntaxShape, Value};
|
||||
use nu_protocol::{LabeledError, PluginSignature, Span, Spanned, SyntaxShape, Value};
|
||||
|
||||
#[test]
|
||||
fn decode_eof() {
|
||||
@ -364,11 +364,15 @@ macro_rules! generate_tests {
|
||||
|
||||
#[test]
|
||||
fn response_round_trip_error() {
|
||||
let error = LabeledError {
|
||||
label: "label".into(),
|
||||
msg: "msg".into(),
|
||||
span: Some(Span::new(2, 30)),
|
||||
};
|
||||
let error = LabeledError::new("label")
|
||||
.with_code("test::error")
|
||||
.with_url("https://example.org/test/error")
|
||||
.with_help("some help")
|
||||
.with_label("msg", Span::new(2, 30))
|
||||
.with_inner(ShellError::IOError {
|
||||
msg: "io error".into(),
|
||||
});
|
||||
|
||||
let response = PluginCallResponse::Error(error.clone());
|
||||
let output = PluginOutput::CallResponse(6, response);
|
||||
|
||||
@ -392,11 +396,7 @@ macro_rules! generate_tests {
|
||||
|
||||
#[test]
|
||||
fn response_round_trip_error_none() {
|
||||
let error = LabeledError {
|
||||
label: "label".into(),
|
||||
msg: "msg".into(),
|
||||
span: None,
|
||||
};
|
||||
let error = LabeledError::new("error");
|
||||
let response = PluginCallResponse::Error(error.clone());
|
||||
let output = PluginOutput::CallResponse(7, response);
|
||||
|
||||
|
241
crates/nu-protocol/src/errors/labeled_error.rs
Normal file
241
crates/nu-protocol/src/errors/labeled_error.rs
Normal file
@ -0,0 +1,241 @@
|
||||
use std::fmt;
|
||||
|
||||
use miette::Diagnostic;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Span;
|
||||
|
||||
use super::ShellError;
|
||||
|
||||
/// A very generic type of error used for interfacing with external code, such as scripts and
|
||||
/// plugins.
|
||||
///
|
||||
/// This generally covers most of the interface of [`miette::Diagnostic`], but with types that are
|
||||
/// well-defined for our protocol.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct LabeledError {
|
||||
/// The main message for the error.
|
||||
pub msg: String,
|
||||
/// Labeled spans attached to the error, demonstrating to the user where the problem is.
|
||||
#[serde(default)]
|
||||
pub labels: Vec<ErrorLabel>,
|
||||
/// A unique machine- and search-friendly error code to associate to the error. (e.g.
|
||||
/// `nu::shell::missing_config_value`)
|
||||
#[serde(default)]
|
||||
pub code: Option<String>,
|
||||
/// A link to documentation about the error, used in conjunction with `code`
|
||||
#[serde(default)]
|
||||
pub url: Option<String>,
|
||||
/// Additional help for the error, usually a hint about what the user might try
|
||||
#[serde(default)]
|
||||
pub help: Option<String>,
|
||||
/// Errors that are related to or caused this error
|
||||
#[serde(default)]
|
||||
pub inner: Vec<LabeledError>,
|
||||
}
|
||||
|
||||
impl LabeledError {
|
||||
/// Create a new plain [`LabeledError`] with the given message.
|
||||
///
|
||||
/// This is usually used builder-style with methods like [`.with_label()`] to build an error.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_protocol::LabeledError;
|
||||
/// let error = LabeledError::new("Something bad happened");
|
||||
/// assert_eq!("Something bad happened", error.to_string());
|
||||
/// ```
|
||||
pub fn new(msg: impl Into<String>) -> LabeledError {
|
||||
LabeledError {
|
||||
msg: msg.into(),
|
||||
labels: vec![],
|
||||
code: None,
|
||||
url: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a labeled span to the error to demonstrate to the user where the problem is.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_protocol::{LabeledError, Span};
|
||||
/// # let span = Span::test_data();
|
||||
/// let error = LabeledError::new("An error")
|
||||
/// .with_label("happened here", span);
|
||||
/// assert_eq!("happened here", &error.labels[0].text);
|
||||
/// assert_eq!(span, error.labels[0].span);
|
||||
/// ```
|
||||
pub fn with_label(mut self, text: impl Into<String>, span: Span) -> Self {
|
||||
self.labels.push(ErrorLabel {
|
||||
text: text.into(),
|
||||
span,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a unique machine- and search-friendly error code to associate to the error. (e.g.
|
||||
/// `nu::shell::missing_config_value`)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_protocol::LabeledError;
|
||||
/// let error = LabeledError::new("An error")
|
||||
/// .with_code("my_product::error");
|
||||
/// assert_eq!(Some("my_product::error"), error.code.as_deref());
|
||||
/// ```
|
||||
pub fn with_code(mut self, code: impl Into<String>) -> Self {
|
||||
self.code = Some(code.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a link to documentation about the error, used in conjunction with `code`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_protocol::LabeledError;
|
||||
/// let error = LabeledError::new("An error")
|
||||
/// .with_url("https://example.org/");
|
||||
/// assert_eq!(Some("https://example.org/"), error.url.as_deref());
|
||||
/// ```
|
||||
pub fn with_url(mut self, url: impl Into<String>) -> Self {
|
||||
self.url = Some(url.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add additional help for the error, usually a hint about what the user might try.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_protocol::LabeledError;
|
||||
/// let error = LabeledError::new("An error")
|
||||
/// .with_help("did you try turning it off and back on again?");
|
||||
/// assert_eq!(Some("did you try turning it off and back on again?"), error.help.as_deref());
|
||||
/// ```
|
||||
pub fn with_help(mut self, help: impl Into<String>) -> Self {
|
||||
self.help = Some(help.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an error that is related to or caused this error.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_protocol::LabeledError;
|
||||
/// let error = LabeledError::new("An error")
|
||||
/// .with_inner(LabeledError::new("out of coolant"));
|
||||
/// assert_eq!(LabeledError::new("out of coolant"), error.inner[0]);
|
||||
/// ```
|
||||
pub fn with_inner(mut self, inner: impl Into<LabeledError>) -> Self {
|
||||
self.inner.push(inner.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a [`LabeledError`] from a type that implements [`miette::Diagnostic`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// [`ShellError`] implements `miette::Diagnostic`:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use nu_protocol::{ShellError, LabeledError};
|
||||
/// let error = LabeledError::from_diagnostic(&ShellError::IOError { msg: "error".into() });
|
||||
/// assert!(error.to_string().contains("I/O error"));
|
||||
/// ```
|
||||
pub fn from_diagnostic(diag: &(impl miette::Diagnostic + ?Sized)) -> LabeledError {
|
||||
LabeledError {
|
||||
msg: diag.to_string(),
|
||||
labels: diag
|
||||
.labels()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|label| ErrorLabel {
|
||||
text: label.label().unwrap_or("").into(),
|
||||
span: Span::new(label.offset(), label.offset() + label.len()),
|
||||
})
|
||||
.collect(),
|
||||
code: diag.code().map(|s| s.to_string()),
|
||||
url: diag.url().map(|s| s.to_string()),
|
||||
help: diag.help().map(|s| s.to_string()),
|
||||
inner: diag
|
||||
.related()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(Self::from_diagnostic)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A labeled span within a [`LabeledError`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ErrorLabel {
|
||||
/// Text to show together with the span
|
||||
pub text: String,
|
||||
/// Span pointing at where the text references in the source
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl fmt::Display for LabeledError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LabeledError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
self.inner.get(0).map(|r| r as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl Diagnostic for LabeledError {
|
||||
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
|
||||
self.code.as_ref().map(Box::new).map(|b| b as _)
|
||||
}
|
||||
|
||||
fn severity(&self) -> Option<miette::Severity> {
|
||||
None
|
||||
}
|
||||
|
||||
fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
|
||||
self.help.as_ref().map(Box::new).map(|b| b as _)
|
||||
}
|
||||
|
||||
fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
|
||||
self.url.as_ref().map(Box::new).map(|b| b as _)
|
||||
}
|
||||
|
||||
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
|
||||
None
|
||||
}
|
||||
|
||||
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
|
||||
Some(Box::new(self.labels.iter().map(|label| {
|
||||
miette::LabeledSpan::new_with_span(
|
||||
Some(label.text.clone()).filter(|s| !s.is_empty()),
|
||||
label.span,
|
||||
)
|
||||
})))
|
||||
}
|
||||
|
||||
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
|
||||
Some(Box::new(self.inner.iter().map(|r| r as _)))
|
||||
}
|
||||
|
||||
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ShellError> for LabeledError {
|
||||
fn from(err: ShellError) -> Self {
|
||||
LabeledError::from_diagnostic(&err)
|
||||
}
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
pub mod cli_error;
|
||||
mod labeled_error;
|
||||
mod parse_error;
|
||||
mod parse_warning;
|
||||
mod shell_error;
|
||||
|
||||
pub use cli_error::{format_error, report_error, report_error_new};
|
||||
pub use labeled_error::{ErrorLabel, LabeledError};
|
||||
pub use parse_error::{DidYouMean, ParseError};
|
||||
pub use parse_warning::ParseWarning;
|
||||
pub use shell_error::*;
|
||||
|
@ -1097,6 +1097,11 @@ pub enum ShellError {
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// This is a generic error type used for user and plugin-generated errors.
|
||||
#[error(transparent)]
|
||||
#[diagnostic(transparent)]
|
||||
LabeledError(#[from] Box<super::LabeledError>),
|
||||
|
||||
/// Attempted to use a command that has been removed from Nushell.
|
||||
///
|
||||
/// ## Resolution
|
||||
@ -1396,6 +1401,12 @@ impl From<Box<dyn std::error::Error + Send + Sync>> for ShellError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<super::LabeledError> for ShellError {
|
||||
fn from(value: super::LabeledError) -> Self {
|
||||
ShellError::LabeledError(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_code(err: &ShellError) -> Option<String> {
|
||||
err.code().map(|code| code.to_string())
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
record, Category, CustomValue, PluginSignature, ShellError, Span, SyntaxShape, Value,
|
||||
record, Category, CustomValue, LabeledError, PluginSignature, ShellError, Span, SyntaxShape,
|
||||
Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{cool_custom_value::CoolCustomValue, CustomValuePlugin};
|
||||
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_protocol::{Category, PluginExample, PluginSignature, Span, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{Category, LabeledError, PluginExample, PluginSignature, Span, Value};
|
||||
|
||||
pub struct Generate;
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::{second_custom_value::SecondCustomValue, CustomValuePlugin};
|
||||
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_protocol::{Category, PluginExample, PluginSignature, Span, SyntaxShape, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
Category, LabeledError, PluginExample, PluginSignature, Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
pub struct Generate2;
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
use nu_plugin::{
|
||||
serve_plugin, EngineInterface, LabeledError, MsgPackSerializer, Plugin, PluginCommand,
|
||||
};
|
||||
use nu_plugin::{serve_plugin, EngineInterface, MsgPackSerializer, Plugin, PluginCommand};
|
||||
|
||||
mod cool_custom_value;
|
||||
mod second_custom_value;
|
||||
@ -14,7 +12,7 @@ mod update_arg;
|
||||
use drop_check::{DropCheck, DropCheckValue};
|
||||
use generate::Generate;
|
||||
use generate2::Generate2;
|
||||
use nu_protocol::CustomValue;
|
||||
use nu_protocol::{CustomValue, LabeledError};
|
||||
use update::Update;
|
||||
use update_arg::UpdateArg;
|
||||
|
||||
|
@ -2,8 +2,10 @@ use crate::{
|
||||
cool_custom_value::CoolCustomValue, second_custom_value::SecondCustomValue, CustomValuePlugin,
|
||||
};
|
||||
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_protocol::{Category, PluginExample, PluginSignature, ShellError, Span, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Value,
|
||||
};
|
||||
|
||||
pub struct Update;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{update::Update, CustomValuePlugin};
|
||||
|
||||
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};
|
||||
|
||||
pub struct UpdateArg;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use eml_parser::eml::*;
|
||||
use eml_parser::EmlParser;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::LabeledError;
|
||||
use nu_protocol::{
|
||||
record, Category, PluginExample, PluginSignature, ShellError, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
@ -1,9 +1,9 @@
|
||||
use ical::parser::ical::component::*;
|
||||
use ical::property::Property;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
record, Category, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
||||
record, Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
||||
};
|
||||
use std::io::BufReader;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
record, Category, PluginExample, PluginSignature, Record, ShellError, Type, Value,
|
||||
record, Category, LabeledError, PluginExample, PluginSignature, Record, ShellError, Type, Value,
|
||||
};
|
||||
|
||||
use crate::FromCmds;
|
||||
|
@ -1,9 +1,9 @@
|
||||
use ical::parser::vcard::component::*;
|
||||
use ical::property::Property;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||
use nu_protocol::{
|
||||
record, Category, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
||||
record, Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
||||
};
|
||||
|
||||
use crate::FromCmds;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use git2::{Branch, BranchType, DescribeOptions, Repository};
|
||||
use nu_plugin::LabeledError;
|
||||
use nu_protocol::{record, IntoSpanned, Span, Spanned, Value};
|
||||
use nu_protocol::{record, IntoSpanned, LabeledError, Span, Spanned, Value};
|
||||
use std::fmt::Write;
|
||||
use std::ops::BitAnd;
|
||||
use std::path::Path;
|
||||
@ -51,39 +50,38 @@ impl GStat {
|
||||
|
||||
// This path has to exist
|
||||
if !absolute_path.exists() {
|
||||
return Err(LabeledError {
|
||||
label: "error with path".to_string(),
|
||||
msg: format!("path does not exist [{}]", absolute_path.display()),
|
||||
span: Some(path.span),
|
||||
});
|
||||
return Err(LabeledError::new("error with path").with_label(
|
||||
format!("path does not exist [{}]", absolute_path.display()),
|
||||
path.span,
|
||||
));
|
||||
}
|
||||
let metadata = std::fs::metadata(&absolute_path).map_err(|e| LabeledError {
|
||||
label: "error with metadata".to_string(),
|
||||
msg: format!(
|
||||
"unable to get metadata for [{}], error: {}",
|
||||
absolute_path.display(),
|
||||
e
|
||||
),
|
||||
span: Some(path.span),
|
||||
let metadata = std::fs::metadata(&absolute_path).map_err(|e| {
|
||||
LabeledError::new("error with metadata").with_label(
|
||||
format!(
|
||||
"unable to get metadata for [{}], error: {}",
|
||||
absolute_path.display(),
|
||||
e
|
||||
),
|
||||
path.span,
|
||||
)
|
||||
})?;
|
||||
|
||||
// This path has to be a directory
|
||||
if !metadata.is_dir() {
|
||||
return Err(LabeledError {
|
||||
label: "error with directory".to_string(),
|
||||
msg: format!("path is not a directory [{}]", absolute_path.display()),
|
||||
span: Some(path.span),
|
||||
});
|
||||
return Err(LabeledError::new("error with directory").with_label(
|
||||
format!("path is not a directory [{}]", absolute_path.display()),
|
||||
path.span,
|
||||
));
|
||||
}
|
||||
|
||||
let repo_path = match absolute_path.canonicalize() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
return Err(LabeledError {
|
||||
label: format!("error canonicalizing [{}]", absolute_path.display()),
|
||||
msg: e.to_string(),
|
||||
span: Some(path.span),
|
||||
});
|
||||
return Err(LabeledError::new(format!(
|
||||
"error canonicalizing [{}]",
|
||||
absolute_path.display()
|
||||
))
|
||||
.with_label(e.to_string(), path.span));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
use crate::GStat;
|
||||
use nu_plugin::{
|
||||
EngineInterface, EvaluatedCall, LabeledError, Plugin, PluginCommand, SimplePluginCommand,
|
||||
};
|
||||
use nu_protocol::{Category, PluginSignature, Spanned, SyntaxShape, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand};
|
||||
use nu_protocol::{Category, LabeledError, PluginSignature, Spanned, SyntaxShape, Value};
|
||||
|
||||
pub struct GStatPlugin;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use nu_plugin::LabeledError;
|
||||
use nu_protocol::{ast::CellPath, Span, Value};
|
||||
use nu_protocol::{ast::CellPath, LabeledError, Span, Value};
|
||||
use semver::{BuildMetadata, Prerelease, Version};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||
@ -102,12 +101,7 @@ impl Inc {
|
||||
let cell_value = self.inc_value(head, &cell_value)?;
|
||||
|
||||
let mut value = value.clone();
|
||||
value
|
||||
.update_data_at_cell_path(&cell_path.members, cell_value)
|
||||
.map_err(|x| {
|
||||
let error: LabeledError = x.into();
|
||||
error
|
||||
})?;
|
||||
value.update_data_at_cell_path(&cell_path.members, cell_value)?;
|
||||
Ok(value)
|
||||
} else {
|
||||
self.inc_value(head, value)
|
||||
@ -119,17 +113,14 @@ impl Inc {
|
||||
Value::Int { val, .. } => Ok(Value::int(val + 1, head)),
|
||||
Value::String { val, .. } => Ok(self.apply(val, head)),
|
||||
x => {
|
||||
let msg = x.coerce_string().map_err(|e| LabeledError {
|
||||
label: "Unable to extract string".into(),
|
||||
msg: format!("value cannot be converted to string {x:?} - {e}"),
|
||||
span: Some(head),
|
||||
let msg = x.coerce_string().map_err(|e| {
|
||||
LabeledError::new("Unable to extract string").with_label(
|
||||
format!("value cannot be converted to string {x:?} - {e}"),
|
||||
head,
|
||||
)
|
||||
})?;
|
||||
|
||||
Err(LabeledError {
|
||||
label: "Incorrect value".into(),
|
||||
msg,
|
||||
span: Some(head),
|
||||
})
|
||||
Err(LabeledError::new("Incorrect value").with_label(msg, head))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
use crate::inc::SemVerAction;
|
||||
use crate::Inc;
|
||||
use nu_plugin::{
|
||||
EngineInterface, EvaluatedCall, LabeledError, Plugin, PluginCommand, SimplePluginCommand,
|
||||
};
|
||||
use nu_protocol::{ast::CellPath, PluginSignature, SyntaxShape, Value};
|
||||
use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand};
|
||||
use nu_protocol::{ast::CellPath, LabeledError, PluginSignature, SyntaxShape, Value};
|
||||
|
||||
pub struct IncPlugin;
|
||||
|
||||
|
@ -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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user