From cb6ccc3c5a42f54801e2509d1f8c803faf168f5d Mon Sep 17 00:00:00 2001 From: Jason Gedge Date: Mon, 25 May 2020 14:19:49 -0400 Subject: [PATCH] Improve the simplified parse form. (#1875) --- crates/nu-cli/tests/commands/parse.rs | 80 +++++++++++++++++++-------- crates/nu_plugin_parse/src/nu/mod.rs | 19 +++++-- 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/crates/nu-cli/tests/commands/parse.rs b/crates/nu-cli/tests/commands/parse.rs index aa14faf62..5c0754329 100644 --- a/crates/nu-cli/tests/commands/parse.rs +++ b/crates/nu-cli/tests/commands/parse.rs @@ -2,31 +2,67 @@ use nu_test_support::fs::Stub; use nu_test_support::playground::Playground; use nu_test_support::{nu, pipeline}; -#[test] -fn extracts_fields_from_the_given_the_pattern() { - Playground::setup("parse_test_1", |dirs, sandbox| { - sandbox.with_files(vec![Stub::FileWithContentToBeTrimmed( - "key_value_separated_arepa_ingredients.txt", - r#" - VAR1=Cheese - VAR2=JonathanParsed - VAR3=NushellSecretIngredient - "#, - )]); +mod simple { + use super::*; - let actual = nu!( - cwd: dirs.test(), pipeline( - r#" - open key_value_separated_arepa_ingredients.txt - | parse "{Name}={Value}" - | nth 1 - | get Value - | echo $it + #[test] + fn extracts_fields_from_the_given_the_pattern() { + Playground::setup("parse_test_1", |dirs, sandbox| { + sandbox.with_files(vec![Stub::FileWithContentToBeTrimmed( + "key_value_separated_arepa_ingredients.txt", + r#" + VAR1=Cheese + VAR2=JonathanParsed + VAR3=NushellSecretIngredient + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open key_value_separated_arepa_ingredients.txt + | lines + | each { echo $it | parse "{Name}={Value}" } + | nth 1 + | echo $it.Value + "# + )); + + assert_eq!(actual.out, "JonathanParsed"); + }) + } + + #[test] + fn double_open_curly_evalutes_to_a_single_curly() { + Playground::setup("parse_test_regex_2", |dirs, _sandbox| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "{abc}123" + | parse "{{abc}{name}" + | echo $it.name "# - )); + )); - assert_eq!(actual.out, "JonathanParsed"); - }) + assert_eq!(actual.out, "123"); + }) + } + + #[test] + fn properly_escapes_text() { + Playground::setup("parse_test_regex_3", |dirs, _sandbox| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "(abc)123" + | parse "(abc){name}" + | echo $it.name + "# + )); + + assert_eq!(actual.out, "123"); + }) + } } mod regex { diff --git a/crates/nu_plugin_parse/src/nu/mod.rs b/crates/nu_plugin_parse/src/nu/mod.rs index 90ba204e1..b12c50541 100644 --- a/crates/nu_plugin_parse/src/nu/mod.rs +++ b/crates/nu_plugin_parse/src/nu/mod.rs @@ -6,7 +6,7 @@ use nu_protocol::{ }; use crate::{ColumnNames, Parse}; -use regex::Regex; +use regex::{self, Regex}; impl Plugin for Parse { fn config(&mut self) -> Result { @@ -125,13 +125,18 @@ fn parse(input: &str) -> Vec { let mut output = vec![]; //let mut loop_input = input; - let mut loop_input = input.chars(); + let mut loop_input = input.chars().peekable(); loop { let mut before = String::new(); while let Some(c) = loop_input.next() { if c == '{' { - break; + // If '{{', still creating a plaintext parse command, but just for a single '{' char + if loop_input.peek() == Some(&'{') { + let _ = loop_input.next(); + } else { + break; + } } before.push(c); } @@ -188,19 +193,21 @@ impl From<&Regex> for ColumnNames { } fn build_regex(commands: &[ParseCommand]) -> String { - let mut output = String::new(); + let mut output = "(?s)\\A".to_string(); for command in commands { match command { ParseCommand::Text(s) => { - output.push_str(&s.replace("(", "\\(")); + output.push_str(®ex::escape(&s)); } ParseCommand::Column(_) => { - output.push_str("(.*)"); + output.push_str("(.*?)"); } } } + output.push_str("\\z"); + output }