Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1072 from jonathandturner/format_parse
Move format/parse to core commands
- Loading branch information
Showing
8 changed files
with
266 additions
and
304 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.