mirror of
https://github.com/nushell/nushell.git
synced 2025-04-01 19:56:41 +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:
parent
8237d15683
commit
efe25e3f58
@ -2,7 +2,8 @@ use nu_engine::CallExt;
|
|||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
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)]
|
#[derive(Clone)]
|
||||||
@ -258,13 +259,10 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// correct return: everything present
|
// correct return: everything present
|
||||||
ShellError::GenericError {
|
let mut error =
|
||||||
error: msg,
|
LabeledError::new(msg).with_label(text, Span::new(span_start as usize, span_end as usize));
|
||||||
msg: text,
|
error.help = help;
|
||||||
span: Some(Span::new(span_start as usize, span_end as usize)),
|
error.into()
|
||||||
help,
|
|
||||||
inner: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_span_sides(span: &Record, span_span: Span, side: &str) -> Result<i64, ShellError> {
|
fn get_span_sides(span: &Record, span_span: Span, side: &str) -> Result<i64, ShellError> {
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
//! invoked by Nushell.
|
//! invoked by Nushell.
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```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_plugin::{Plugin, PluginCommand, SimplePluginCommand, EngineInterface};
|
||||||
//! use nu_protocol::{PluginSignature, Value};
|
//! use nu_protocol::{PluginSignature, LabeledError, Value};
|
||||||
//!
|
//!
|
||||||
//! struct MyPlugin;
|
//! struct MyPlugin;
|
||||||
//! struct MyCommand;
|
//! struct MyCommand;
|
||||||
@ -64,7 +64,7 @@ mod util;
|
|||||||
pub use plugin::{
|
pub use plugin::{
|
||||||
serve_plugin, EngineInterface, Plugin, PluginCommand, PluginEncoder, SimplePluginCommand,
|
serve_plugin, EngineInterface, Plugin, PluginCommand, PluginEncoder, SimplePluginCommand,
|
||||||
};
|
};
|
||||||
pub use protocol::{EvaluatedCall, LabeledError};
|
pub use protocol::EvaluatedCall;
|
||||||
pub use serializers::{json::JsonSerializer, msgpack::MsgPackSerializer};
|
pub use serializers::{json::JsonSerializer, msgpack::MsgPackSerializer};
|
||||||
|
|
||||||
// Used by other nu crates.
|
// 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
|
/// The API for a Nushell plugin command
|
||||||
///
|
///
|
||||||
@ -18,7 +18,7 @@ use crate::{EngineInterface, EvaluatedCall, LabeledError, Plugin};
|
|||||||
/// Basic usage:
|
/// Basic usage:
|
||||||
/// ```
|
/// ```
|
||||||
/// # use nu_plugin::*;
|
/// # use nu_plugin::*;
|
||||||
/// # use nu_protocol::{PluginSignature, PipelineData, Type, Value};
|
/// # use nu_protocol::{PluginSignature, PipelineData, Type, Value, LabeledError};
|
||||||
/// struct LowercasePlugin;
|
/// struct LowercasePlugin;
|
||||||
/// struct Lowercase;
|
/// struct Lowercase;
|
||||||
///
|
///
|
||||||
@ -108,7 +108,7 @@ pub trait PluginCommand: Sync {
|
|||||||
/// Basic usage:
|
/// Basic usage:
|
||||||
/// ```
|
/// ```
|
||||||
/// # use nu_plugin::*;
|
/// # use nu_plugin::*;
|
||||||
/// # use nu_protocol::{PluginSignature, Type, Value};
|
/// # use nu_protocol::{PluginSignature, Type, Value, LabeledError};
|
||||||
/// struct HelloPlugin;
|
/// struct HelloPlugin;
|
||||||
/// struct Hello;
|
/// struct Hello;
|
||||||
///
|
///
|
||||||
|
@ -6,7 +6,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::Closure, Config, IntoInterruptiblePipelineData, ListStream, PipelineData,
|
engine::Closure, Config, IntoInterruptiblePipelineData, LabeledError, ListStream, PipelineData,
|
||||||
PluginSignature, ShellError, Spanned, Value,
|
PluginSignature, ShellError, Spanned, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ use crate::{
|
|||||||
PluginCall, PluginCallId, PluginCallResponse, PluginCustomValue, PluginInput, PluginOption,
|
PluginCall, PluginCallId, PluginCallResponse, PluginCustomValue, PluginInput, PluginOption,
|
||||||
ProtocolInfo,
|
ProtocolInfo,
|
||||||
},
|
},
|
||||||
LabeledError, PluginOutput,
|
PluginOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -4,8 +4,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::Closure, Config, CustomValue, IntoInterruptiblePipelineData, PipelineData,
|
engine::Closure, Config, CustomValue, IntoInterruptiblePipelineData, LabeledError,
|
||||||
PluginExample, PluginSignature, ShellError, Span, Spanned, Value,
|
PipelineData, PluginExample, PluginSignature, ShellError, Span, Spanned, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -16,7 +16,7 @@ use crate::{
|
|||||||
ListStreamInfo, PipelineDataHeader, PluginCall, PluginCustomValue, PluginInput, Protocol,
|
ListStreamInfo, PipelineDataHeader, PluginCall, PluginCustomValue, PluginInput, Protocol,
|
||||||
ProtocolInfo, RawStreamInfo, StreamData, StreamMessage,
|
ProtocolInfo, RawStreamInfo, StreamData, StreamMessage,
|
||||||
},
|
},
|
||||||
EvaluatedCall, LabeledError, PluginCallResponse, PluginOutput,
|
EvaluatedCall, PluginCallResponse, PluginOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{EngineInterfaceManager, ReceivedPluginCall};
|
use super::{EngineInterfaceManager, ReceivedPluginCall};
|
||||||
@ -738,11 +738,7 @@ fn interface_write_response_with_stream() -> Result<(), ShellError> {
|
|||||||
fn interface_write_response_with_error() -> Result<(), ShellError> {
|
fn interface_write_response_with_error() -> Result<(), ShellError> {
|
||||||
let test = TestCase::new();
|
let test = TestCase::new();
|
||||||
let interface = test.engine().interface_for_context(35);
|
let interface = test.engine().interface_for_context(35);
|
||||||
let labeled_error = LabeledError {
|
let labeled_error = LabeledError::new("this is an error").with_help("a test error");
|
||||||
label: "this is an error".into(),
|
|
||||||
msg: "a test error".into(),
|
|
||||||
span: None,
|
|
||||||
};
|
|
||||||
interface
|
interface
|
||||||
.write_response(Err(labeled_error.clone()))?
|
.write_response(Err(labeled_error.clone()))?
|
||||||
.write()?;
|
.write()?;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use nu_engine::documentation::get_flags_section;
|
use nu_engine::documentation::get_flags_section;
|
||||||
|
use nu_protocol::LabeledError;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -13,9 +14,7 @@ use std::sync::mpsc::TrySendError;
|
|||||||
use std::sync::{mpsc, Arc, Mutex};
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
|
|
||||||
use crate::plugin::interface::{EngineInterfaceManager, ReceivedPluginCall};
|
use crate::plugin::interface::{EngineInterfaceManager, ReceivedPluginCall};
|
||||||
use crate::protocol::{
|
use crate::protocol::{CallInfo, CustomValueOp, PluginCustomValue, PluginInput, PluginOutput};
|
||||||
CallInfo, CustomValueOp, LabeledError, PluginCustomValue, PluginInput, PluginOutput,
|
|
||||||
};
|
|
||||||
use crate::EncodingType;
|
use crate::EncodingType;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -230,7 +229,7 @@ where
|
|||||||
/// Basic usage:
|
/// Basic usage:
|
||||||
/// ```
|
/// ```
|
||||||
/// # use nu_plugin::*;
|
/// # use nu_plugin::*;
|
||||||
/// # use nu_protocol::{PluginSignature, Type, Value};
|
/// # use nu_protocol::{PluginSignature, LabeledError, Type, Value};
|
||||||
/// struct HelloPlugin;
|
/// struct HelloPlugin;
|
||||||
/// struct Hello;
|
/// 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) {
|
let result = if let Some(command) = commands.get(&name) {
|
||||||
command.run(plugin, &engine, &call, input)
|
command.run(plugin, &engine, &call, input)
|
||||||
} else {
|
} else {
|
||||||
Err(LabeledError {
|
Err(
|
||||||
label: format!("Plugin command not found: `{name}`"),
|
LabeledError::new(format!("Plugin command not found: `{name}`")).with_label(
|
||||||
msg: format!("plugin `{plugin_name}` doesn't have this command"),
|
format!("plugin `{plugin_name}` doesn't have this command"),
|
||||||
span: Some(call.head),
|
call.head,
|
||||||
})
|
),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let write_result = engine
|
let write_result = engine
|
||||||
.write_response(result)
|
.write_response(result)
|
||||||
|
@ -12,8 +12,8 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
pub use evaluated_call::EvaluatedCall;
|
pub use evaluated_call::EvaluatedCall;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Operator, engine::Closure, Config, PipelineData, PluginSignature, RawStream, ShellError,
|
ast::Operator, engine::Closure, Config, LabeledError, PipelineData, PluginSignature, RawStream,
|
||||||
Span, Spanned, Value,
|
ShellError, Span, Spanned, Value,
|
||||||
};
|
};
|
||||||
pub use plugin_custom_value::PluginCustomValue;
|
pub use plugin_custom_value::PluginCustomValue;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -289,73 +289,6 @@ pub enum StreamMessage {
|
|||||||
Ack(StreamId),
|
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.
|
/// Response to a [`PluginCall`]. The type parameter determines the output type for pipeline data.
|
||||||
///
|
///
|
||||||
/// Note: exported for internal use, not public.
|
/// Note: exported for internal use, not public.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
macro_rules! generate_tests {
|
macro_rules! generate_tests {
|
||||||
($encoder:expr) => {
|
($encoder:expr) => {
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
CallInfo, CustomValueOp, EvaluatedCall, LabeledError, PipelineDataHeader, PluginCall,
|
CallInfo, CustomValueOp, EvaluatedCall, PipelineDataHeader, PluginCall,
|
||||||
PluginCallResponse, PluginCustomValue, PluginInput, PluginOption, PluginOutput,
|
PluginCallResponse, PluginCustomValue, PluginInput, PluginOption, PluginOutput,
|
||||||
StreamData, StreamMessage,
|
StreamData, StreamMessage,
|
||||||
};
|
};
|
||||||
use nu_protocol::{PluginSignature, Span, Spanned, SyntaxShape, Value};
|
use nu_protocol::{LabeledError, PluginSignature, Span, Spanned, SyntaxShape, Value};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_eof() {
|
fn decode_eof() {
|
||||||
@ -364,11 +364,15 @@ macro_rules! generate_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn response_round_trip_error() {
|
fn response_round_trip_error() {
|
||||||
let error = LabeledError {
|
let error = LabeledError::new("label")
|
||||||
label: "label".into(),
|
.with_code("test::error")
|
||||||
msg: "msg".into(),
|
.with_url("https://example.org/test/error")
|
||||||
span: Some(Span::new(2, 30)),
|
.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 response = PluginCallResponse::Error(error.clone());
|
||||||
let output = PluginOutput::CallResponse(6, response);
|
let output = PluginOutput::CallResponse(6, response);
|
||||||
|
|
||||||
@ -392,11 +396,7 @@ macro_rules! generate_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn response_round_trip_error_none() {
|
fn response_round_trip_error_none() {
|
||||||
let error = LabeledError {
|
let error = LabeledError::new("error");
|
||||||
label: "label".into(),
|
|
||||||
msg: "msg".into(),
|
|
||||||
span: None,
|
|
||||||
};
|
|
||||||
let response = PluginCallResponse::Error(error.clone());
|
let response = PluginCallResponse::Error(error.clone());
|
||||||
let output = PluginOutput::CallResponse(7, response);
|
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;
|
pub mod cli_error;
|
||||||
|
mod labeled_error;
|
||||||
mod parse_error;
|
mod parse_error;
|
||||||
mod parse_warning;
|
mod parse_warning;
|
||||||
mod shell_error;
|
mod shell_error;
|
||||||
|
|
||||||
pub use cli_error::{format_error, report_error, report_error_new};
|
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_error::{DidYouMean, ParseError};
|
||||||
pub use parse_warning::ParseWarning;
|
pub use parse_warning::ParseWarning;
|
||||||
pub use shell_error::*;
|
pub use shell_error::*;
|
||||||
|
@ -1097,6 +1097,11 @@ pub enum ShellError {
|
|||||||
span: Span,
|
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.
|
/// Attempted to use a command that has been removed from Nushell.
|
||||||
///
|
///
|
||||||
/// ## Resolution
|
/// ## 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> {
|
pub fn into_code(err: &ShellError) -> Option<String> {
|
||||||
err.code().map(|code| code.to_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::{
|
use nu_protocol::{
|
||||||
record, Category, CustomValue, PluginSignature, ShellError, Span, SyntaxShape, Value,
|
record, Category, CustomValue, LabeledError, PluginSignature, ShellError, Span, SyntaxShape,
|
||||||
|
Value,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{cool_custom_value::CoolCustomValue, CustomValuePlugin};
|
use crate::{cool_custom_value::CoolCustomValue, CustomValuePlugin};
|
||||||
|
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginExample, PluginSignature, Span, Value};
|
use nu_protocol::{Category, LabeledError, PluginExample, PluginSignature, Span, Value};
|
||||||
|
|
||||||
pub struct Generate;
|
pub struct Generate;
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::{second_custom_value::SecondCustomValue, CustomValuePlugin};
|
use crate::{second_custom_value::SecondCustomValue, CustomValuePlugin};
|
||||||
|
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginExample, PluginSignature, Span, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Category, LabeledError, PluginExample, PluginSignature, Span, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Generate2;
|
pub struct Generate2;
|
||||||
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use nu_plugin::{
|
use nu_plugin::{serve_plugin, EngineInterface, MsgPackSerializer, Plugin, PluginCommand};
|
||||||
serve_plugin, EngineInterface, LabeledError, MsgPackSerializer, Plugin, PluginCommand,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod cool_custom_value;
|
mod cool_custom_value;
|
||||||
mod second_custom_value;
|
mod second_custom_value;
|
||||||
@ -14,7 +12,7 @@ mod update_arg;
|
|||||||
use drop_check::{DropCheck, DropCheckValue};
|
use drop_check::{DropCheck, DropCheckValue};
|
||||||
use generate::Generate;
|
use generate::Generate;
|
||||||
use generate2::Generate2;
|
use generate2::Generate2;
|
||||||
use nu_protocol::CustomValue;
|
use nu_protocol::{CustomValue, LabeledError};
|
||||||
use update::Update;
|
use update::Update;
|
||||||
use update_arg::UpdateArg;
|
use update_arg::UpdateArg;
|
||||||
|
|
||||||
|
@ -2,8 +2,10 @@ use crate::{
|
|||||||
cool_custom_value::CoolCustomValue, second_custom_value::SecondCustomValue, CustomValuePlugin,
|
cool_custom_value::CoolCustomValue, second_custom_value::SecondCustomValue, CustomValuePlugin,
|
||||||
};
|
};
|
||||||
|
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginExample, PluginSignature, ShellError, Span, Value};
|
use nu_protocol::{
|
||||||
|
Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Value,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Update;
|
pub struct Update;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{update::Update, CustomValuePlugin};
|
use crate::{update::Update, CustomValuePlugin};
|
||||||
|
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, SyntaxShape, Value};
|
use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Value};
|
||||||
|
|
||||||
pub struct UpdateArg;
|
pub struct UpdateArg;
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, RawStream, Type, Value};
|
use nu_protocol::{
|
||||||
|
Category, LabeledError, PipelineData, PluginExample, PluginSignature, RawStream, Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, Type, Value};
|
use nu_protocol::{Category, LabeledError, PluginSignature, Type, Value};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
@ -27,12 +27,10 @@ impl SimplePluginCommand for Config {
|
|||||||
let config = engine.get_plugin_config()?;
|
let config = engine.get_plugin_config()?;
|
||||||
match config {
|
match config {
|
||||||
Some(config) => Ok(config.clone()),
|
Some(config) => Ok(config.clone()),
|
||||||
None => Err(LabeledError {
|
None => Err(LabeledError::new("No config sent").with_label(
|
||||||
label: "No config sent".into(),
|
"configuration for this plugin was not found in `$env.config.plugins.example`",
|
||||||
msg: "Configuration for this plugin was not found in `$env.config.plugins.example`"
|
call.head,
|
||||||
.into(),
|
)),
|
||||||
span: Some(call.head),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, Value};
|
use nu_protocol::{Category, LabeledError, PluginSignature, Value};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, SyntaxShape, Type, Value};
|
use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Type, Value};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
@ -42,11 +42,8 @@ impl SimplePluginCommand for Env {
|
|||||||
// Get working directory
|
// Get working directory
|
||||||
Ok(Value::string(engine.get_current_dir()?, call.head))
|
Ok(Value::string(engine.get_current_dir()?, call.head))
|
||||||
}
|
}
|
||||||
Some(value) => Err(LabeledError {
|
Some(value) => Err(LabeledError::new("Invalid arguments")
|
||||||
label: "Invalid arguments".into(),
|
.with_label("--cwd can't be used with --set", value.span())),
|
||||||
msg: "--cwd can't be used with --set".into(),
|
|
||||||
span: Some(value.span()),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
} else if let Some(value) = call.get_flag_value("set") {
|
} else if let Some(value) = call.get_flag_value("set") {
|
||||||
// Set single env var
|
// Set single env var
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type};
|
use nu_protocol::{
|
||||||
|
Category, LabeledError, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, IntoInterruptiblePipelineData, PipelineData, PluginExample, PluginSignature,
|
Category, IntoInterruptiblePipelineData, LabeledError, PipelineData, PluginExample,
|
||||||
SyntaxShape, Type, Value,
|
PluginSignature, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, Value};
|
use nu_protocol::{Category, LabeledError, PluginSignature, Value};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
@ -32,10 +32,7 @@ particularly useful.
|
|||||||
call: &EvaluatedCall,
|
call: &EvaluatedCall,
|
||||||
_input: &Value,
|
_input: &Value,
|
||||||
) -> Result<Value, LabeledError> {
|
) -> Result<Value, LabeledError> {
|
||||||
Err(LabeledError {
|
Err(LabeledError::new("No subcommand provided")
|
||||||
label: "No subcommand provided".into(),
|
.with_label("add --help to see a list of subcommands", call.head))
|
||||||
msg: "add --help to see a list of subcommands".into(),
|
|
||||||
span: Some(call.head),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginExample, PluginSignature, SyntaxShape, Value};
|
use nu_protocol::{Category, LabeledError, PluginExample, PluginSignature, SyntaxShape, Value};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, ListStream, PipelineData, PluginExample, PluginSignature, SyntaxShape, Type, Value,
|
Category, LabeledError, ListStream, PipelineData, PluginExample, PluginSignature, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, PluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand};
|
||||||
use nu_protocol::{Category, PipelineData, PluginExample, PluginSignature, Span, Type, Value};
|
use nu_protocol::{
|
||||||
|
Category, LabeledError, PipelineData, PluginExample, PluginSignature, Span, Type, Value,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
@ -33,18 +35,15 @@ impl PluginCommand for Sum {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, LabeledError> {
|
) -> Result<PipelineData, LabeledError> {
|
||||||
let mut acc = IntOrFloat::Int(0);
|
let mut acc = IntOrFloat::Int(0);
|
||||||
let span = input.span();
|
|
||||||
for value in input {
|
for value in input {
|
||||||
if let Ok(n) = value.as_i64() {
|
if let Ok(n) = value.as_i64() {
|
||||||
acc.add_i64(n);
|
acc.add_i64(n);
|
||||||
} else if let Ok(n) = value.as_f64() {
|
} else if let Ok(n) = value.as_f64() {
|
||||||
acc.add_f64(n);
|
acc.add_f64(n);
|
||||||
} else {
|
} else {
|
||||||
return Err(LabeledError {
|
return Err(LabeledError::new("Sum only accepts ints and floats")
|
||||||
label: "Stream only accepts ints and floats".into(),
|
.with_label(format!("found {} in input", value.get_type()), value.span())
|
||||||
msg: format!("found {}", value.get_type()),
|
.with_label("can't be used here", call.head));
|
||||||
span,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(PipelineData::Value(acc.to_value(call.head), None))
|
Ok(PipelineData::Value(acc.to_value(call.head), None))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, SyntaxShape, Value};
|
use nu_protocol::{Category, LabeledError, PluginSignature, SyntaxShape, Value};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
@ -31,10 +31,7 @@ impl SimplePluginCommand for Three {
|
|||||||
) -> Result<Value, LabeledError> {
|
) -> Result<Value, LabeledError> {
|
||||||
plugin.print_values(3, call, input)?;
|
plugin.print_values(3, call, input)?;
|
||||||
|
|
||||||
Err(LabeledError {
|
Err(LabeledError::new("ERROR from plugin")
|
||||||
label: "ERROR from plugin".into(),
|
.with_label("error message pointing to call head span", call.head))
|
||||||
msg: "error message pointing to call head span".into(),
|
|
||||||
span: Some(call.head),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{record, Category, PluginSignature, SyntaxShape, Value};
|
use nu_protocol::{record, Category, LabeledError, PluginSignature, SyntaxShape, Value};
|
||||||
|
|
||||||
use crate::Example;
|
use crate::Example;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_plugin::{EvaluatedCall, LabeledError};
|
use nu_plugin::EvaluatedCall;
|
||||||
use nu_protocol::Value;
|
use nu_protocol::{LabeledError, Value};
|
||||||
|
|
||||||
pub struct Example;
|
pub struct Example;
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use eml_parser::eml::*;
|
use eml_parser::eml::*;
|
||||||
use eml_parser::EmlParser;
|
use eml_parser::EmlParser;
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
|
use nu_protocol::LabeledError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
record, Category, PluginExample, PluginSignature, ShellError, Span, SyntaxShape, Type, Value,
|
record, Category, PluginExample, PluginSignature, ShellError, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use ical::parser::ical::component::*;
|
use ical::parser::ical::component::*;
|
||||||
use ical::property::Property;
|
use ical::property::Property;
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
record, Category, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
record, Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
record, Category, PluginExample, PluginSignature, Record, ShellError, Type, Value,
|
record, Category, LabeledError, PluginExample, PluginSignature, Record, ShellError, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::FromCmds;
|
use crate::FromCmds;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use ical::parser::vcard::component::*;
|
use ical::parser::vcard::component::*;
|
||||||
use ical::property::Property;
|
use ical::property::Property;
|
||||||
use indexmap::map::IndexMap;
|
use indexmap::map::IndexMap;
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
record, Category, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
record, Category, LabeledError, PluginExample, PluginSignature, ShellError, Span, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::FromCmds;
|
use crate::FromCmds;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use git2::{Branch, BranchType, DescribeOptions, Repository};
|
use git2::{Branch, BranchType, DescribeOptions, Repository};
|
||||||
use nu_plugin::LabeledError;
|
use nu_protocol::{record, IntoSpanned, LabeledError, Span, Spanned, Value};
|
||||||
use nu_protocol::{record, IntoSpanned, Span, Spanned, Value};
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::ops::BitAnd;
|
use std::ops::BitAnd;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@ -51,39 +50,38 @@ impl GStat {
|
|||||||
|
|
||||||
// This path has to exist
|
// This path has to exist
|
||||||
if !absolute_path.exists() {
|
if !absolute_path.exists() {
|
||||||
return Err(LabeledError {
|
return Err(LabeledError::new("error with path").with_label(
|
||||||
label: "error with path".to_string(),
|
format!("path does not exist [{}]", absolute_path.display()),
|
||||||
msg: format!("path does not exist [{}]", absolute_path.display()),
|
path.span,
|
||||||
span: Some(path.span),
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let metadata = std::fs::metadata(&absolute_path).map_err(|e| LabeledError {
|
let metadata = std::fs::metadata(&absolute_path).map_err(|e| {
|
||||||
label: "error with metadata".to_string(),
|
LabeledError::new("error with metadata").with_label(
|
||||||
msg: format!(
|
format!(
|
||||||
"unable to get metadata for [{}], error: {}",
|
"unable to get metadata for [{}], error: {}",
|
||||||
absolute_path.display(),
|
absolute_path.display(),
|
||||||
e
|
e
|
||||||
),
|
),
|
||||||
span: Some(path.span),
|
path.span,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// This path has to be a directory
|
// This path has to be a directory
|
||||||
if !metadata.is_dir() {
|
if !metadata.is_dir() {
|
||||||
return Err(LabeledError {
|
return Err(LabeledError::new("error with directory").with_label(
|
||||||
label: "error with directory".to_string(),
|
format!("path is not a directory [{}]", absolute_path.display()),
|
||||||
msg: format!("path is not a directory [{}]", absolute_path.display()),
|
path.span,
|
||||||
span: Some(path.span),
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let repo_path = match absolute_path.canonicalize() {
|
let repo_path = match absolute_path.canonicalize() {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(LabeledError {
|
return Err(LabeledError::new(format!(
|
||||||
label: format!("error canonicalizing [{}]", absolute_path.display()),
|
"error canonicalizing [{}]",
|
||||||
msg: e.to_string(),
|
absolute_path.display()
|
||||||
span: Some(path.span),
|
))
|
||||||
});
|
.with_label(e.to_string(), path.span));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use crate::GStat;
|
use crate::GStat;
|
||||||
use nu_plugin::{
|
use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand};
|
||||||
EngineInterface, EvaluatedCall, LabeledError, Plugin, PluginCommand, SimplePluginCommand,
|
use nu_protocol::{Category, LabeledError, PluginSignature, Spanned, SyntaxShape, Value};
|
||||||
};
|
|
||||||
use nu_protocol::{Category, PluginSignature, Spanned, SyntaxShape, Value};
|
|
||||||
|
|
||||||
pub struct GStatPlugin;
|
pub struct GStatPlugin;
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use nu_plugin::LabeledError;
|
use nu_protocol::{ast::CellPath, LabeledError, Span, Value};
|
||||||
use nu_protocol::{ast::CellPath, Span, Value};
|
|
||||||
use semver::{BuildMetadata, Prerelease, Version};
|
use semver::{BuildMetadata, Prerelease, Version};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
||||||
@ -102,12 +101,7 @@ impl Inc {
|
|||||||
let cell_value = self.inc_value(head, &cell_value)?;
|
let cell_value = self.inc_value(head, &cell_value)?;
|
||||||
|
|
||||||
let mut value = value.clone();
|
let mut value = value.clone();
|
||||||
value
|
value.update_data_at_cell_path(&cell_path.members, cell_value)?;
|
||||||
.update_data_at_cell_path(&cell_path.members, cell_value)
|
|
||||||
.map_err(|x| {
|
|
||||||
let error: LabeledError = x.into();
|
|
||||||
error
|
|
||||||
})?;
|
|
||||||
Ok(value)
|
Ok(value)
|
||||||
} else {
|
} else {
|
||||||
self.inc_value(head, value)
|
self.inc_value(head, value)
|
||||||
@ -119,17 +113,14 @@ impl Inc {
|
|||||||
Value::Int { val, .. } => Ok(Value::int(val + 1, head)),
|
Value::Int { val, .. } => Ok(Value::int(val + 1, head)),
|
||||||
Value::String { val, .. } => Ok(self.apply(val, head)),
|
Value::String { val, .. } => Ok(self.apply(val, head)),
|
||||||
x => {
|
x => {
|
||||||
let msg = x.coerce_string().map_err(|e| LabeledError {
|
let msg = x.coerce_string().map_err(|e| {
|
||||||
label: "Unable to extract string".into(),
|
LabeledError::new("Unable to extract string").with_label(
|
||||||
msg: format!("value cannot be converted to string {x:?} - {e}"),
|
format!("value cannot be converted to string {x:?} - {e}"),
|
||||||
span: Some(head),
|
head,
|
||||||
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Err(LabeledError {
|
Err(LabeledError::new("Incorrect value").with_label(msg, head))
|
||||||
label: "Incorrect value".into(),
|
|
||||||
msg,
|
|
||||||
span: Some(head),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::inc::SemVerAction;
|
use crate::inc::SemVerAction;
|
||||||
use crate::Inc;
|
use crate::Inc;
|
||||||
use nu_plugin::{
|
use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand};
|
||||||
EngineInterface, EvaluatedCall, LabeledError, Plugin, PluginCommand, SimplePluginCommand,
|
use nu_protocol::{ast::CellPath, LabeledError, PluginSignature, SyntaxShape, Value};
|
||||||
};
|
|
||||||
use nu_protocol::{ast::CellPath, PluginSignature, SyntaxShape, Value};
|
|
||||||
|
|
||||||
pub struct IncPlugin;
|
pub struct IncPlugin;
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ use crate::query_web::QueryWeb;
|
|||||||
use crate::query_xml::QueryXml;
|
use crate::query_xml::QueryXml;
|
||||||
|
|
||||||
use nu_engine::documentation::get_flags_section;
|
use nu_engine::documentation::get_flags_section;
|
||||||
use nu_plugin::{EvaluatedCall, LabeledError, Plugin, PluginCommand, SimplePluginCommand};
|
use nu_plugin::{EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, Value};
|
use nu_protocol::{Category, LabeledError, PluginSignature, Value};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use gjson::Value as gjValue;
|
use gjson::Value as gjValue;
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginSignature, Record, Span, Spanned, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Category, LabeledError, PluginSignature, Record, Span, Spanned, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::Query;
|
use crate::Query;
|
||||||
|
|
||||||
@ -39,22 +41,15 @@ pub fn execute_json_query(
|
|||||||
let input_string = match input.coerce_str() {
|
let input_string = match input.coerce_str() {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(LabeledError {
|
return Err(LabeledError::new("Problem with input data").with_inner(e));
|
||||||
span: Some(call.head),
|
|
||||||
msg: e.to_string(),
|
|
||||||
label: "problem with input data".to_string(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let query_string = match &query {
|
let query_string = match &query {
|
||||||
Some(v) => &v.item,
|
Some(v) => &v.item,
|
||||||
None => {
|
None => {
|
||||||
return Err(LabeledError {
|
return Err(LabeledError::new("Problem with input data")
|
||||||
msg: "problem with input data".to_string(),
|
.with_label("query string missing", call.head));
|
||||||
label: "problem with input data".to_string(),
|
|
||||||
span: Some(call.head),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,11 +57,9 @@ pub fn execute_json_query(
|
|||||||
let is_valid_json = gjson::valid(&input_string);
|
let is_valid_json = gjson::valid(&input_string);
|
||||||
|
|
||||||
if !is_valid_json {
|
if !is_valid_json {
|
||||||
return Err(LabeledError {
|
return Err(
|
||||||
msg: "invalid json".to_string(),
|
LabeledError::new("Invalid JSON").with_label("this is not valid JSON", call.head)
|
||||||
label: "invalid json".to_string(),
|
);
|
||||||
span: Some(call.head),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let val: gjValue = gjson::get(&input_string, query_string);
|
let val: gjValue = gjson::get(&input_string, query_string);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use crate::{web_tables::WebTable, Query};
|
use crate::{web_tables::WebTable, Query};
|
||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{Category, PluginExample, PluginSignature, Record, Span, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Category, LabeledError, PluginExample, PluginSignature, Record, Span, Spanned, SyntaxShape,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
use scraper::{Html, Selector as ScraperSelector};
|
use scraper::{Html, Selector as ScraperSelector};
|
||||||
|
|
||||||
pub struct QueryWeb;
|
pub struct QueryWeb;
|
||||||
@ -99,10 +102,7 @@ impl Default for Selector {
|
|||||||
|
|
||||||
pub fn parse_selector_params(call: &EvaluatedCall, input: &Value) -> Result<Value, LabeledError> {
|
pub fn parse_selector_params(call: &EvaluatedCall, input: &Value) -> Result<Value, LabeledError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let query: String = match call.get_flag("query")? {
|
let query: Option<Spanned<String>> = call.get_flag("query")?;
|
||||||
Some(q2) => q2,
|
|
||||||
None => "".to_string(),
|
|
||||||
};
|
|
||||||
let as_html = call.has_flag("as-html")?;
|
let as_html = call.has_flag("as-html")?;
|
||||||
let attribute = call.get_flag("attribute")?.unwrap_or_default();
|
let attribute = call.get_flag("attribute")?.unwrap_or_default();
|
||||||
let as_table: Value = call
|
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")?;
|
let inspect = call.has_flag("inspect")?;
|
||||||
|
|
||||||
if !&query.is_empty() && ScraperSelector::parse(&query).is_err() {
|
if let Some(query) = &query {
|
||||||
return Err(LabeledError {
|
if let Err(err) = ScraperSelector::parse(&query.item) {
|
||||||
msg: "Cannot parse this query as a valid css selector".to_string(),
|
return Err(LabeledError::new("CSS query parse error")
|
||||||
label: "Parse error".to_string(),
|
.with_label(err.to_string(), query.span)
|
||||||
span: Some(head),
|
.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 {
|
let selector = Selector {
|
||||||
query,
|
query: query.map(|q| q.item).unwrap_or_default(),
|
||||||
as_html,
|
as_html,
|
||||||
attribute,
|
attribute,
|
||||||
as_table,
|
as_table,
|
||||||
@ -130,11 +134,8 @@ pub fn parse_selector_params(call: &EvaluatedCall, input: &Value) -> Result<Valu
|
|||||||
let span = input.span();
|
let span = input.span();
|
||||||
match input {
|
match input {
|
||||||
Value::String { val, .. } => Ok(begin_selector_query(val.to_string(), selector, span)),
|
Value::String { val, .. } => Ok(begin_selector_query(val.to_string(), selector, span)),
|
||||||
_ => Err(LabeledError {
|
_ => Err(LabeledError::new("Requires text input")
|
||||||
label: "requires text input".to_string(),
|
.with_label("expected text from pipeline", span)),
|
||||||
msg: "Expected text from pipeline".to_string(),
|
|
||||||
span: Some(span),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use nu_plugin::{EngineInterface, EvaluatedCall, LabeledError, SimplePluginCommand};
|
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
|
||||||
use nu_protocol::{record, Category, PluginSignature, Record, Span, Spanned, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
record, Category, LabeledError, PluginSignature, Record, Span, Spanned, SyntaxShape, Value,
|
||||||
|
};
|
||||||
use sxd_document::parser;
|
use sxd_document::parser;
|
||||||
use sxd_xpath::{Context, Factory};
|
use sxd_xpath::{Context, Factory};
|
||||||
|
|
||||||
@ -38,11 +40,9 @@ pub fn execute_xpath_query(
|
|||||||
let (query_string, span) = match &query {
|
let (query_string, span) = match &query {
|
||||||
Some(v) => (&v.item, v.span),
|
Some(v) => (&v.item, v.span),
|
||||||
None => {
|
None => {
|
||||||
return Err(LabeledError {
|
return Err(
|
||||||
msg: "problem with input data".to_string(),
|
LabeledError::new("problem with input data").with_label("query missing", call.head)
|
||||||
label: "problem with input data".to_string(),
|
)
|
||||||
span: Some(call.head),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,12 +50,10 @@ pub fn execute_xpath_query(
|
|||||||
let input_string = input.coerce_str()?;
|
let input_string = input.coerce_str()?;
|
||||||
let package = parser::parse(&input_string);
|
let package = parser::parse(&input_string);
|
||||||
|
|
||||||
if package.is_err() {
|
if let Err(err) = package {
|
||||||
return Err(LabeledError {
|
return Err(
|
||||||
label: "invalid xml document".to_string(),
|
LabeledError::new("Invalid XML document").with_label(err.to_string(), input.span())
|
||||||
msg: "invalid xml document".to_string(),
|
);
|
||||||
span: Some(call.head),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let package = package.expect("invalid xml document");
|
let package = package.expect("invalid xml document");
|
||||||
@ -107,29 +105,20 @@ pub fn execute_xpath_query(
|
|||||||
|
|
||||||
Ok(Value::list(records, call.head))
|
Ok(Value::list(records, call.head))
|
||||||
}
|
}
|
||||||
Err(_) => Err(LabeledError {
|
Err(err) => {
|
||||||
label: "xpath query error".to_string(),
|
Err(LabeledError::new("xpath query error").with_label(err.to_string(), call.head))
|
||||||
msg: "xpath query error".to_string(),
|
}
|
||||||
span: Some(call.head),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_xpath(xpath_str: &str, span: Span) -> Result<sxd_xpath::XPath, LabeledError> {
|
fn build_xpath(xpath_str: &str, span: Span) -> Result<sxd_xpath::XPath, LabeledError> {
|
||||||
let factory = Factory::new();
|
let factory = Factory::new();
|
||||||
|
|
||||||
if let Ok(xpath) = factory.build(xpath_str) {
|
match factory.build(xpath_str) {
|
||||||
xpath.ok_or_else(|| LabeledError {
|
Ok(xpath) => xpath.ok_or_else(|| {
|
||||||
label: "invalid xpath query".to_string(),
|
LabeledError::new("invalid xpath query").with_label("the query must not be empty", span)
|
||||||
msg: "invalid xpath query".to_string(),
|
}),
|
||||||
span: Some(span),
|
Err(err) => Err(LabeledError::new("invalid xpath query").with_label(err.to_string(), span)),
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(LabeledError {
|
|
||||||
label: "expected valid xpath query".to_string(),
|
|
||||||
msg: "expected valid xpath query".to_string(),
|
|
||||||
span: Some(span),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user