Find with regex flag (#4649)

* split find functions

* find command with regex

* corrected message

* cargo fmt
This commit is contained in:
Fernando Herrera 2022-02-26 09:19:19 +00:00 committed by GitHub
parent 3eca43c0bb
commit 11bc056576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 199 additions and 112 deletions

View File

@ -5,6 +5,7 @@ use nu_protocol::{
Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span,
SyntaxShape, Value, SyntaxShape, Value,
}; };
use regex::Regex;
#[derive(Clone)] #[derive(Clone)]
pub struct Find; pub struct Find;
@ -22,6 +23,12 @@ impl Command for Find {
"the predicate to satisfy", "the predicate to satisfy",
Some('p'), Some('p'),
) )
.named(
"regex",
SyntaxShape::String,
"regex to match with",
Some('r'),
)
.rest("rest", SyntaxShape::Any, "terms to search") .rest("rest", SyntaxShape::Any, "terms to search")
.category(Category::Filters) .category(Category::Filters)
} }
@ -59,8 +66,8 @@ impl Command for Find {
}) })
}, },
Example { Example {
description: "Find the first odd value", description: "Find odd values",
example: "echo [2 4 3 6 5 8] | find --predicate { |it| ($it mod 2) == 1 }", example: "[2 4 3 6 5 8] | find --predicate { |it| ($it mod 2) == 1 }",
result: Some(Value::List { result: Some(Value::List {
vals: vec![Value::test_int(3), Value::test_int(5)], vals: vec![Value::test_int(3), Value::test_int(5)],
span: Span::test_data() span: Span::test_data()
@ -68,7 +75,7 @@ impl Command for Find {
}, },
Example { Example {
description: "Find if a service is not running", description: "Find if a service is not running",
example: "echo [[version patch]; [0.1.0 $false] [0.1.1 $true] [0.2.0 $false]] | find -p { |it| $it.patch }", example: "[[version patch]; [0.1.0 $false] [0.1.1 $true] [0.2.0 $false]] | find -p { |it| $it.patch }",
result: Some(Value::List { result: Some(Value::List {
vals: vec![Value::test_record( vals: vec![Value::test_record(
vec!["version", "patch"], vec!["version", "patch"],
@ -77,6 +84,33 @@ impl Command for Find {
span: Span::test_data() span: Span::test_data()
}), }),
}, },
Example {
description: "Find using regex",
example: r#"[abc bde arc abf] | find --regex "ab""#,
result: Some(Value::List {
vals: vec![Value::test_string("abc".to_string()), Value::test_string("abf".to_string())],
span: Span::test_data()
})
},
Example {
description: "Find using regex case insensitive",
example: r#"[aBc bde Arc abf] | find --regex "(?i)ab""#,
result: Some(Value::List {
vals: vec![Value::test_string("aBc".to_string()), Value::test_string("abf".to_string())],
span: Span::test_data()
})
},
Example {
description: "Find value in records",
example: r#"[[version name]; [0.1.0 nushell] [0.1.1 fish] [0.2.0 zsh]] | find -r "nu""#,
result: Some(Value::List {
vals: vec![Value::test_record(
vec!["version", "name"],
vec![Value::test_string("0.1.0"), Value::test_string("nushell".to_string())]
)],
span: Span::test_data()
}),
},
] ]
} }
@ -86,18 +120,61 @@ impl Command for Find {
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> {
let predicate = call.get_flag::<CaptureBlock>(engine_state, stack, "predicate")?;
let regex = call.get_flag::<String>(engine_state, stack, "regex")?;
match (regex, predicate) {
(None, Some(predicate)) => {
find_with_predicate(predicate, engine_state, stack, call, input)
}
(Some(regex), None) => find_with_regex(regex, engine_state, stack, call, input),
(None, None) => find_with_rest(engine_state, stack, call, input),
(Some(_), Some(_)) => Err(ShellError::IncompatibleParametersSingle(
"expected either predicate or regex flag, not both".to_owned(),
call.head,
)),
}
}
}
fn find_with_regex(
regex: String,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let span = call.head; let span = call.head;
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let metadata = input.metadata();
let config = stack.get_config()?; let config = stack.get_config()?;
let re = Regex::new(regex.as_str())
.map_err(|e| ShellError::UnsupportedInput(format!("incorrect regex: {}", e), span))?;
input.filter(
move |value| {
let string = value.into_string(" ", &config);
re.is_match(string.as_str())
},
ctrlc,
)
}
fn find_with_predicate(
predicate: CaptureBlock,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let span = call.head;
let ctrlc = engine_state.ctrlc.clone();
let metadata = input.metadata();
let redirect_stdout = call.redirect_stdout; let redirect_stdout = call.redirect_stdout;
let redirect_stderr = call.redirect_stderr; let redirect_stderr = call.redirect_stderr;
let engine_state = engine_state.clone();
match call.get_flag::<CaptureBlock>(&engine_state, stack, "predicate")? {
Some(predicate) => {
let capture_block = predicate; let capture_block = predicate;
let block_id = capture_block.block_id; let block_id = capture_block.block_id;
@ -134,7 +211,19 @@ impl Command for Find {
ctrlc, ctrlc,
) )
} }
None => {
fn find_with_rest(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let span = call.head;
let ctrlc = engine_state.ctrlc.clone();
let metadata = input.metadata();
let engine_state = engine_state.clone();
let config = stack.get_config()?;
let terms = call.rest::<Value>(&engine_state, stack, 0)?; let terms = call.rest::<Value>(&engine_state, stack, 0)?;
let lower_terms = terms let lower_terms = terms
.iter() .iter()
@ -154,6 +243,7 @@ impl Command for Find {
} else { } else {
value.clone() value.clone()
}; };
lower_terms.iter().any(|term| match value { lower_terms.iter().any(|term| match value {
Value::Bool { .. } Value::Bool { .. }
| Value::Int { .. } | Value::Int { .. }
@ -191,16 +281,12 @@ impl Command for Find {
}, },
ctrlc, ctrlc,
)?; )?;
match metadata { match metadata {
Some(m) => { Some(m) => Ok(pipe.into_pipeline_data_with_metadata(m, engine_state.ctrlc.clone())),
Ok(pipe.into_pipeline_data_with_metadata(m, engine_state.ctrlc.clone()))
}
None => Ok(pipe), None => Ok(pipe),
} }
} }
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -171,6 +171,7 @@ impl Clone for Value {
} }
impl Value { impl Value {
/// Converts into string values that can be changed into string natively
pub fn as_string(&self) -> Result<String, ShellError> { pub fn as_string(&self) -> Result<String, ShellError> {
match self { match self {
Value::Int { val, .. } => Ok(val.to_string()), Value::Int { val, .. } => Ok(val.to_string()),