introducing the find command (#3971)

* introducing the `find` command

* added tests

* merged main to accomodate "rest" changes

* test fix
This commit is contained in:
Darren Schroeder 2021-08-27 03:48:41 -05:00 committed by GitHub
parent b8e2bdd6b1
commit 17ef531905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 225 additions and 0 deletions

View File

@ -0,0 +1,110 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Find;
impl WholeStreamCommand for Find {
fn name(&self) -> &str {
"find"
}
fn signature(&self) -> Signature {
Signature::build("find").rest("rest", SyntaxShape::String, "search term")
}
fn usage(&self) -> &str {
"Find text in the output of a previous command"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
find(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Search pipeline output for multiple terms",
example: r#"ls | find toml md sh"#,
result: None,
},
Example {
description: "Search strings for term(s)",
example: r#"echo Cargo.toml | find toml"#,
result: Some(vec![Value::from("Cargo.toml")]),
},
Example {
description: "Search a number list for term(s)",
example: r#"[1 2 3 4 5] | find 5"#,
result: Some(vec![UntaggedValue::int(5).into()]),
},
Example {
description: "Search string list for term(s)",
example: r#"[moe larry curly] | find l"#,
result: Some(vec![Value::from("larry"), Value::from("curly")]),
},
]
}
}
fn row_contains(row: &Dictionary, search_terms: Vec<String>) -> bool {
for term in search_terms {
for (k, v) in row.entries.iter() {
let key = k.to_string().trim().to_lowercase();
let value = v.convert_to_string().trim().to_lowercase();
if key.contains(&term) || value.contains(&term) {
return true;
}
}
}
false
}
fn find(args: CommandArgs) -> Result<OutputStream, ShellError> {
let rest: Vec<Value> = args.rest(0)?;
Ok(args
.input
.filter(move |row| match &row.value {
UntaggedValue::Row(row) => {
let sterms: Vec<String> = rest
.iter()
.map(|t| t.convert_to_string().trim().to_lowercase())
.collect();
row_contains(row, sterms)
}
UntaggedValue::Primitive(_p) => {
// eprint!("prim {}", p.type_name());
let sterms: Vec<String> = rest
.iter()
.map(|t| t.convert_to_string().trim().to_lowercase())
.collect();
let prim_string = &row.convert_to_string().trim().to_lowercase();
for term in sterms {
if prim_string.contains(&term) {
return true;
}
}
false
}
_ => false,
})
.into_output_stream())
}
#[cfg(test)]
mod tests {
use super::Find;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Find {})
}
}

View File

@ -4,6 +4,7 @@ mod def;
mod describe;
mod do_;
pub(crate) mod echo;
mod find;
mod help;
mod history;
mod if_;
@ -27,6 +28,7 @@ pub use def::Def;
pub use describe::Describe;
pub use do_::Do;
pub use echo::Echo;
pub use find::Find;
pub use help::Help;
pub use history::History;
pub use if_::If;

View File

@ -21,6 +21,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(Unalias),
whole_stream_command(Ignore),
whole_stream_command(Tutor),
whole_stream_command(Find),
// System/file operations
whole_stream_command(Exec),
whole_stream_command(Pwd),

View File

@ -0,0 +1,111 @@
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
fn find_with_list_search_with_string() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[moe larry curly] | find moe
"#
));
assert_eq!(actual.out, "moe");
}
#[test]
fn find_with_list_search_with_char() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[moe larry curly] | find l | to json
"#
));
assert_eq!(actual.out, r#"["larry","curly"]"#);
}
#[test]
fn find_with_list_search_with_number() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[1 2 3 4 5] | find 3
"#
));
assert_eq!(actual.out, "3");
}
#[test]
fn find_with_string_search_with_string() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo Cargo.toml | find toml
"#
));
assert_eq!(actual.out, "Cargo.toml");
}
#[test]
fn find_with_string_search_with_string_not_found() {
let actual = nu!(
cwd: ".", pipeline(
r#"
[moe larry curly] | find shemp
"#
));
assert_eq!(actual.out, "");
}
#[test]
fn find_with_filepath_search_with_string() {
Playground::setup("filepath_test_1", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| find arep
| to json
"#
));
assert_eq!(actual.out, r#""arepas.clu""#);
})
}
#[test]
fn find_with_filepath_search_with_multiple_patterns() {
Playground::setup("filepath_test_2", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("amigos.txt"),
EmptyFile("arepas.clu"),
EmptyFile("los.txt"),
EmptyFile("tres.txt"),
]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
ls
| get name
| find arep ami
| to json
"#
));
assert_eq!(actual.out, r#"["amigos.txt","arepas.clu"]"#);
})
}

View File

@ -14,6 +14,7 @@ mod echo;
mod empty;
mod enter;
mod every;
mod find;
mod first;
mod flatten;
mod format;