fd/src/main.rs

153 lines
4.8 KiB
Rust
Raw Normal View History

2017-05-12 13:02:20 +02:00
extern crate ansi_term;
2017-06-10 17:30:48 +02:00
extern crate atty;
2017-10-14 18:04:11 +02:00
#[macro_use]
extern crate clap;
2017-05-15 22:38:34 +02:00
extern crate ignore;
2017-10-14 18:04:11 +02:00
#[macro_use]
extern crate lazy_static;
2017-10-15 03:25:56 +02:00
#[cfg(all(unix, not(target_os = "redox")))]
extern crate libc;
extern crate num_cpus;
2017-10-14 18:04:11 +02:00
extern crate regex;
2017-10-14 18:30:10 +02:00
extern crate regex_syntax;
2017-06-05 11:56:39 +02:00
pub mod fshelper;
2017-10-14 18:04:11 +02:00
pub mod lscolors;
2017-10-04 14:31:08 +02:00
mod app;
2017-10-14 18:04:11 +02:00
mod exec;
2017-10-10 08:01:17 +02:00
mod internal;
mod output;
mod walk;
2017-05-12 11:50:03 +02:00
use std::env;
2017-05-12 19:34:31 +02:00
use std::error::Error;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time;
2017-05-12 11:50:03 +02:00
2017-06-10 17:30:48 +02:00
use atty::Stream;
2017-10-10 08:01:17 +02:00
use regex::RegexBuilder;
2017-05-12 13:02:20 +02:00
2017-10-14 18:04:11 +02:00
use exec::TokenizedCommand;
use internal::{error, pattern_has_uppercase_char, FdOptions, PathDisplay, ROOT_DIR};
2017-06-05 11:56:39 +02:00
use lscolors::LsColors;
2017-10-10 08:01:17 +02:00
use walk::FileType;
2017-05-12 11:50:03 +02:00
fn main() {
2017-10-04 14:31:08 +02:00
let matches = app::build_app().get_matches();
2017-05-12 11:50:03 +02:00
// Get the search pattern
let empty_pattern = String::new();
let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);
2017-05-12 11:50:03 +02:00
// Get the current working directory
let current_dir = Path::new(".");
// .is_dir() is not guarandteed to be intuitively correct for "." and ".."
if let Err(_) = current_dir.canonicalize() {
error("Error: could not get current directory.");
}
2017-05-12 11:50:03 +02:00
// Get the root directory for the search
let mut root_dir_buf = match matches.value_of("path") {
Some(path) => PathBuf::from(path),
None => current_dir.to_path_buf(),
};
if let Err(_) = root_dir_buf.canonicalize() {
2017-10-12 08:01:51 +02:00
error(&format!(
"Error: '{}' is not a directory.",
root_dir_buf.to_string_lossy()
));
}
let path_display = if matches.is_present("absolute-path") || root_dir_buf.is_absolute() {
PathDisplay::Absolute
} else {
PathDisplay::Relative
};
if path_display == PathDisplay::Absolute && root_dir_buf.is_relative() {
root_dir_buf = fshelper::absolute_path(root_dir_buf.as_path()).unwrap();
}
let root_dir = root_dir_buf.as_path();
2017-06-05 14:14:01 +02:00
// The search will be case-sensitive if the command line flag is set or
// if the pattern has an uppercase character (smart case).
let case_sensitive = !matches.is_present("ignore-case") &&
(matches.is_present("case-sensitive") || pattern_has_uppercase_char(pattern));
2017-06-05 14:14:01 +02:00
let colored_output = match matches.value_of("color") {
Some("always") => true,
Some("never") => false,
2017-10-12 08:01:51 +02:00
_ => atty::is(Stream::Stdout),
};
2017-10-12 08:01:51 +02:00
let ls_colors = if colored_output {
Some(
env::var("LS_COLORS")
.ok()
.map(|val| LsColors::from_string(&val))
.unwrap_or_default(),
)
} else {
None
};
2017-10-14 20:04:04 +02:00
let command = matches.value_of("exec").map(|x| TokenizedCommand::new(&x));
2017-10-14 18:04:11 +02:00
let config = FdOptions {
2017-10-14 18:04:11 +02:00
case_sensitive,
2017-10-12 08:01:51 +02:00
search_full_path: matches.is_present("full-path"),
ignore_hidden: !(matches.is_present("hidden") ||
matches.occurrences_of("rg-alias-hidden-ignore") >= 2),
read_ignore: !(matches.is_present("no-ignore") ||
matches.is_present("rg-alias-hidden-ignore")),
follow_links: matches.is_present("follow"),
null_separator: matches.is_present("null_separator"),
max_depth: matches.value_of("depth").and_then(|n| {
usize::from_str_radix(n, 10).ok()
}),
threads: std::cmp::max(
matches
.value_of("threads")
.and_then(|n| usize::from_str_radix(n, 10).ok())
.unwrap_or_else(num_cpus::get),
1,
),
max_buffer_time: matches
.value_of("max-buffer-time")
.and_then(|n| u64::from_str_radix(n, 10).ok())
.map(time::Duration::from_millis),
2017-10-15 15:37:48 +02:00
path_display,
2017-10-14 18:04:11 +02:00
ls_colors,
2017-10-12 08:01:51 +02:00
file_type: match matches.value_of("file-type") {
Some("f") | Some("file") => FileType::RegularFile,
Some("d") |
Some("directory") => FileType::Directory,
Some("l") | Some("symlink") => FileType::SymLink,
_ => FileType::Any,
},
extension: matches.value_of("extension").map(|e| {
e.trim_left_matches('.').to_lowercase()
}),
2017-10-14 20:04:04 +02:00
command,
};
2017-05-12 13:32:30 +02:00
// If base_dir is ROOT_DIR, then root_dir must be absolute.
// Otherwise root_dir/entry cannot be turned into an existing relative path from base_dir.
//
// We utilize ROOT_DIR to avoid resolving the components of root_dir.
let base_dir_buf = match config.path_display {
PathDisplay::Relative => current_dir.to_path_buf(),
PathDisplay::Absolute => PathBuf::from(ROOT_DIR),
};
let base_dir = base_dir_buf.as_path();
match RegexBuilder::new(pattern)
2017-10-12 08:01:51 +02:00
.case_insensitive(!config.case_sensitive)
.dot_matches_new_line(true)
2017-10-12 08:01:51 +02:00
.build() {
Ok(re) => walk::scan(root_dir, Arc::new(re), base_dir, Arc::new(config)),
2017-10-12 08:01:51 +02:00
Err(err) => error(err.description()),
2017-05-12 11:50:03 +02:00
}
}