Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
license = "Unlicense OR MIT"
Expand All @@ -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"
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ If you're a Rust programmer, download and install `ff` command using `cargo inst

```
USAGE:
ff [FLAGS] <PATTERN> [ROOT_PATH]
ff [FLAGS] [OPTIONS] <PATTERN> [ROOT_PATH]

FLAGS:
-s, --case-sensitive Search case sensitively. By default, files are
Expand All @@ -38,10 +38,15 @@ FLAGS:
in the search results.
-V, --version Prints version information

OPTIONS:
-j, --threads <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:
<PATTERN> Find files whose name (path) matches this substring or
the regular expression.
<ROOT_PATH> Path to the directory to search files inside. [default:
<ROOT_PATH> Path to the directory to search files inside.[default:
`$PWD`]
```
## Examples
Expand Down
7 changes: 7 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down
15 changes: 15 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -13,6 +15,7 @@ pub struct Args {
pub ignore_gitignore: bool,
pub ignore_hidden: bool,
pub case_sensitive: bool,
pub threads: usize,
}

struct ArgMatchesWrapper {
Expand Down Expand Up @@ -90,13 +93,25 @@ 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(),
ignore_hidden: self.should_ignore_hidden_files(),
ignore_gitignore: self.should_ignore_gitignore_files(),
case_sensitive: self.is_case_sensitive(),
reg_exp: self.search_pattern(),
threads: self.threads(),
}
}
}
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
78 changes: 59 additions & 19 deletions src/walker.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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::<String>();
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, &reg_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(&regex, &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()
}
}