forked from extern/nushell
Create errors from tables. (#3986)
``` > [ [ msg, labels, span]; ["The message", "Helpful message here", ([[start, end]; [0, 141]])] ] | error make error: The message ┌─ shell:1:1 │ 1 │ ╭ [ 2 │ │ [ msg, labels, span]; 3 │ │ ["The message", "Helpful message here", ([[start, end]; [0, 141]])] │ ╰─────────────────────────────────────────────────────────────────────^ Helpful message here ``` Adding a more flexible approach for creating error values. One use case, for instance is the idea of a test framework. A failed assertion instead of printing to the screen it could create tables with more details of the failed assertion and pass it to this command for making a full fledge error that Nu can show. This can (and should) be extended for capturing error values as well in the pipeline. One could also use it for inspection. For example: `.... | error inspect { # inspection here }` or "error handling" as well, like so: `.... | error capture { fix here }` However, we start here only with `error make` that creates an error value for you with limited support for the time being.
This commit is contained in:
parent
d90420ac4c
commit
c9c6bd4836
108
crates/nu-command/src/commands/core_commands/error/make.rs
Normal file
108
crates/nu-command/src/commands/core_commands/error/make.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||||
|
|
||||||
|
pub struct SubCommand;
|
||||||
|
|
||||||
|
impl WholeStreamCommand for SubCommand {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"error make"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("error make")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Create an error."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
let input = args.input;
|
||||||
|
|
||||||
|
Ok(input
|
||||||
|
.map(|value| {
|
||||||
|
make_error(&value)
|
||||||
|
.map(|err| UntaggedValue::Error(err).into_value(value.tag()))
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
UntaggedValue::Error(ShellError::untagged_runtime_error(
|
||||||
|
"Creating error value not supported.",
|
||||||
|
))
|
||||||
|
.into_value(value.tag())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.into_output_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Creates a labeled error",
|
||||||
|
example: r#"[
|
||||||
|
[ msg, labels, span];
|
||||||
|
["The message", "Helpful message here", ([[start, end]; [0, 141]])]
|
||||||
|
] | error make"#,
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_error(value: &Value) -> Option<ShellError> {
|
||||||
|
if let Value {
|
||||||
|
value: UntaggedValue::Row(dict),
|
||||||
|
..
|
||||||
|
} = value
|
||||||
|
{
|
||||||
|
let msg = dict.get_data_by_key("msg".spanned_unknown());
|
||||||
|
|
||||||
|
let labels = dict
|
||||||
|
.get_data_by_key("labels".spanned_unknown())
|
||||||
|
.map(|table| match &table.value {
|
||||||
|
UntaggedValue::Table(_) => table
|
||||||
|
.table_entries()
|
||||||
|
.map(|value| value.as_string().ok())
|
||||||
|
.collect(),
|
||||||
|
UntaggedValue::Primitive(Primitive::String(label)) => Some(vec![label.to_string()]),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let _anchor = dict.get_data_by_key("tag".spanned_unknown());
|
||||||
|
let span = dict.get_data_by_key("span".spanned_unknown());
|
||||||
|
|
||||||
|
if msg.is_none() || labels.is_none() || span.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = msg.map(|msg| msg.as_string().ok()).flatten();
|
||||||
|
|
||||||
|
if let Some(labels) = labels {
|
||||||
|
if labels.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(ShellError::labeled_error(
|
||||||
|
msg.expect("Message will always be present."),
|
||||||
|
&labels[0],
|
||||||
|
span.map(|data| match data {
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Row(vals),
|
||||||
|
..
|
||||||
|
} => match (vals.entries.get("start"), vals.entries.get("end")) {
|
||||||
|
(Some(start), Some(end)) => {
|
||||||
|
let start = start.as_usize().ok().unwrap_or(0);
|
||||||
|
let end = end.as_usize().ok().unwrap_or(0);
|
||||||
|
|
||||||
|
Span::new(start, end)
|
||||||
|
}
|
||||||
|
(_, _) => Span::unknown(),
|
||||||
|
},
|
||||||
|
_ => Span::unknown(),
|
||||||
|
})
|
||||||
|
.unwrap_or_else(Span::unknown),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
mod make;
|
||||||
|
|
||||||
|
pub use make::SubCommand as ErrorMake;
|
@ -4,6 +4,7 @@ mod def;
|
|||||||
mod describe;
|
mod describe;
|
||||||
mod do_;
|
mod do_;
|
||||||
pub(crate) mod echo;
|
pub(crate) mod echo;
|
||||||
|
mod error;
|
||||||
mod find;
|
mod find;
|
||||||
mod help;
|
mod help;
|
||||||
mod history;
|
mod history;
|
||||||
@ -28,6 +29,7 @@ pub use def::Def;
|
|||||||
pub use describe::Describe;
|
pub use describe::Describe;
|
||||||
pub use do_::Do;
|
pub use do_::Do;
|
||||||
pub use echo::Echo;
|
pub use echo::Echo;
|
||||||
|
pub use error::*;
|
||||||
pub use find::Find;
|
pub use find::Find;
|
||||||
pub use help::Help;
|
pub use help::Help;
|
||||||
pub use history::History;
|
pub use history::History;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_engine::WholeStreamCommand;
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue};
|
use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value};
|
||||||
|
|
||||||
pub struct Tags;
|
pub struct Tags;
|
||||||
|
|
||||||
@ -26,31 +26,49 @@ impl WholeStreamCommand for Tags {
|
|||||||
fn tags(args: CommandArgs) -> ActionStream {
|
fn tags(args: CommandArgs) -> ActionStream {
|
||||||
args.input
|
args.input
|
||||||
.map(move |v| {
|
.map(move |v| {
|
||||||
let mut tags = TaggedDictBuilder::new(v.tag());
|
TaggedDictBuilder::build(v.tag(), |tags| {
|
||||||
{
|
if let Some(anchor) = anchor_as_value(&v) {
|
||||||
let anchor = v.anchor();
|
tags.insert_value("anchor", anchor);
|
||||||
let span = v.tag.span;
|
|
||||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
|
||||||
dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
|
|
||||||
dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
|
|
||||||
tags.insert_value("span", dict.into_value());
|
|
||||||
|
|
||||||
match anchor {
|
|
||||||
Some(AnchorLocation::File(source)) => {
|
|
||||||
tags.insert_untagged("anchor", UntaggedValue::string(source));
|
|
||||||
}
|
|
||||||
Some(AnchorLocation::Url(source)) => {
|
|
||||||
tags.insert_untagged("anchor", UntaggedValue::string(source));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tags.into_value()
|
tags.insert_value(
|
||||||
|
"span",
|
||||||
|
TaggedDictBuilder::build(v.tag(), |span_dict| {
|
||||||
|
let span = v.tag().span;
|
||||||
|
span_dict.insert_untagged("start", UntaggedValue::int(span.start() as i64));
|
||||||
|
span_dict.insert_untagged("end", UntaggedValue::int(span.end() as i64));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.into_action_stream()
|
.into_action_stream()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn anchor_as_value(value: &Value) -> Option<Value> {
|
||||||
|
let tag = value.tag();
|
||||||
|
let anchor = tag.anchor;
|
||||||
|
|
||||||
|
anchor.as_ref()?;
|
||||||
|
|
||||||
|
Some(TaggedDictBuilder::build(value.tag(), |table| {
|
||||||
|
let value = match anchor {
|
||||||
|
Some(AnchorLocation::File(path)) => Some(("file", UntaggedValue::from(path))),
|
||||||
|
Some(AnchorLocation::Url(destination)) => {
|
||||||
|
Some(("url", UntaggedValue::from(destination)))
|
||||||
|
}
|
||||||
|
Some(AnchorLocation::Source(text)) => Some((
|
||||||
|
"source",
|
||||||
|
UntaggedValue::Primitive(Primitive::String(text.to_string())),
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((key, value)) = value {
|
||||||
|
table.insert_untagged(key, value);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::ShellError;
|
use super::ShellError;
|
||||||
|
@ -68,6 +68,7 @@ mod tests {
|
|||||||
|
|
||||||
fn full_tests() -> Vec<Command> {
|
fn full_tests() -> Vec<Command> {
|
||||||
vec![
|
vec![
|
||||||
|
whole_stream_command(ErrorMake),
|
||||||
whole_stream_command(Drop),
|
whole_stream_command(Drop),
|
||||||
whole_stream_command(DropNth),
|
whole_stream_command(DropNth),
|
||||||
whole_stream_command(DropColumn),
|
whole_stream_command(DropColumn),
|
||||||
|
@ -23,6 +23,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
|||||||
whole_stream_command(Tutor),
|
whole_stream_command(Tutor),
|
||||||
whole_stream_command(Find),
|
whole_stream_command(Find),
|
||||||
// System/file operations
|
// System/file operations
|
||||||
|
whole_stream_command(ErrorMake),
|
||||||
whole_stream_command(Exec),
|
whole_stream_command(Exec),
|
||||||
whole_stream_command(Pwd),
|
whole_stream_command(Pwd),
|
||||||
whole_stream_command(Ls),
|
whole_stream_command(Ls),
|
||||||
|
@ -79,6 +79,7 @@ fn tags_dont_persist_through_column_path() {
|
|||||||
cd temp;
|
cd temp;
|
||||||
let x = (open ../nu_times.csv).name;
|
let x = (open ../nu_times.csv).name;
|
||||||
$x | tags | get anchor | autoview;
|
$x | tags | get anchor | autoview;
|
||||||
|
cd ..;
|
||||||
rmdir temp
|
rmdir temp
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
@ -105,7 +106,8 @@ fn tags_persist_through_vars() {
|
|||||||
mkdir temp;
|
mkdir temp;
|
||||||
cd temp;
|
cd temp;
|
||||||
let x = (open ../nu_times.csv);
|
let x = (open ../nu_times.csv);
|
||||||
$x | tags | get anchor | autoview;
|
$x | tags | get anchor.file | autoview;
|
||||||
|
cd ..;
|
||||||
rmdir temp
|
rmdir temp
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
Loading…
Reference in New Issue
Block a user