Commands to engine (#3448)

* commands to engine

* Correction of error in parser

* Added detailed regex error to parse

* better regex error parsing

* clippy corrections

* parse example with test

* secondary error for regex

* removed clone in error parser

* Secondary error message
This commit is contained in:
Fernando Herrera 2021-05-22 16:52:04 +01:00 committed by GitHub
parent 94a26abf21
commit f075e2459d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 77 deletions

View File

@ -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<ColumnPath>,
after: Option<ColumnPath>,
before: Option<ColumnPath>,
}
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<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args)
}
@ -82,16 +75,13 @@ impl WholeStreamCommand for Command {
}
}
fn operate(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
fn operate(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
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<ColumnPath> = args.rest(0)?;
let before: Option<ColumnPath> = args.get_flag("before")?;
let after: Option<ColumnPath> = args.get_flag("after")?;
if columns.is_empty() {
return Err(ShellError::labeled_error(
@ -125,7 +115,8 @@ fn operate(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
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<ActionStream, ShellError> {
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<ActionStream, ShellError> {
))
}
})
.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<ActionStream, ShellError> {
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<ActionStream, ShellError> {
))
}
})
.to_action_stream())
.to_input_stream())
} else {
Err(ShellError::labeled_error(
"no columns given",

View File

@ -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<String>,
regex: Tagged<bool>,
}
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<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
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 {
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<foo>\\w+) (?P<bar>\\w+)\"",
result: Some(vec![UntaggedValue::row(row).into()]),
}]
},
]
}
}
pub fn operate(args: CommandArgs) -> Result<ActionStream, ShellError> {
pub fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
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<String> = 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(&regex_pattern);
let mut parsed: VecDeque<Value> = 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<ActionStream, ShellError> {
}
}
Ok(parsed.into_iter().to_action_stream())
Ok(parsed.into_iter().to_output_stream())
}
fn build_regex(input: &str, tag: Tag) -> Result<String, ShellError> {
@ -165,6 +167,38 @@ fn column_names(regex: &Regex) -> Vec<String> {
.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;

View File

@ -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<Tagged<u64>>,
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<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
roll(args)
}
}
pub fn roll(args: CommandArgs) -> Result<ActionStream, ShellError> {
let (args, input) = args.process()?;
pub fn roll(args: CommandArgs) -> Result<OutputStream, ShellError> {
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<Vec<Value>> {

View File

@ -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<Tagged<u64>>,
}
@ -26,22 +25,23 @@ impl WholeStreamCommand for Command {
"Rolls the table rows."
}
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
roll(args)
}
}
pub fn roll(args: CommandArgs) -> Result<ActionStream, ShellError> {
pub fn roll(args: CommandArgs) -> Result<OutputStream, ShellError> {
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<Value>, Arguments { by: ref n }: &Arguments) -> Option<Vec<Value>> {

View File

@ -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<ActionStream, ShellError> {
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
roll(args)
}
}
pub fn roll(args: CommandArgs) -> Result<ActionStream, ShellError> {
pub fn roll(args: CommandArgs) -> Result<OutputStream, ShellError> {
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<Value>, Arguments { by: ref n }: &Arguments) -> Option<Vec<Value>> {

View File

@ -181,7 +181,7 @@ mod regex {
"#
));
assert!(actual.err.contains("invalid regex"));
assert!(actual.err.contains("unclosed group"));
})
}
}