diff --git a/crates/nu-command/src/commands/core_commands/error/make.rs b/crates/nu-command/src/commands/core_commands/error/make.rs new file mode 100644 index 0000000000..e47aa04d23 --- /dev/null +++ b/crates/nu-command/src/commands/core_commands/error/make.rs @@ -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 { + 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 { + 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 { + 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 +} diff --git a/crates/nu-command/src/commands/core_commands/error/mod.rs b/crates/nu-command/src/commands/core_commands/error/mod.rs new file mode 100644 index 0000000000..4a2ff564fd --- /dev/null +++ b/crates/nu-command/src/commands/core_commands/error/mod.rs @@ -0,0 +1,3 @@ +mod make; + +pub use make::SubCommand as ErrorMake; diff --git a/crates/nu-command/src/commands/core_commands/mod.rs b/crates/nu-command/src/commands/core_commands/mod.rs index a366862749..c7fd932d59 100644 --- a/crates/nu-command/src/commands/core_commands/mod.rs +++ b/crates/nu-command/src/commands/core_commands/mod.rs @@ -4,6 +4,7 @@ mod def; mod describe; mod do_; pub(crate) mod echo; +mod error; mod find; mod help; mod history; @@ -28,6 +29,7 @@ pub use def::Def; pub use describe::Describe; pub use do_::Do; pub use echo::Echo; +pub use error::*; pub use find::Find; pub use help::Help; pub use history::History; diff --git a/crates/nu-command/src/commands/core_commands/tags.rs b/crates/nu-command/src/commands/core_commands/tags.rs index 6a153c5fad..41200d6779 100644 --- a/crates/nu-command/src/commands/core_commands/tags.rs +++ b/crates/nu-command/src/commands/core_commands/tags.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; -use nu_protocol::{Signature, TaggedDictBuilder, UntaggedValue}; +use nu_protocol::{Primitive, Signature, TaggedDictBuilder, UntaggedValue, Value}; pub struct Tags; @@ -26,31 +26,49 @@ impl WholeStreamCommand for Tags { fn tags(args: CommandArgs) -> ActionStream { args.input .map(move |v| { - let mut tags = TaggedDictBuilder::new(v.tag()); - { - let anchor = v.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)); - } - _ => {} + TaggedDictBuilder::build(v.tag(), |tags| { + if let Some(anchor) = anchor_as_value(&v) { + tags.insert_value("anchor", anchor); } - } - 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() } +fn anchor_as_value(value: &Value) -> Option { + 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)] mod tests { use super::ShellError; diff --git a/crates/nu-command/src/commands/mod.rs b/crates/nu-command/src/commands/mod.rs index 56d5c0539e..9e91c9ba6e 100644 --- a/crates/nu-command/src/commands/mod.rs +++ b/crates/nu-command/src/commands/mod.rs @@ -68,6 +68,7 @@ mod tests { fn full_tests() -> Vec { vec![ + whole_stream_command(ErrorMake), whole_stream_command(Drop), whole_stream_command(DropNth), whole_stream_command(DropColumn), diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 0aa0995b4e..fa9d405a4c 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -23,6 +23,7 @@ pub fn create_default_context(interactive: bool) -> Result