diff --git a/Cargo.lock b/Cargo.lock index 0b58394..e42a4f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,13 +67,14 @@ dependencies = [ [[package]] name = "find-files" -version = "0.1.3" +version = "0.1.4" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "ignore 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -135,6 +136,14 @@ name = "memchr" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "num_cpus" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_syscall" version = "0.1.51" @@ -280,6 +289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" +"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f" diff --git a/Cargo.toml b/Cargo.toml index f30ca6d..6f82a80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "find-files" -version = "0.1.3" +version = "0.1.4" description = "Find Files (ff) utility recursively searches the files whose names match the specified RegExp pattern in the provided directory (defaults to the current directory if not provided)." authors = ["Vishal Telangre "] license = "Unlicense OR MIT" @@ -14,6 +14,7 @@ regex = "1" atty = "0.2" lazy_static = "1.1.0" ignore = "0.4" +num_cpus = "1.0" [dependencies.clap] version = "2.32" diff --git a/README.md b/README.md index ac5c848..29fb21b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ If you're a Rust programmer, download and install `ff` command using `cargo inst ``` USAGE: - ff [FLAGS] [ROOT_PATH] + ff [FLAGS] [OPTIONS] [ROOT_PATH] FLAGS: -s, --case-sensitive Search case sensitively. By default, files are @@ -38,10 +38,15 @@ FLAGS: in the search results. -V, --version Prints version information +OPTIONS: + -j, --threads The approximate number of threads to use. A value + of 0 (which is the default) results in thread + count set to available CPU cores. + ARGS: Find files whose name (path) matches this substring or the regular expression. - Path to the directory to search files inside. [default: + Path to the directory to search files inside.[default: `$PWD`] ``` ## Examples diff --git a/src/app.rs b/src/app.rs index 58bea41..cfd7f2d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -48,6 +48,13 @@ pub fn app() -> ArgMatches<'static> { .short("s") .long("case-sensitive"), ) + .arg( + Arg::with_name("threads") + .help("The approximate number of threads to use. A value of 0 (which is the default) results in thread count set to available CPU cores.") + .short("j") + .takes_value(true) + .long("threads"), + ) .get_matches() } diff --git a/src/args.rs b/src/args.rs index 8a9060b..fbd75fd 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,7 +1,9 @@ use ansi_term::Colour::Red; use atty::Stream; use clap; +use num_cpus; use regex::{Regex, RegexBuilder}; +use std::cmp; use std::path::Path; use std::process; @@ -13,6 +15,7 @@ pub struct Args { pub ignore_gitignore: bool, pub ignore_hidden: bool, pub case_sensitive: bool, + pub threads: usize, } struct ArgMatchesWrapper { @@ -90,6 +93,17 @@ impl ArgMatchesWrapper { } } + fn threads(&self) -> usize { + let matches = &self.matches; + let threads = clap::value_t!(matches.value_of("threads"), usize).unwrap_or(0); + + if threads == 0 { + cmp::min(12, num_cpus::get()) + } else { + threads + } + } + fn to_args(&self) -> Args { Args { root_path: self.root_path(), @@ -97,6 +111,7 @@ impl ArgMatchesWrapper { ignore_gitignore: self.should_ignore_gitignore_files(), case_sensitive: self.is_case_sensitive(), reg_exp: self.search_pattern(), + threads: self.threads(), } } } diff --git a/src/main.rs b/src/main.rs index 311cf43..2e3e72f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ extern crate ansi_term; extern crate atty; extern crate clap; -extern crate lazy_static; extern crate ignore; +extern crate lazy_static; +extern crate num_cpus; extern crate regex; extern crate walkdir; diff --git a/src/walker.rs b/src/walker.rs index 255659a..3336b6e 100644 --- a/src/walker.rs +++ b/src/walker.rs @@ -1,7 +1,10 @@ use crate::app; use crate::args::Args; use crate::path_printer::PathPrinter; -use ignore::WalkBuilder; +use ignore::{WalkBuilder, WalkState}; +use regex::Regex; +use std::io; +use std::process; pub struct Walker<'a> { args: &'a Args, @@ -13,34 +16,71 @@ impl<'a> Walker<'a> { } pub fn walk_and_print(&self) { + use std::sync::mpsc; + use std::thread; + let walker = WalkBuilder::new(&self.args.root_path) .hidden(self.args.ignore_hidden) .git_ignore(self.args.ignore_gitignore) - .build(); + .threads(self.args.threads) + .build_parallel(); - for path_entry in walker { - if let Ok(entry) = path_entry { - let path = entry.path().display().to_string(); - let path = self.truncate_working_dir_path(path); + let (tx, rx) = mpsc::channel::(); + let reg_exp = self.args.reg_exp.clone(); - if self.is_match(&path) { - PathPrinter::new(path, &self.args.reg_exp).print(); - } + let print_thread = thread::spawn(move || -> io::Result<()> { + for path in rx.iter() { + PathPrinter::new(path, ®_exp).print(); } - } - } + Ok(()) + }); + + walker.run(|| { + let tx = tx.clone(); + let regex = self.args.reg_exp.clone(); + + Box::new(move |path_entry| { + if let Ok(entry) = path_entry { + let path = entry.path().display().to_string(); + let path = truncate_working_dir_path(path); + + if is_match(®ex, &path) { + match tx.send(path) { + Ok(_) => WalkState::Continue, + Err(_) => WalkState::Quit, + } + } else { + WalkState::Continue + } + } else { + WalkState::Continue + } + }) + }); + + drop(tx); - fn truncate_working_dir_path(&self, path: String) -> String { - let working_dir_path = app::working_dir_path(); + if let Err(err) = print_thread.join().unwrap() { + if err.kind() != io::ErrorKind::BrokenPipe { + if let Some(err_msg) = err.into() { + eprintln!("{}", err_msg); + } - if path.contains(&working_dir_path) { - path.replace(&working_dir_path, ".") - } else { - path.clone() + process::exit(1); + } } } +} +fn is_match(reg_exp: &Regex, path: &str) -> bool { + reg_exp.is_match(path) +} + +fn truncate_working_dir_path(path: String) -> String { + let working_dir_path = app::working_dir_path(); - fn is_match(&self, path: &str) -> bool { - self.args.reg_exp.is_match(path) + if path.contains(&working_dir_path) { + path.replace(&working_dir_path, ".") + } else { + path.clone() } }