Skip to content

Commit

Permalink
Merge pull request #1072 from jonathandturner/format_parse
Browse files Browse the repository at this point in the history
Move format/parse to core commands
  • Loading branch information
Jonathan Turner committed Dec 9, 2019
2 parents d26e938 + e98ed1b commit f9b7376
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 304 deletions.
8 changes: 0 additions & 8 deletions Cargo.toml
Expand Up @@ -151,14 +151,6 @@ path = "src/plugins/average.rs"
name = "nu_plugin_embed"
path = "src/plugins/embed.rs"

[[bin]]
name = "nu_plugin_format"
path = "src/plugins/format.rs"

[[bin]]
name = "nu_plugin_parse"
path = "src/plugins/parse.rs"

[[bin]]
name = "nu_plugin_str"
path = "src/plugins/str.rs"
Expand Down
2 changes: 2 additions & 0 deletions src/cli.rs
Expand Up @@ -286,6 +286,8 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
per_item_command(Echo),
per_item_command(Edit),
per_item_command(Insert),
per_item_command(Format),
per_item_command(Parse),
whole_stream_command(Config),
whole_stream_command(Compact),
whole_stream_command(Default),
Expand Down
4 changes: 4 additions & 0 deletions src/commands.rs
Expand Up @@ -26,6 +26,7 @@ pub(crate) mod env;
pub(crate) mod evaluate_by;
pub(crate) mod exit;
pub(crate) mod first;
pub(crate) mod format;
pub(crate) mod from_bson;
pub(crate) mod from_csv;
pub(crate) mod from_ini;
Expand Down Expand Up @@ -54,6 +55,7 @@ pub(crate) mod mv;
pub(crate) mod next;
pub(crate) mod nth;
pub(crate) mod open;
pub(crate) mod parse;
pub(crate) mod pick;
pub(crate) mod pivot;
pub(crate) mod plugin;
Expand Down Expand Up @@ -115,6 +117,7 @@ pub(crate) use env::Env;
pub(crate) use evaluate_by::EvaluateBy;
pub(crate) use exit::Exit;
pub(crate) use first::First;
pub(crate) use format::Format;
pub(crate) use from_bson::FromBSON;
pub(crate) use from_csv::FromCSV;
pub(crate) use from_ini::FromINI;
Expand Down Expand Up @@ -145,6 +148,7 @@ pub(crate) use mv::Move;
pub(crate) use next::Next;
pub(crate) use nth::Nth;
pub(crate) use open::Open;
pub(crate) use parse::Parse;
pub(crate) use pick::Pick;
pub(crate) use pivot::Pivot;
pub(crate) use prepend::Prepend;
Expand Down
121 changes: 109 additions & 12 deletions src/commands/format.rs
@@ -1,16 +1,113 @@
use crate::commands::PerItemCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
use crate::{EntriesListView, GenericView, TreeView};
use futures::stream::{self, StreamExt};
use std::sync::{Arc, Mutex};

pub(crate) fn format(input: Vec<Value>, host: &mut dyn Host) {
let last = input.len() - 1;
for (i, item) in input.iter().enumerate() {
let view = GenericView::new(item);
crate::format::print_view(&view, &mut *host);

if last != i {
outln!("");
use nu_errors::ShellError;
use nu_protocol::{CallInfo, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use std::borrow::Borrow;

use nom::{
bytes::complete::{tag, take_while},
IResult,
};

pub struct Format;

impl PerItemCommand for Format {
fn name(&self) -> &str {
"format"
}

fn signature(&self) -> Signature {
Signature::build("format").required(
"pattern",
SyntaxShape::Any,
"the pattern to output. Eg) \"{foo}: {bar}\"",
)
}

fn usage(&self) -> &str {
"Format columns into a string using a simple pattern."
}

fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
) -> Result<OutputStream, ShellError> {
//let value_tag = value.tag();
let pattern = call_info.args.expect_nth(0)?.as_string().unwrap();

let format_pattern = format(&pattern).unwrap();
let commands = format_pattern.1;

let output = if let Value {
value: UntaggedValue::Row(dict),
..
} = value
{
let mut output = String::new();

for command in &commands {
match command {
FormatCommand::Text(s) => {
output.push_str(s);
}
FormatCommand::Column(c) => {
match dict.entries.get(c) {
Some(c) => output
.push_str(&value::format_leaf(c.borrow()).plain_string(100_000)),
None => {
// This column doesn't match, so don't emit anything
}
}
}
}
}

output
} else {
String::new()
};

Ok(VecDeque::from(vec![ReturnSuccess::value(
UntaggedValue::string(output).into_untagged_value(),
)])
.to_output_stream())
}
}

#[derive(Debug)]
enum FormatCommand {
Text(String),
Column(String),
}

fn format(input: &str) -> IResult<&str, Vec<FormatCommand>> {
let mut output = vec![];

let mut loop_input = input;
loop {
let (input, before) = take_while(|c| c != '{')(loop_input)?;
if !before.is_empty() {
output.push(FormatCommand::Text(before.to_string()));
}
if input != "" {
// Look for column as we're now at one
let (input, _) = tag("{")(input)?;
let (input, column) = take_while(|c| c != '}')(input)?;
let (input, _) = tag("}")(input)?;

output.push(FormatCommand::Column(column.to_string()));
loop_input = input;
} else {
loop_input = input;
}
if loop_input == "" {
break;
}
}

Ok((loop_input, output))
}
133 changes: 133 additions & 0 deletions src/commands/parse.rs
@@ -0,0 +1,133 @@
use crate::commands::PerItemCommand;
use crate::context::CommandRegistry;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
CallInfo, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
};

use nom::{
bytes::complete::{tag, take_while},
IResult,
};
use regex::Regex;

#[derive(Debug)]
enum ParseCommand {
Text(String),
Column(String),
}

fn parse(input: &str) -> IResult<&str, Vec<ParseCommand>> {
let mut output = vec![];

let mut loop_input = input;
loop {
let (input, before) = take_while(|c| c != '{')(loop_input)?;
if !before.is_empty() {
output.push(ParseCommand::Text(before.to_string()));
}
if input != "" {
// Look for column as we're now at one
let (input, _) = tag("{")(input)?;
let (input, column) = take_while(|c| c != '}')(input)?;
let (input, _) = tag("}")(input)?;

output.push(ParseCommand::Column(column.to_string()));
loop_input = input;
} else {
loop_input = input;
}
if loop_input == "" {
break;
}
}

Ok((loop_input, 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
}
pub struct Parse;

impl PerItemCommand for Parse {
fn name(&self) -> &str {
"parse"
}

fn signature(&self) -> Signature {
Signature::build("parse").required(
"pattern",
SyntaxShape::Any,
"the pattern to match. Eg) \"{foo}: {bar}\"",
)
}

fn usage(&self) -> &str {
"Parse columns from string data using a simple pattern."
}

fn run(
&self,
call_info: &CallInfo,
_registry: &CommandRegistry,
_raw_args: &RawCommandArgs,
value: Value,
) -> Result<OutputStream, ShellError> {
//let value_tag = value.tag();
let pattern = call_info.args.expect_nth(0)?.as_string().unwrap();

let parse_pattern = parse(&pattern).unwrap();
let parse_regex = build_regex(&parse_pattern.1);

let column_names = column_names(&parse_pattern.1);
let regex = Regex::new(&parse_regex).unwrap();

let output = if let Ok(s) = value.as_string() {
let mut results = vec![];
for cap in regex.captures_iter(&s) {
let mut dict = TaggedDictBuilder::new(value.tag());

for (idx, column_name) in column_names.iter().enumerate() {
dict.insert_untagged(
column_name,
UntaggedValue::string(&cap[idx + 1].to_string()),
);
}

results.push(ReturnSuccess::value(dict.into_value()));
}

VecDeque::from(results)
} else {
VecDeque::new()
};
Ok(output.to_output_stream())
}
}
26 changes: 18 additions & 8 deletions src/evaluate/operator.rs
Expand Up @@ -29,13 +29,23 @@ fn contains(
left: &UntaggedValue,
right: &UntaggedValue,
) -> Result<bool, (&'static str, &'static str)> {
if let (
UntaggedValue::Primitive(Primitive::String(l)),
UntaggedValue::Primitive(Primitive::String(r)),
) = (left, right)
{
Ok(l.contains(r))
} else {
Err((left.type_name(), right.type_name()))
match (left, right) {
(
UntaggedValue::Primitive(Primitive::String(l)),
UntaggedValue::Primitive(Primitive::String(r)),
) => Ok(l.contains(r)),
(
UntaggedValue::Primitive(Primitive::Line(l)),
UntaggedValue::Primitive(Primitive::String(r)),
) => Ok(l.contains(r)),
(
UntaggedValue::Primitive(Primitive::String(l)),
UntaggedValue::Primitive(Primitive::Line(r)),
) => Ok(l.contains(r)),
(
UntaggedValue::Primitive(Primitive::Line(l)),
UntaggedValue::Primitive(Primitive::Line(r)),
) => Ok(l.contains(r)),
_ => Err((left.type_name(), right.type_name())),
}
}

0 comments on commit f9b7376

Please sign in to comment.