mirror of
https://github.com/nushell/nushell
synced 2025-01-15 22:54:16 +00:00
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:
parent
94a26abf21
commit
f075e2459d
6 changed files with 108 additions and 77 deletions
|
@ -2,18 +2,11 @@ use crate::prelude::*;
|
||||||
use nu_data::base::select_fields;
|
use nu_data::base::select_fields;
|
||||||
use nu_engine::WholeStreamCommand;
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, Value};
|
use nu_protocol::{ColumnPath, Signature, SyntaxShape, Value};
|
||||||
use nu_source::HasFallibleSpan;
|
use nu_source::HasFallibleSpan;
|
||||||
|
|
||||||
pub struct Command;
|
pub struct Command;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Arguments {
|
|
||||||
rest: Vec<ColumnPath>,
|
|
||||||
after: Option<ColumnPath>,
|
|
||||||
before: Option<ColumnPath>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WholeStreamCommand for Command {
|
impl WholeStreamCommand for Command {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
"move"
|
"move"
|
||||||
|
@ -40,7 +33,7 @@ impl WholeStreamCommand for Command {
|
||||||
"Move columns."
|
"Move columns."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
operate(args)
|
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 name = raw_args.call_info.name_tag.clone();
|
||||||
let (
|
let args = raw_args.evaluate_once()?;
|
||||||
Arguments {
|
|
||||||
rest: mut columns,
|
let mut columns: Vec<ColumnPath> = args.rest(0)?;
|
||||||
before,
|
let before: Option<ColumnPath> = args.get_flag("before")?;
|
||||||
after,
|
let after: Option<ColumnPath> = args.get_flag("after")?;
|
||||||
},
|
|
||||||
input,
|
|
||||||
) = raw_args.process()?;
|
|
||||||
|
|
||||||
if columns.is_empty() {
|
if columns.is_empty() {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
|
@ -125,7 +115,8 @@ fn operate(raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
||||||
if let Some(after) = after {
|
if let Some(after) = after {
|
||||||
let member = columns.remove(0);
|
let member = columns.remove(0);
|
||||||
|
|
||||||
Ok(input
|
Ok(args
|
||||||
|
.input
|
||||||
.map(move |item| {
|
.map(move |item| {
|
||||||
let member = vec![member.clone()];
|
let member = vec![member.clone()];
|
||||||
let column_paths = vec![&member, &columns]
|
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 let Some(column) = after.last() {
|
||||||
if !keys.contains(&column.as_string()) {
|
if !keys.contains(&column.as_string()) {
|
||||||
ReturnSuccess::value(move_after(&item, &keys, &after)?)
|
move_after(&item, &keys, &after)
|
||||||
} else {
|
} else {
|
||||||
let msg =
|
let msg =
|
||||||
format!("can't move column {} after itself", column.as_string());
|
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 {
|
} else if let Some(before) = before {
|
||||||
let member = columns.remove(0);
|
let member = columns.remove(0);
|
||||||
|
|
||||||
Ok(input
|
Ok(args
|
||||||
|
.input
|
||||||
.map(move |item| {
|
.map(move |item| {
|
||||||
let member = vec![member.clone()];
|
let member = vec![member.clone()];
|
||||||
let column_paths = vec![&member, &columns]
|
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 let Some(column) = before.last() {
|
||||||
if !keys.contains(&column.as_string()) {
|
if !keys.contains(&column.as_string()) {
|
||||||
ReturnSuccess::value(move_before(&item, &keys, &before)?)
|
move_before(&item, &keys, &before)
|
||||||
} else {
|
} else {
|
||||||
let msg =
|
let msg =
|
||||||
format!("can't move column {} before itself", column.as_string());
|
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 {
|
} else {
|
||||||
Err(ShellError::labeled_error(
|
Err(ShellError::labeled_error(
|
||||||
"no columns given",
|
"no columns given",
|
||||||
|
|
|
@ -4,13 +4,7 @@ use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
use nu_protocol::{Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::{self, Regex};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Arguments {
|
|
||||||
pattern: Tagged<String>,
|
|
||||||
regex: Tagged<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Command;
|
pub struct Command;
|
||||||
|
|
||||||
|
@ -33,7 +27,7 @@ impl WholeStreamCommand for Command {
|
||||||
"Parse columns from string data using a simple pattern."
|
"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)
|
operate(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,33 +35,41 @@ impl WholeStreamCommand for Command {
|
||||||
let mut row = IndexMap::new();
|
let mut row = IndexMap::new();
|
||||||
row.insert("foo".to_string(), Value::from("hi"));
|
row.insert("foo".to_string(), Value::from("hi"));
|
||||||
row.insert("bar".to_string(), Value::from("there"));
|
row.insert("bar".to_string(), Value::from("there"));
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Parse a string into two named columns",
|
Example {
|
||||||
example: "echo \"hi there\" | parse \"{foo} {bar}\"",
|
description: "Parse a string into two named columns",
|
||||||
result: Some(vec![UntaggedValue::row(row).into()]),
|
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 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 {
|
let pattern: Tagged<String> = args.req(0)?;
|
||||||
Regex::new(&pattern.item)
|
let regex: bool = args.has_flag("regex");
|
||||||
.map_err(|_| ShellError::labeled_error("Invalid regex", "invalid regex", tag.span))?
|
|
||||||
|
let item_to_parse = if regex {
|
||||||
|
pattern.item.clone()
|
||||||
} else {
|
} else {
|
||||||
let parse_regex = build_regex(&pattern.item, name_tag.clone())?;
|
build_regex(&pattern.item, pattern.tag.clone())?
|
||||||
|
|
||||||
Regex::new(&parse_regex).map_err(|_| {
|
|
||||||
ShellError::labeled_error("Invalid pattern", "invalid pattern", name_tag.span)
|
|
||||||
})?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let regex_pattern =
|
||||||
|
Regex::new(&item_to_parse).map_err(|e| parse_regex_error(e, pattern.span()))?;
|
||||||
|
|
||||||
let columns = column_names(®ex_pattern);
|
let columns = column_names(®ex_pattern);
|
||||||
let mut parsed: VecDeque<Value> = VecDeque::new();
|
let mut parsed: VecDeque<Value> = VecDeque::new();
|
||||||
|
|
||||||
for v in input {
|
for v in args.input {
|
||||||
match v.as_string() {
|
match v.as_string() {
|
||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
let results = regex_pattern.captures_iter(&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> {
|
fn build_regex(input: &str, tag: Tag) -> Result<String, ShellError> {
|
||||||
|
@ -165,6 +167,38 @@ fn column_names(regex: &Regex) -> Vec<String> {
|
||||||
.collect()
|
.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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Command;
|
use super::Command;
|
||||||
|
|
|
@ -2,18 +2,16 @@ use crate::prelude::*;
|
||||||
use nu_data::base::select_fields;
|
use nu_data::base::select_fields;
|
||||||
use nu_engine::WholeStreamCommand;
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
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 nu_source::Tagged;
|
||||||
|
|
||||||
use super::support::{rotate, Direction};
|
use super::support::{rotate, Direction};
|
||||||
|
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
by: Option<Tagged<u64>>,
|
by: Option<Tagged<u64>>,
|
||||||
opposite: bool,
|
opposite: bool,
|
||||||
#[serde(rename(deserialize = "cells-only"))]
|
|
||||||
cells_only: bool,
|
cells_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,25 +45,31 @@ impl WholeStreamCommand for SubCommand {
|
||||||
"Rolls the table columns"
|
"Rolls the table columns"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
roll(args)
|
roll(args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn roll(args: CommandArgs) -> Result<ActionStream, ShellError> {
|
pub fn roll(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
let (args, input) = args.process()?;
|
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| {
|
.map(move |value| {
|
||||||
let tag = value.tag();
|
let tag = value.tag();
|
||||||
|
|
||||||
roll_by(value, &args)
|
roll_by(value, &options)
|
||||||
.unwrap_or_else(|| vec![UntaggedValue::nothing().into_value(tag)])
|
.unwrap_or_else(|| vec![UntaggedValue::nothing().into_value(tag)])
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(ReturnSuccess::value)
|
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.to_action_stream())
|
.to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roll_by(value: Value, options: &Arguments) -> Option<Vec<Value>> {
|
fn roll_by(value: Value, options: &Arguments) -> Option<Vec<Value>> {
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
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::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
|
|
||||||
use super::support::{rotate, Direction};
|
use super::support::{rotate, Direction};
|
||||||
|
|
||||||
pub struct Command;
|
pub struct Command;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
by: Option<Tagged<u64>>,
|
by: Option<Tagged<u64>>,
|
||||||
}
|
}
|
||||||
|
@ -26,22 +25,23 @@ impl WholeStreamCommand for Command {
|
||||||
"Rolls the table rows."
|
"Rolls the table rows."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
roll(args)
|
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 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)])
|
.unwrap_or_else(|| vec![UntaggedValue::nothing().into_value(&name)])
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(ReturnSuccess::value))
|
.to_output_stream())
|
||||||
.to_action_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roll_down(values: Vec<Value>, Arguments { by: ref n }: &Arguments) -> Option<Vec<Value>> {
|
fn roll_down(values: Vec<Value>, Arguments { by: ref n }: &Arguments) -> Option<Vec<Value>> {
|
||||||
|
|
|
@ -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::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
|
|
||||||
use super::support::{rotate, Direction};
|
use super::support::{rotate, Direction};
|
||||||
|
@ -26,22 +26,23 @@ impl WholeStreamCommand for SubCommand {
|
||||||
"Rolls the table rows"
|
"Rolls the table rows"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_with_actions(&self, args: CommandArgs) -> Result<ActionStream, ShellError> {
|
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
roll(args)
|
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 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)])
|
.unwrap_or_else(|| vec![UntaggedValue::nothing().into_value(&name)])
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(ReturnSuccess::value))
|
.to_output_stream())
|
||||||
.to_action_stream())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn roll_up(values: Vec<Value>, Arguments { by: ref n }: &Arguments) -> Option<Vec<Value>> {
|
fn roll_up(values: Vec<Value>, Arguments { by: ref n }: &Arguments) -> Option<Vec<Value>> {
|
||||||
|
|
|
@ -181,7 +181,7 @@ mod regex {
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(actual.err.contains("invalid regex"));
|
assert!(actual.err.contains("unclosed group"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue