diff --git a/crates/nu-command/src/commands/move_/command.rs b/crates/nu-command/src/commands/move_/command.rs index bfe0d2144e..ebce46bc74 100644 --- a/crates/nu-command/src/commands/move_/command.rs +++ b/crates/nu-command/src/commands/move_/command.rs @@ -2,18 +2,11 @@ use crate::prelude::*; use nu_data::base::select_fields; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; -use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, Value}; +use nu_protocol::{ColumnPath, Signature, SyntaxShape, Value}; use nu_source::HasFallibleSpan; pub struct Command; -#[derive(Deserialize)] -pub struct Arguments { - rest: Vec, - after: Option, - before: Option, -} - impl WholeStreamCommand for Command { fn name(&self) -> &str { "move" @@ -40,7 +33,7 @@ impl WholeStreamCommand for Command { "Move columns." } - fn run_with_actions(&self, args: CommandArgs) -> Result { + fn run(&self, args: CommandArgs) -> Result { operate(args) } @@ -82,16 +75,13 @@ impl WholeStreamCommand for Command { } } -fn operate(raw_args: CommandArgs) -> Result { +fn operate(raw_args: CommandArgs) -> Result { let name = raw_args.call_info.name_tag.clone(); - let ( - Arguments { - rest: mut columns, - before, - after, - }, - input, - ) = raw_args.process()?; + let args = raw_args.evaluate_once()?; + + let mut columns: Vec = args.rest(0)?; + let before: Option = args.get_flag("before")?; + let after: Option = args.get_flag("after")?; if columns.is_empty() { return Err(ShellError::labeled_error( @@ -125,7 +115,8 @@ fn operate(raw_args: CommandArgs) -> Result { if let Some(after) = after { let member = columns.remove(0); - Ok(input + Ok(args + .input .map(move |item| { let member = vec![member.clone()]; let column_paths = vec![&member, &columns] @@ -144,7 +135,7 @@ fn operate(raw_args: CommandArgs) -> Result { if let Some(column) = after.last() { if !keys.contains(&column.as_string()) { - ReturnSuccess::value(move_after(&item, &keys, &after)?) + move_after(&item, &keys, &after) } else { let msg = format!("can't move column {} after itself", column.as_string()); @@ -169,11 +160,12 @@ fn operate(raw_args: CommandArgs) -> Result { )) } }) - .to_action_stream()) + .to_input_stream()) } else if let Some(before) = before { let member = columns.remove(0); - Ok(input + Ok(args + .input .map(move |item| { let member = vec![member.clone()]; let column_paths = vec![&member, &columns] @@ -192,7 +184,7 @@ fn operate(raw_args: CommandArgs) -> Result { if let Some(column) = before.last() { if !keys.contains(&column.as_string()) { - ReturnSuccess::value(move_before(&item, &keys, &before)?) + move_before(&item, &keys, &before) } else { let msg = format!("can't move column {} before itself", column.as_string()); @@ -217,7 +209,7 @@ fn operate(raw_args: CommandArgs) -> Result { )) } }) - .to_action_stream()) + .to_input_stream()) } else { Err(ShellError::labeled_error( "no columns given", diff --git a/crates/nu-command/src/commands/parse/command.rs b/crates/nu-command/src/commands/parse/command.rs index 483b78fbed..aa4dea5d27 100644 --- a/crates/nu-command/src/commands/parse/command.rs +++ b/crates/nu-command/src/commands/parse/command.rs @@ -4,13 +4,7 @@ use nu_errors::ShellError; use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value}; use nu_source::Tagged; -use regex::Regex; - -#[derive(Deserialize)] -struct Arguments { - pattern: Tagged, - regex: Tagged, -} +use regex::{self, Regex}; pub struct Command; @@ -33,7 +27,7 @@ impl WholeStreamCommand for Command { "Parse columns from string data using a simple pattern." } - fn run_with_actions(&self, args: CommandArgs) -> Result { + fn run(&self, args: CommandArgs) -> Result { operate(args) } @@ -41,33 +35,41 @@ impl WholeStreamCommand for Command { let mut row = IndexMap::new(); row.insert("foo".to_string(), Value::from("hi")); row.insert("bar".to_string(), Value::from("there")); - vec![Example { - description: "Parse a string into two named columns", - example: "echo \"hi there\" | parse \"{foo} {bar}\"", - result: Some(vec![UntaggedValue::row(row).into()]), - }] + vec![ + Example { + description: "Parse a string into two named columns", + example: "echo \"hi there\" | parse \"{foo} {bar}\"", + result: Some(vec![UntaggedValue::row(row.clone()).into()]), + }, + Example { + description: "Parse a string using regex pattern", + example: "echo \"hi there\" | parse -r \"(?P\\w+) (?P\\w+)\"", + result: Some(vec![UntaggedValue::row(row).into()]), + }, + ] } } -pub fn operate(args: CommandArgs) -> Result { +pub fn operate(args: CommandArgs) -> Result { let name_tag = args.call_info.name_tag.clone(); - let (Arguments { regex, pattern }, input) = args.process()?; + let args = args.evaluate_once()?; - let regex_pattern = if let Tagged { item: true, tag } = regex { - Regex::new(&pattern.item) - .map_err(|_| ShellError::labeled_error("Invalid regex", "invalid regex", tag.span))? + let pattern: Tagged = args.req(0)?; + let regex: bool = args.has_flag("regex"); + + let item_to_parse = if regex { + pattern.item.clone() } else { - let parse_regex = build_regex(&pattern.item, name_tag.clone())?; - - Regex::new(&parse_regex).map_err(|_| { - ShellError::labeled_error("Invalid pattern", "invalid pattern", name_tag.span) - })? + build_regex(&pattern.item, pattern.tag.clone())? }; + let regex_pattern = + Regex::new(&item_to_parse).map_err(|e| parse_regex_error(e, pattern.span()))?; + let columns = column_names(®ex_pattern); let mut parsed: VecDeque = VecDeque::new(); - for v in input { + for v in args.input { match v.as_string() { Ok(s) => { let results = regex_pattern.captures_iter(&s); @@ -95,7 +97,7 @@ pub fn operate(args: CommandArgs) -> Result { } } - Ok(parsed.into_iter().to_action_stream()) + Ok(parsed.into_iter().to_output_stream()) } fn build_regex(input: &str, tag: Tag) -> Result { @@ -165,6 +167,38 @@ fn column_names(regex: &Regex) -> Vec { .collect() } +fn parse_regex_error(e: regex::Error, base_span: Span) -> ShellError { + match e { + regex::Error::Syntax(msg) => { + let mut lines = msg.lines(); + + let main_msg = lines + .next() + .map(|l| l.replace(':', "")) + .expect("invalid regex pattern"); + + let span = lines.nth(1).map(|l| l.find('^')).flatten().map(|space| { + let start = base_span.start() + space - 3; + Span::for_char(start) + }); + + let msg = lines + .next() + .map(|l| l.split(':').nth(1)) + .flatten() + .map(|s| s.trim().to_string()); + + match (msg, span) { + (Some(msg), Some(span)) => { + ShellError::labeled_error_with_secondary(&msg, &msg, span, main_msg, span) + } + _ => ShellError::labeled_error("Invalid regex", "invalid regex", base_span), + } + } + _ => ShellError::labeled_error("Invalid regex", "invalid regex", base_span), + } +} + #[cfg(test)] mod tests { use super::Command; diff --git a/crates/nu-command/src/commands/roll/column.rs b/crates/nu-command/src/commands/roll/column.rs index 180fc69544..097e5ac955 100644 --- a/crates/nu-command/src/commands/roll/column.rs +++ b/crates/nu-command/src/commands/roll/column.rs @@ -2,18 +2,16 @@ use crate::prelude::*; use nu_data::base::select_fields; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value}; +use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value}; use nu_source::Tagged; use super::support::{rotate, Direction}; pub struct SubCommand; -#[derive(Deserialize)] pub struct Arguments { by: Option>, opposite: bool, - #[serde(rename(deserialize = "cells-only"))] cells_only: bool, } @@ -47,25 +45,31 @@ impl WholeStreamCommand for SubCommand { "Rolls the table columns" } - fn run_with_actions(&self, args: CommandArgs) -> Result { + fn run(&self, args: CommandArgs) -> Result { roll(args) } } -pub fn roll(args: CommandArgs) -> Result { - let (args, input) = args.process()?; +pub fn roll(args: CommandArgs) -> Result { + let args = args.evaluate_once()?; - Ok(input + let options = Arguments { + by: args.opt(0)?, + opposite: args.has_flag("opposite"), + cells_only: args.has_flag("cells-only"), + }; + + Ok(args + .input .map(move |value| { let tag = value.tag(); - roll_by(value, &args) + roll_by(value, &options) .unwrap_or_else(|| vec![UntaggedValue::nothing().into_value(tag)]) .into_iter() - .map(ReturnSuccess::value) }) .flatten() - .to_action_stream()) + .to_output_stream()) } fn roll_by(value: Value, options: &Arguments) -> Option> { diff --git a/crates/nu-command/src/commands/roll/command.rs b/crates/nu-command/src/commands/roll/command.rs index 3d8bf28314..950608b236 100644 --- a/crates/nu-command/src/commands/roll/command.rs +++ b/crates/nu-command/src/commands/roll/command.rs @@ -1,14 +1,13 @@ use crate::prelude::*; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; use super::support::{rotate, Direction}; pub struct Command; -#[derive(Deserialize)] pub struct Arguments { by: Option>, } @@ -26,22 +25,23 @@ impl WholeStreamCommand for Command { "Rolls the table rows." } - fn run_with_actions(&self, args: CommandArgs) -> Result { + fn run(&self, args: CommandArgs) -> Result { roll(args) } } -pub fn roll(args: CommandArgs) -> Result { +pub fn roll(args: CommandArgs) -> Result { let name = args.call_info.name_tag.clone(); - let (args, mut input) = args.process()?; + let mut args = args.evaluate_once()?; - let values = input.drain_vec(); + let options = Arguments { by: args.opt(0)? }; - Ok((roll_down(values, &args) + let values = args.input.drain_vec(); + + Ok(roll_down(values, &options) .unwrap_or_else(|| vec![UntaggedValue::nothing().into_value(&name)]) .into_iter() - .map(ReturnSuccess::value)) - .to_action_stream()) + .to_output_stream()) } fn roll_down(values: Vec, Arguments { by: ref n }: &Arguments) -> Option> { diff --git a/crates/nu-command/src/commands/roll/up.rs b/crates/nu-command/src/commands/roll/up.rs index d4ca29d8d1..e5b8905365 100644 --- a/crates/nu-command/src/commands/roll/up.rs +++ b/crates/nu-command/src/commands/roll/up.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; -use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; use nu_source::Tagged; use super::support::{rotate, Direction}; @@ -26,22 +26,23 @@ impl WholeStreamCommand for SubCommand { "Rolls the table rows" } - fn run_with_actions(&self, args: CommandArgs) -> Result { + fn run(&self, args: CommandArgs) -> Result { roll(args) } } -pub fn roll(args: CommandArgs) -> Result { +pub fn roll(args: CommandArgs) -> Result { let name = args.call_info.name_tag.clone(); - let (args, mut input) = args.process()?; + let mut args = args.evaluate_once()?; - let values = input.drain_vec(); + let options = Arguments { by: args.opt(0)? }; - Ok((roll_up(values, &args) + let values = args.input.drain_vec(); + + Ok(roll_up(values, &options) .unwrap_or_else(|| vec![UntaggedValue::nothing().into_value(&name)]) .into_iter() - .map(ReturnSuccess::value)) - .to_action_stream()) + .to_output_stream()) } fn roll_up(values: Vec, Arguments { by: ref n }: &Arguments) -> Option> { diff --git a/crates/nu-command/tests/commands/parse.rs b/crates/nu-command/tests/commands/parse.rs index 76c7525718..3b9c9c5272 100644 --- a/crates/nu-command/tests/commands/parse.rs +++ b/crates/nu-command/tests/commands/parse.rs @@ -181,7 +181,7 @@ mod regex { "# )); - assert!(actual.err.contains("invalid regex")); + assert!(actual.err.contains("unclosed group")); }) } }