Evaluation of command arguments (#1801)

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Finish adding the baseline refactors for argument invocation

* Finish cleanup and add test

* Add missing plugin references
This commit is contained in:
Jonathan Turner
2020-05-15 20:18:24 -07:00
committed by GitHub
parent 822440d5ff
commit 076fde16dd
139 changed files with 2496 additions and 2188 deletions

View File

@ -0,0 +1,21 @@
[package]
name = "nu_plugin_parse"
version = "0.14.1"
authors = ["The Nu Project Contributors"]
edition = "2018"
description = "A string parsing plugin for Nushell"
license = "MIT"
[lib]
doctest = false
[dependencies]
nu-plugin = { path = "../nu-plugin", version = "0.14.1" }
nu-protocol = { path = "../nu-protocol", version = "0.14.1" }
nu-source = { path = "../nu-source", version = "0.14.1" }
nu-errors = { path = "../nu-errors", version = "0.14.1" }
futures = { version = "0.3", features = ["compat", "io-compat"] }
regex = "1"
[build-dependencies]
nu-build = { version = "0.14.1", path = "../nu-build" }

View File

@ -0,0 +1,3 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
nu_build::build()
}

View File

@ -0,0 +1,4 @@
mod nu;
mod parse;
pub use parse::Parse;

View File

@ -0,0 +1,7 @@
use nu_plugin::serve_plugin;
use nu_plugin_parse::Parse;
fn main() -> Result<(), Box<dyn std::error::Error>> {
serve_plugin(&mut Parse::new()?);
Ok(())
}

View File

@ -0,0 +1,153 @@
use nu_errors::ShellError;
use nu_plugin::Plugin;
use nu_protocol::{
CallInfo, Primitive, ReturnSuccess, ReturnValue, Signature, SyntaxShape, TaggedDictBuilder,
UntaggedValue, Value,
};
use crate::Parse;
use regex::Regex;
impl Plugin for Parse {
fn config(&mut self) -> Result<Signature, ShellError> {
Ok(Signature::build("parse")
.required(
"pattern",
SyntaxShape::String,
"the pattern to match. Eg) \"{foo}: {bar}\"",
)
.filter())
}
fn begin_filter(&mut self, call_info: CallInfo) -> Result<Vec<ReturnValue>, ShellError> {
if let Some(args) = call_info.args.positional {
match &args[0] {
Value {
value: UntaggedValue::Primitive(Primitive::String(s)),
tag,
} => {
let parse_pattern = parse(&s);
let parse_regex = build_regex(&parse_pattern);
self.column_names = column_names(&parse_pattern);
self.regex = Regex::new(&parse_regex).map_err(|_| {
ShellError::labeled_error(
"Could not parse regex",
"could not parse regex",
tag.span,
)
})?;
}
Value { tag, .. } => {
return Err(ShellError::labeled_error(
"Unrecognized type in params",
"unexpected value",
tag,
));
}
}
}
Ok(vec![])
}
fn filter(&mut self, input: Value) -> Result<Vec<ReturnValue>, ShellError> {
match &input.as_string() {
Ok(s) => {
let mut output = vec![];
for cap in self.regex.captures_iter(&s) {
let mut dict = TaggedDictBuilder::new(&input.tag);
for (idx, column_name) in self.column_names.iter().enumerate() {
dict.insert_untagged(
column_name,
UntaggedValue::string(cap[idx + 1].to_string()),
);
}
output.push(Ok(ReturnSuccess::Value(dict.into_value())));
}
Ok(output)
}
_ => Err(ShellError::labeled_error_with_secondary(
"Expected string input",
"expected string input",
&self.name,
"value originated here",
input.tag,
)),
}
}
}
fn parse(input: &str) -> Vec<ParseCommand> {
let mut output = vec![];
//let mut loop_input = input;
let mut loop_input = input.chars();
loop {
let mut before = String::new();
while let Some(c) = loop_input.next() {
if c == '{' {
break;
}
before.push(c);
}
if !before.is_empty() {
output.push(ParseCommand::Text(before.to_string()));
}
// Look for column as we're now at one
let mut column = String::new();
while let Some(c) = loop_input.next() {
if c == '}' {
break;
}
column.push(c);
}
if !column.is_empty() {
output.push(ParseCommand::Column(column.to_string()));
}
if before.is_empty() && column.is_empty() {
break;
}
}
output
}
fn column_names(commands: &[ParseCommand]) -> Vec<String> {
let mut output = vec![];
for command in commands {
if let ParseCommand::Column(c) = command {
output.push(c.clone());
}
}
output
}
fn build_regex(commands: &[ParseCommand]) -> String {
let mut output = String::new();
for command in commands {
match command {
ParseCommand::Text(s) => {
output.push_str(&s.replace("(", "\\("));
}
ParseCommand::Column(_) => {
output.push_str("(.*)");
}
}
}
output
}
#[derive(Debug)]
enum ParseCommand {
Text(String),
Column(String),
}

View File

@ -0,0 +1,19 @@
use nu_source::Tag;
use regex::Regex;
pub struct Parse {
pub regex: Regex,
pub name: Tag,
pub column_names: Vec<String>,
}
impl Parse {
#[allow(clippy::trivial_regex)]
pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
Ok(Parse {
regex: Regex::new("")?,
name: Tag::unknown(),
column_names: vec![],
})
}
}