Code clean-ups, documentation

This commit is contained in:
sharkdp 2017-06-05 14:14:01 +02:00
parent 0f5ede48d5
commit bf013da8ff
2 changed files with 58 additions and 25 deletions

View File

@ -1,5 +1,6 @@
use std::collections::HashMap; /// A parser for the `LS_COLORS` environment variable.
use std::collections::HashMap;
use ansi_term::{Style, Colour}; use ansi_term::{Style, Colour};
/// Maps file extensions to ANSI colors / styles. /// Maps file extensions to ANSI colors / styles.
@ -14,11 +15,19 @@ const LS_CODES: &'static [&'static str] =
"ec", "su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", "ca", "mh", "ec", "su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", "ca", "mh",
"cl"]; "cl"];
/// Defines how different file system entries should be colorized / styled.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct LsColors { pub struct LsColors {
/// ANSI Style for directories.
pub directory: Style, pub directory: Style,
/// ANSI style for symbolic links.
pub symlink: Style, pub symlink: Style,
/// A map that defines ANSI styles for different file extensions.
pub extensions: ExtensionStyles, pub extensions: ExtensionStyles,
/// A map that defines ANSI styles for different specific filenames.
pub filenames: FilenameStyles, pub filenames: FilenameStyles,
} }
@ -35,6 +44,7 @@ impl Default for LsColors {
} }
impl LsColors { impl LsColors {
/// Parse a single text-decoration code (normal, bold, italic, ...).
fn parse_decoration(code: &str) -> Option<fn(Colour) -> Style> { fn parse_decoration(code: &str) -> Option<fn(Colour) -> Style> {
match code { match code {
"0" | "00" => Some(Colour::normal), "0" | "00" => Some(Colour::normal),
@ -97,7 +107,7 @@ impl LsColors {
} }
} }
/// Add a new LS_COLORS entry /// Add a new `LS_COLORS` entry.
fn add_entry(&mut self, input: &str) { fn add_entry(&mut self, input: &str) {
let mut parts = input.trim().split('='); let mut parts = input.trim().split('=');
if let Some(pattern) = parts.next() { if let Some(pattern) = parts.next() {

View File

@ -22,14 +22,31 @@ use lscolors::LsColors;
/// Configuration options for *fd*. /// Configuration options for *fd*.
struct FdOptions { struct FdOptions {
/// Determines whether the regex search is case-sensitive or case-insensitive.
case_sensitive: bool, case_sensitive: bool,
/// Whether to search within the full file path or just the base name (filename or directory
/// name).
search_full_path: bool, search_full_path: bool,
/// Whether to ignore hidden files and directories (or not).
ignore_hidden: bool, ignore_hidden: bool,
/// Whether to respect VCS ignore files (`.gitignore`, `.ignore`, ..) or not.
read_ignore: bool, read_ignore: bool,
/// Whether to follow symlinks or not.
follow_links: bool, follow_links: bool,
/// The maximum search depth, or `None` if no maximum search depth should be set.
///
/// A depth of `1` includes all files under the current directory, a depth of `2` also includes
/// all files under subdirectories of the current directory, etc.
max_depth: Option<usize>, max_depth: Option<usize>,
colored: bool,
ls_colors: LsColors /// `None` if the output should not be colorized. Otherwise, a `LsColors` instance that defines
/// how to style different filetypes.
ls_colors: Option<LsColors>
} }
/// Print a search result to the console. /// Print a search result to the console.
@ -41,9 +58,7 @@ fn print_entry(path_root: &Path, path_entry: &Path, config: &FdOptions) {
None => return None => return
}; };
if !config.colored { if let Some(ref ls_colors) = config.ls_colors {
println!("{}", path_str);
} else {
let mut component_path = path_root.to_path_buf(); let mut component_path = path_root.to_path_buf();
// Traverse the path and colorize each component // Traverse the path and colorize each component
@ -59,15 +74,15 @@ fn print_entry(path_root: &Path, path_entry: &Path, config: &FdOptions) {
if component_path.symlink_metadata() if component_path.symlink_metadata()
.map(|md| md.file_type().is_symlink()) .map(|md| md.file_type().is_symlink())
.unwrap_or(false) { .unwrap_or(false) {
config.ls_colors.symlink ls_colors.symlink
} else if component_path.is_dir() { } else if component_path.is_dir() {
config.ls_colors.directory ls_colors.directory
} else { } else {
// Look up file name // Look up file name
let o_style = let o_style =
component_path.file_name() component_path.file_name()
.and_then(|n| n.to_str()) .and_then(|n| n.to_str())
.and_then(|n| config.ls_colors.filenames.get(n)); .and_then(|n| ls_colors.filenames.get(n));
match o_style { match o_style {
Some(s) => *s, Some(s) => *s,
@ -75,7 +90,7 @@ fn print_entry(path_root: &Path, path_entry: &Path, config: &FdOptions) {
// Look up file extension // Look up file extension
component_path.extension() component_path.extension()
.and_then(|e| e.to_str()) .and_then(|e| e.to_str())
.and_then(|e| config.ls_colors.extensions.get(e)) .and_then(|e| ls_colors.extensions.get(e))
.cloned() .cloned()
.unwrap_or_default() .unwrap_or_default()
} }
@ -89,11 +104,13 @@ fn print_entry(path_root: &Path, path_entry: &Path, config: &FdOptions) {
} }
} }
println!(); println!();
} else {
// Uncolored output:
println!("{}", path_str);
} }
} }
/// Recursively scan the given root path and search for files / pathnames /// Recursively scan the given root path and search for files / pathnames matching the pattern.
/// matching the pattern.
fn scan(root: &Path, pattern: &Regex, config: &FdOptions) { fn scan(root: &Path, pattern: &Regex, config: &FdOptions) {
let walker = WalkBuilder::new(root) let walker = WalkBuilder::new(root)
.hidden(config.ignore_hidden) .hidden(config.ignore_hidden)
@ -176,28 +193,34 @@ fn main() {
}; };
let current_dir = current_dir_buf.as_path(); let current_dir = current_dir_buf.as_path();
// 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.opt_present("sensitive") ||
pattern.chars().any(char::is_uppercase);
let colored_output = !matches.opt_present("no-color") && let colored_output = !matches.opt_present("no-color") &&
stdout_isatty(); stdout_isatty();
let ls_colors = let ls_colors =
env::var("LS_COLORS") if colored_output {
.ok() Some(
.map(|val| LsColors::from_string(&val)) env::var("LS_COLORS")
.unwrap_or_default(); .ok()
.map(|val| LsColors::from_string(&val))
.unwrap_or_default()
)
} else {
None
};
let config = FdOptions { let config = FdOptions {
// The search will be case-sensitive if the command line flag is set or case_sensitive: case_sensitive,
// if the pattern has an uppercase character (smart case).
case_sensitive: matches.opt_present("sensitive") ||
pattern.chars().any(char::is_uppercase),
search_full_path: matches.opt_present("full-path"), search_full_path: matches.opt_present("full-path"),
ignore_hidden: !matches.opt_present("hidden"), ignore_hidden: !matches.opt_present("hidden"),
read_ignore: !matches.opt_present("no-ignore"), read_ignore: !matches.opt_present("no-ignore"),
follow_links: matches.opt_present("follow"), follow_links: matches.opt_present("follow"),
max_depth: max_depth: matches.opt_str("max-depth")
matches.opt_str("max-depth") .and_then(|ds| usize::from_str_radix(&ds, 10).ok()),
.and_then(|ds| usize::from_str_radix(&ds, 10).ok()),
colored: colored_output,
ls_colors: ls_colors ls_colors: ls_colors
}; };