diff --git a/src/output.rs b/src/output.rs index 88664f2..a32c4d3 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,4 +1,4 @@ -use internal::{FdOptions, PathDisplay, ROOT_DIR}; +use internal::{FdOptions, PathDisplay}; use lscolors::LsColors; use std::{fs, process}; @@ -11,10 +11,18 @@ use std::os::unix::fs::PermissionsExt; use ansi_term; pub fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) { - let r = if let Some(ref ls_colors) = config.ls_colors { - print_entry_colorized(base, entry, config, ls_colors) + let path_full = base.join(entry); + + let path_to_print = if config.path_display == PathDisplay::Absolute { + &path_full } else { - print_entry_uncolorized(entry, config) + entry + }; + + let r = if let Some(ref ls_colors) = config.ls_colors { + print_entry_colorized(base, path_to_print, config, ls_colors) + } else { + print_entry_uncolorized(path_to_print, config) }; if r.is_err() { @@ -25,73 +33,39 @@ pub fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) { fn print_entry_colorized( base: &Path, - entry: &PathBuf, + path: &Path, config: &FdOptions, ls_colors: &LsColors, ) -> io::Result<()> { let default_style = ansi_term::Style::default(); - let mut component_path = base.to_path_buf(); - let stdout = io::stdout(); let mut handle = stdout.lock(); - let path_full = base.join(entry); + // Separator to use before the current component. + let mut separator = String::new(); - if config.path_display == PathDisplay::Absolute { - write!(handle, "{}", ls_colors.directory.paint(ROOT_DIR))?; - } + // Full path to the current component. + let mut component_path = base.to_path_buf(); // Traverse the path and colorize each component - for component in entry.components() { + for component in path.components() { let comp_str = component.as_os_str().to_string_lossy(); - component_path.push(Path::new(comp_str.deref())); - if let Component::RootDir = component { - // Printing the root dir would result in too many path separators on Windows. - // Note that a root dir component won't occur on Unix, because `entry` is never an - // absolute path in that case. - continue; - } + let style = get_path_style(&component_path, ls_colors).unwrap_or(&default_style); - let metadata = component_path.metadata(); - let is_directory = metadata.as_ref().map(|md| md.is_dir()).unwrap_or(false); + write!(handle, "{}{}", separator, style.paint(comp_str))?; - let style = if component_path - .symlink_metadata() - .map(|md| md.file_type().is_symlink()) - .unwrap_or(false) - { - &ls_colors.symlink - } else if is_directory { - &ls_colors.directory - } else if metadata.map(|md| is_executable(&md)).unwrap_or(false) { - &ls_colors.executable - } else { - // Look up file name - let o_style = component_path - .file_name() - .and_then(|n| n.to_str()) - .and_then(|n| ls_colors.filenames.get(n)); - - match o_style { - Some(s) => s, - None => - // Look up file extension - component_path.extension() - .and_then(|e| e.to_str()) - .and_then(|e| ls_colors.extensions.get(e)) - .unwrap_or(&default_style) - } + // Determine separator to print before next component. + separator = match component { + // Prefix needs no separator, as it is always followed by RootDir. + Component::Prefix(_) => String::new(), + // RootDir is already a separator. + Component::RootDir => String::new(), + // Everything else uses a separator that is painted the same way as the component. + _ => style.paint(path::MAIN_SEPARATOR.to_string()).to_string(), }; - - write!(handle, "{}", style.paint(comp_str))?; - - if is_directory && component_path != path_full { - let sep = path::MAIN_SEPARATOR.to_string(); - write!(handle, "{}", style.paint(sep))?; - } } if config.null_separator { @@ -101,17 +75,42 @@ fn print_entry_colorized( } } -fn print_entry_uncolorized(entry: &PathBuf, config: &FdOptions) -> io::Result<()> { - // Uncolorized output - let prefix = if config.path_display == PathDisplay::Absolute { - ROOT_DIR - } else { - "" - }; +fn print_entry_uncolorized(path: &Path, config: &FdOptions) -> io::Result<()> { let separator = if config.null_separator { "\0" } else { "\n" }; - let path_str = entry.to_string_lossy(); - write!(&mut io::stdout(), "{}{}{}", prefix, path_str, separator) + let path_str = path.to_string_lossy(); + write!(&mut io::stdout(), "{}{}", path_str, separator) +} + +fn get_path_style<'a>(path: &Path, ls_colors: &'a LsColors) -> Option<&'a ansi_term::Style> { + if path.symlink_metadata() + .map(|md| md.file_type().is_symlink()) + .unwrap_or(false) + { + return Some(&ls_colors.symlink); + } + + let metadata = path.metadata(); + + if metadata.as_ref().map(|md| md.is_dir()).unwrap_or(false) { + Some(&ls_colors.directory) + } else if metadata.map(|md| is_executable(&md)).unwrap_or(false) { + Some(&ls_colors.executable) + } else if let Some(filename_style) = + path.file_name().and_then(|n| n.to_str()).and_then(|n| { + ls_colors.filenames.get(n) + }) + { + Some(filename_style) + } else if let Some(extension_style) = + path.extension().and_then(|e| e.to_str()).and_then(|e| { + ls_colors.extensions.get(e) + }) + { + Some(extension_style) + } else { + None + } } #[cfg(unix)]