Support Multiple File Paths
If you want to support multiple file paths like this, you need to tune clap
parsing logic again.
grustep pattern file1.txt file2.txt file3.txt
# Or
grustep pattern *.txt
Note:
Using*.txt
as an argument in the command line directly may not work as expected because the shell (e.g., Bash) is responsible for expanding wildcard patterns like*.txt
. Theclap
crate in Rust won't automatically perform this expansion.
However, you can use theglob
crate to manually expand the wildcard pattern.
Add multiple paths version grep
in src/lib.rs:
pub fn grep_multi(
pattern: &str,
file_paths: &Vec<&str>,
options: &GrepOptions,
) -> Result<MatchResult, io::Error> {
if file_paths.is_empty() {
// For pipes like "cat a.txt | ..."
return grep(pattern, Path::new(""), options);
}
let mut results = MatchResult::new();
for &file_path in file_paths {
if let Ok(result) = grep(pattern, Path::new(file_path), options) {
results.extend(result);
}
}
Ok(results)
}
pub fn grep_recursive_multi(
pattern: &str,
dir_paths: &Vec<&str>,
options: &GrepOptions,
) -> Result<MatchResult, io::Error> {
let mut results = MatchResult::new();
for &dir_path in dir_paths {
if let Ok(result) = grep_recursive(pattern, Path::new(dir_path), options) {
results.extend(result);
}
}
Ok(results)
}
Modify the clap
part and call multiple path version functions in main.rs:
@@ -1,5 +1,6 @@
use clap::{App, Arg};
-use lr_grustep::{grep, grep_count, grep_recursive, GrepOptions, MatchResult};
+
+use lr_grustep::{grep_count, grep_multi, grep_recursive_multi, GrepOptions, MatchResult};
fn main() {
// Define command-line arguments using clap
@@ -8,7 +9,7 @@ fn main() {
.author("literank")
.about("A grep-like utility in Rust")
.arg(Arg::with_name("pattern").required(true).index(1).help("The pattern to search for"))
- .arg(Arg::with_name("file_path").required(false).index(2).help("The file to search in"))
+ .arg(Arg::with_name("file_paths").required(false).multiple(true).help("The files to search in"))
.arg(Arg::with_name("count").short("c").long("count").help("Only a count of selected lines is written to standard output"))
.arg(Arg::with_name("ignore-case").short("i").long("ignore-case").help("Perform case-insensitive matching"))
.arg(Arg::with_name("line-number").short("n").long("line-number").help("Each output line is preceded by its relative line number in the file, starting at line 1"))
@@ -18,16 +19,19 @@ fn main() {
// Extract command-line arguments
let pattern = matches.value_of("pattern").unwrap();
- let file_path = matches.value_of("file_path").unwrap_or("");
+ let file_paths: Vec<&str> = matches
+ .values_of("file_paths")
+ .unwrap_or_default()
+ .collect();
let options = GrepOptions {
ignore_case: matches.is_present("ignore-case"),
invert_match: matches.is_present("invert-match"),
};
- let result = if matches.is_present("recursive") && !file_path.is_empty() {
- grep_recursive(pattern, file_path.as_ref(), &options)
+ let result = if matches.is_present("recursive") && !file_paths.is_empty() {
+ grep_recursive_multi(pattern, &file_paths, &options)
} else {
- grep(pattern, file_path.as_ref(), &options)
+ grep_multi(pattern, &file_paths, &options)
};
Re-install your project:
cargo install --path .
# Or
make install
Then you can do this:
grustep -n result src/*rs
# Or
grustep -n result src/lib.rs src/main.rs
Result:
src/lib.rs:
42: let mut result = MatchResult::new();
43: result.insert(file_path.to_string_lossy().to_string(), matching_lines);
44: Ok(result)
56: let mut results = MatchResult::new();
58: if let Ok(result) = grep(pattern, Path::new(file_path), options) {
59: results.extend(result);
62: Ok(results)
65: pub fn grep_count(result: &MatchResult) -> usize {
66: result.values().map(|v| v.len()).sum()
74: let mut results = MatchResult::new();
79: let result = grep(pattern, file_path, options)?;
80: results.extend(result);
83: Ok(results)
91: let mut results = MatchResult::new();
93: if let Ok(result) = grep_recursive(pattern, Path::new(dir_path), options) {
94: results.extend(result);
97: Ok(results)
src/main.rs:
31: let result = if matches.is_present("recursive") && !file_paths.is_empty() {
37: match result {
38: Ok(result) => {
40: println!("{}", grep_count(&result));
42: print_result(&result, matches.is_present("line-number"))
51: fn print_result(result: &MatchResult, show_line_number: bool) {
53: let file_count = result.len();
55: for (file_path, items) in result {
Congratulations🎉! You've made a great CLI app in Rust now.
Complete code: https://github.com/Literank/lr_grustep
Keep Going! Keep Learning!