nushell/crates/nu-cli/src/commands/parse.rs

144 lines
3.5 KiB
Rust
Raw Normal View History

2019-12-09 02:57:53 +01:00
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 regex::Regex;
#[derive(Debug)]
enum ParseCommand {
Text(String),
Column(String),
}
2020-04-07 09:51:17 +02:00
fn parse(input: &str) -> Vec<ParseCommand> {
2019-12-09 02:57:53 +01:00
let mut output = vec![];
2020-04-07 09:51:17 +02:00
//let mut loop_input = input;
let mut loop_input = input.chars();
2019-12-09 02:57:53 +01:00
loop {
2020-04-07 09:51:17 +02:00
let mut before = String::new();
while let Some(c) = loop_input.next() {
if c == '{' {
break;
}
before.push(c);
}
2019-12-09 02:57:53 +01:00
if !before.is_empty() {
output.push(ParseCommand::Text(before.to_string()));
}
2020-04-07 09:51:17 +02:00
// 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);
}
2019-12-09 02:57:53 +01:00
2020-04-07 09:51:17 +02:00
if !column.is_empty() {
2019-12-09 02:57:53 +01:00
output.push(ParseCommand::Column(column.to_string()));
}
2020-04-07 09:51:17 +02:00
if before.is_empty() && column.is_empty() {
2019-12-09 02:57:53 +01:00
break;
}
}
2020-04-07 09:51:17 +02:00
output
2019-12-09 02:57:53 +01:00
}
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()?;
2020-04-07 09:51:17 +02:00
let parse_pattern = parse(&pattern);
let parse_regex = build_regex(&parse_pattern);
2019-12-09 02:57:53 +01:00
2020-04-07 09:51:17 +02:00
let column_names = column_names(&parse_pattern);
let regex = Regex::new(&parse_regex).map_err(|_| {
ShellError::labeled_error("Could not parse regex", "could not parse regex", &value.tag)
})?;
2019-12-09 02:57:53 +01:00
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()
};
Futures v0.3 upgrade (#1344) * Upgrade futures, async-stream, and futures_codec These were the last three dependencies on futures-preview. `nu` itself is now fully dependent on `futures@0.3`, as opposed to `futures-preview` alpha. Because the update to `futures` from `0.3.0-alpha.19` to `0.3.0` removed the `Stream` implementation of `VecDeque` ([changelog][changelog]), most commands that convert a `VecDeque` to an `OutputStream` broke and had to be fixed. The current solution is to now convert `VecDeque`s to a `Stream` via `futures::stream::iter`. However, it may be useful for `futures` to create an `IntoStream` trait, implemented on the `std::collections` (or really any `IntoIterator`). If something like this happends, it may be worthwhile to update the trait implementations on `OutputStream` and refactor these commands again. While upgrading `futures_codec`, we remove a custom implementation of `LinesCodec`, as one has been added to the library. There's also a small refactor to make the stream output more idiomatic. [changelog]: https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md#030---2019-11-5 * Upgrade sys & ps plugin dependencies They were previously dependent on `futures-preview`, and `nu_plugin_ps` was dependent on an old version of `futures-timer`. * Remove dependency on futures-timer from nu * Update Cargo.lock * Fix formatting * Revert fmt regressions CI is still on 1.40.0, but the latest rustfmt v1.41.0 has changes to the `val @ pattern` syntax, causing the linting job to fail. * Fix clippy warnings
2020-02-06 04:46:48 +01:00
Ok(output.into())
2019-12-09 02:57:53 +01:00
}
}