Structify file_name -> FileName

This turns `file` into `self.file` and `colours` into `self.colours`, but it means we don’t need to pass arguments everywhere, which will be more of a problem the more functions there are.

Most of the code has just been indented.
This commit is contained in:
Benjamin Sago 2017-05-01 15:37:02 +01:00
parent 79feeba67d
commit 05a0a5e199
4 changed files with 140 additions and 128 deletions

View File

@ -101,7 +101,7 @@ use output::colours::Colours;
use output::column::{Alignment, Column, Columns, SizeFormat}; use output::column::{Alignment, Column, Columns, SizeFormat};
use output::cell::{TextCell, DisplayWidth}; use output::cell::{TextCell, DisplayWidth};
use output::tree::TreeTrunk; use output::tree::TreeTrunk;
use output::file_name::filename; use output::file_name::FileName;
/// With the **Details** view, the output gets formatted into columns, with /// With the **Details** view, the output gets formatted into columns, with
@ -307,7 +307,7 @@ impl Details {
let mut files = Vec::new(); let mut files = Vec::new();
let mut errors = egg.errors; let mut errors = egg.errors;
let filename = filename(&egg.file, &self.colours, true, self.classify); let filename = FileName::new(&egg.file, &self.colours).file_name(true, self.classify);
let mut width = filename.width(); let mut width = filename.width();
if egg.file.dir.is_none() { if egg.file.dir.is_none() {
@ -458,7 +458,7 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> {
} }
pub fn filename_cell(&self, file: File, links: bool) -> TextCell { pub fn filename_cell(&self, file: File, links: bool) -> TextCell {
let filename = filename(&file, &self.opts.colours, links, self.opts.classify); let filename = FileName::new(&file, &self.opts.colours).file_name(links, self.opts.classify);
let mut width = filename.width(); let mut width = filename.width();
if file.dir.is_none() { if file.dir.is_none() {

View File

@ -5,141 +5,153 @@ use output::Colours;
use output::cell::TextCellContents; use output::cell::TextCellContents;
pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents { pub struct FileName<'a, 'dir: 'a> {
let mut bits = Vec::new(); file: &'a File<'dir>,
colours: &'a Colours,
}
// TODO: This long function could do with some splitting up. impl<'a, 'dir> FileName<'a, 'dir> {
pub fn new(file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> {
if file.dir.is_none() { FileName {
if let Some(parent) = file.path.parent() { file: file,
let coconut = parent.components().count(); colours: colours,
if coconut == 1 && parent.has_root() {
bits.push(colours.symlink_path.paint("/"));
}
else if coconut >= 1 {
bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
bits.push(colours.symlink_path.paint("/"));
}
} }
} }
if !file.name.is_empty() { pub fn file_name(&self, links: bool, classify: bool) -> TextCellContents {
for bit in coloured_file_name(file, colours) { let mut bits = Vec::new();
bits.push(bit);
}
}
if links && file.is_link() { if self.file.dir.is_none() {
match file.link_target() { if let Some(parent) = self.file.path.parent() {
FileTarget::Ok(target) => { let coconut = parent.components().count();
bits.push(Style::default().paint(" "));
bits.push(colours.punctuation.paint("->"));
bits.push(Style::default().paint(" "));
if let Some(parent) = target.path.parent() { if coconut == 1 && parent.has_root() {
let coconut = parent.components().count(); bits.push(self.colours.symlink_path.paint("/"));
if coconut == 1 && parent.has_root() {
bits.push(colours.symlink_path.paint("/"));
}
else if coconut >= 1 {
bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
bits.push(colours.symlink_path.paint("/"));
}
} }
else if coconut >= 1 {
if !target.name.is_empty() { bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string()));
bits.push(file_colour(colours, &target).paint(target.name)); bits.push(self.colours.symlink_path.paint("/"));
} }
},
FileTarget::Broken(broken_path) => {
bits.push(Style::default().paint(" "));
bits.push(colours.broken_arrow.paint("->"));
bits.push(Style::default().paint(" "));
bits.push(colours.broken_filename.paint(broken_path.display().to_string()));
},
FileTarget::Err(_) => {
// Do nothing -- the error gets displayed on the next line
} }
} }
} else if classify {
if file.is_executable_file() {
bits.push(Style::default().paint("*"));
} else if file.is_directory() {
bits.push(Style::default().paint("/"));
} else if file.is_pipe() {
bits.push(Style::default().paint("|"));
} else if file.is_link() {
bits.push(Style::default().paint("@"));
} else if file.is_socket() {
bits.push(Style::default().paint("="));
}
}
bits.into() if !self.file.name.is_empty() {
} for bit in self.coloured_file_name() {
bits.push(bit);
/// Returns at least one ANSI-highlighted string representing this files
/// name using the given set of colours.
///
/// Ordinarily, this will be just one string: the files complete name,
/// coloured according to its file type. If the name contains control
/// characters such as newlines or escapes, though, we cant just print them
/// to the screen directly, because then therell be newlines in weird places.
///
/// So in that situation, those characters will be escaped and highlighted in
/// a different colour.
fn coloured_file_name<'a>(file: &File, colours: &Colours) -> Vec<ANSIString<'a>> {
let colour = file_colour(colours, file);
let mut bits = Vec::new();
if file.name.chars().all(|c| c >= 0x20 as char) {
bits.push(colour.paint(file.name.clone()));
}
else {
for c in file.name.chars() {
// The `escape_default` method on `char` is *almost* what we want here, but
// it still escapes non-ASCII UTF-8 characters, which are still printable.
if c >= 0x20 as char {
// TODO: This allocates way too much,
// hence the `all` check above.
let mut s = String::new();
s.push(c);
bits.push(colour.paint(s));
} else {
let s = c.escape_default().collect::<String>();
bits.push(colours.control_char.paint(s));
} }
} }
if links && self.file.is_link() {
match self.file.link_target() {
FileTarget::Ok(target) => {
bits.push(Style::default().paint(" "));
bits.push(self.colours.punctuation.paint("->"));
bits.push(Style::default().paint(" "));
if let Some(parent) = target.path.parent() {
let coconut = parent.components().count();
if coconut == 1 && parent.has_root() {
bits.push(self.colours.symlink_path.paint("/"));
}
else if coconut >= 1 {
bits.push(self.colours.symlink_path.paint(parent.to_string_lossy().to_string()));
bits.push(self.colours.symlink_path.paint("/"));
}
}
if !target.name.is_empty() {
bits.push(FileName::new(&target, self.colours).file_colour().paint(target.name));
}
},
FileTarget::Broken(broken_path) => {
bits.push(Style::default().paint(" "));
bits.push(self.colours.broken_arrow.paint("->"));
bits.push(Style::default().paint(" "));
bits.push(self.colours.broken_filename.paint(broken_path.display().to_string()));
},
FileTarget::Err(_) => {
// Do nothing -- the error gets displayed on the next line
}
}
} else if classify {
if self.file.is_executable_file() {
bits.push(Style::default().paint("*"));
} else if self.file.is_directory() {
bits.push(Style::default().paint("/"));
} else if self.file.is_pipe() {
bits.push(Style::default().paint("|"));
} else if self.file.is_link() {
bits.push(Style::default().paint("@"));
} else if self.file.is_socket() {
bits.push(Style::default().paint("="));
}
}
bits.into()
} }
bits /// Returns at least one ANSI-highlighted string representing this files
} /// name using the given set of colours.
///
/// Ordinarily, this will be just one string: the files complete name,
/// coloured according to its file type. If the name contains control
/// characters such as newlines or escapes, though, we cant just print them
/// to the screen directly, because then therell be newlines in weird places.
///
/// So in that situation, those characters will be escaped and highlighted in
/// a different colour.
fn coloured_file_name<'unused>(&self) -> Vec<ANSIString<'unused>> {
let colour = self.file_colour();
let mut bits = Vec::new();
pub fn file_colour(colours: &Colours, file: &File) -> Style { if self.file.name.chars().all(|c| c >= 0x20 as char) {
match file { bits.push(colour.paint(self.file.name.clone()));
f if f.is_directory() => colours.filetypes.directory, }
f if f.is_executable_file() => colours.filetypes.executable, else {
f if f.is_link() => colours.filetypes.symlink, for c in self.file.name.chars() {
f if f.is_pipe() => colours.filetypes.pipe, // The `escape_default` method on `char` is *almost* what we want here, but
f if f.is_char_device() // it still escapes non-ASCII UTF-8 characters, which are still printable.
| f.is_block_device() => colours.filetypes.device,
f if f.is_socket() => colours.filetypes.socket, if c >= 0x20 as char {
f if !f.is_file() => colours.filetypes.special, // TODO: This allocates way too much,
f if f.is_immediate() => colours.filetypes.immediate, // hence the `all` check above.
f if f.is_image() => colours.filetypes.image, let mut s = String::new();
f if f.is_video() => colours.filetypes.video, s.push(c);
f if f.is_music() => colours.filetypes.music, bits.push(colour.paint(s));
f if f.is_lossless() => colours.filetypes.lossless, } else {
f if f.is_crypto() => colours.filetypes.crypto, let s = c.escape_default().collect::<String>();
f if f.is_document() => colours.filetypes.document, bits.push(self.colours.control_char.paint(s));
f if f.is_compressed() => colours.filetypes.compressed, }
f if f.is_temp() => colours.filetypes.temp, }
f if f.is_compiled() => colours.filetypes.compiled, }
_ => colours.filetypes.normal,
bits
}
pub fn file_colour(&self) -> Style {
match self.file {
f if f.is_directory() => self.colours.filetypes.directory,
f if f.is_executable_file() => self.colours.filetypes.executable,
f if f.is_link() => self.colours.filetypes.symlink,
f if f.is_pipe() => self.colours.filetypes.pipe,
f if f.is_char_device()
| f.is_block_device() => self.colours.filetypes.device,
f if f.is_socket() => self.colours.filetypes.socket,
f if !f.is_file() => self.colours.filetypes.special,
f if f.is_immediate() => self.colours.filetypes.immediate,
f if f.is_image() => self.colours.filetypes.image,
f if f.is_video() => self.colours.filetypes.video,
f if f.is_music() => self.colours.filetypes.music,
f if f.is_lossless() => self.colours.filetypes.lossless,
f if f.is_crypto() => self.colours.filetypes.crypto,
f if f.is_document() => self.colours.filetypes.document,
f if f.is_compressed() => self.colours.filetypes.compressed,
f if f.is_temp() => self.colours.filetypes.temp,
f if f.is_compiled() => self.colours.filetypes.compiled,
_ => self.colours.filetypes.normal,
}
} }
} }

View File

@ -5,7 +5,7 @@ use term_grid as grid;
use fs::File; use fs::File;
use output::DisplayWidth; use output::DisplayWidth;
use output::colours::Colours; use output::colours::Colours;
use output::file_name::filename; use output::file_name::FileName;
#[derive(PartialEq, Debug, Copy, Clone)] #[derive(PartialEq, Debug, Copy, Clone)]
@ -29,7 +29,7 @@ impl Grid {
grid.reserve(files.len()); grid.reserve(files.len());
for file in files.iter() { for file in files.iter() {
let filename = filename(file, &self.colours, false, self.classify); let filename = FileName::new(file, &self.colours).file_name(false, self.classify);
let mut width = filename.width(); let mut width = filename.width();
if file.dir.is_none() { if file.dir.is_none() {
@ -50,7 +50,7 @@ impl Grid {
else { else {
// File names too long for a grid - drop down to just listing them! // File names too long for a grid - drop down to just listing them!
for file in files.iter() { for file in files.iter() {
writeln!(w, "{}", filename(file, &self.colours, false, self.classify).strings())?; writeln!(w, "{}", FileName::new(file, &self.colours).file_name(false, self.classify).strings())?;
} }
Ok(()) Ok(())
} }

View File

@ -4,7 +4,7 @@ use ansi_term::ANSIStrings;
use fs::File; use fs::File;
use output::file_name::filename; use output::file_name::FileName;
use super::colours::Colours; use super::colours::Colours;
@ -18,7 +18,7 @@ pub struct Lines {
impl Lines { impl Lines {
pub fn view<W: Write>(&self, files: Vec<File>, w: &mut W) -> IOResult<()> { pub fn view<W: Write>(&self, files: Vec<File>, w: &mut W) -> IOResult<()> {
for file in files { for file in files {
writeln!(w, "{}", ANSIStrings(&filename(&file, &self.colours, true, self.classify)))?; writeln!(w, "{}", ANSIStrings(&FileName::new(&file, &self.colours).file_name(true, self.classify)))?;
} }
Ok(()) Ok(())
} }