summaryrefslogtreecommitdiffstats
path: root/src/output
diff options
context:
space:
mode:
authorBen S <ogham@bsago.me>2015-05-11 23:28:01 +0100
committerBen S <ogham@bsago.me>2015-05-11 23:28:01 +0100
commitdc6599b1b344370b851b9e9ca32ef62ba7d9a1c8 (patch)
tree47f1277d34621b1e93f38d4e487ed6ad92f9540e /src/output
parentbc844a3843cdc56630cf87c8f3ba1f2d6aa4152f (diff)
Turn File into simply a data source
And move all the rendering, ansi_term, colourful stuff into the output modules, which is the only place they should be used!
Diffstat (limited to 'src/output')
-rw-r--r--src/output/details.rs220
-rw-r--r--src/output/lines.rs3
-rw-r--r--src/output/mod.rs30
3 files changed, 230 insertions, 23 deletions
diff --git a/src/output/details.rs b/src/output/details.rs
index ebe43ae..979c99d 100644
--- a/src/output/details.rs
+++ b/src/output/details.rs
@@ -2,12 +2,23 @@ use colours::Colours;
use column::{Alignment, Column, Cell};
use feature::Attribute;
use dir::Dir;
-use file::File;
-use options::{Columns, FileFilter, RecurseOptions};
-use users::OSUsers;
+use file::{Blocks, File, Git, GitStatus, Group, Inode, Links, Permissions, Size, Time, User};
+use options::{Columns, FileFilter, RecurseOptions, SizeFormat};
+use users::{OSUsers, Users};
+
+use super::filename;
+
+use ansi_term::{ANSIString, ANSIStrings, Style};
+use ansi_term::Style::Plain;
+use ansi_term::Colour::Fixed;
use locale;
+use number_prefix::{binary_prefix, decimal_prefix, Prefixed, Standalone, PrefixNames};
+
+use datetime::local::{LocalDateTime, DatePiece};
+use datetime::format::{DateFormat};
+
/// With the **Details** view, the output gets formatted into columns, with
/// each `Column` object showing some piece of information about the file,
/// such as its size, or its permissions.
@@ -45,7 +56,7 @@ impl Details {
pub fn view(&self, dir: Option<&Dir>, files: &[File]) {
// First, transform the Columns object into a vector of columns for
// the current directory.
- let mut table = Table::with_options(self.colours, self.columns.for_dir(dir));
+ let mut table = Table::with_options(self.colours, self.columns.for_dir(dir), self.columns.size_format);
if self.header { table.add_header() }
// Then add files to the table and print it out.
@@ -109,21 +120,26 @@ struct Row {
/// A **Table** object gets built up by the view as it lists files and
/// directories.
struct Table {
- columns: Vec<Column>,
- users: OSUsers,
- locale: UserLocale,
- rows: Vec<Row>,
- colours: Colours,
+ columns: Vec<Column>,
+ rows: Vec<Row>,
+
+ local: Locals,
+ colours: Colours,
}
impl Table {
/// Create a new, empty Table object, setting the caching fields to their
/// empty states.
- fn with_options(colours: Colours, columns: Vec<Column>) -> Table {
+ fn with_options(colours: Colours, columns: Vec<Column>, size: SizeFormat) -> Table {
Table {
columns: columns,
- users: OSUsers::empty_cache(),
- locale: UserLocale::new(),
+ local: Locals {
+ time: locale::Time::load_user_locale().unwrap_or_else(|_| locale::Time::english()),
+ numeric: locale::Numeric::load_user_locale().unwrap_or_else(|_| locale::Numeric::english()),
+ users: OSUsers::empty_cache(),
+ current_year: LocalDateTime::now().year(),
+ size_format: size,
+ },
rows: Vec::new(),
colours: colours,
}
@@ -149,16 +165,30 @@ impl Table {
/// this file, per-column.
fn cells_for_file(&mut self, file: &File) -> Vec<Cell> {
self.columns.clone().iter()
- .map(|c| file.display(c, &self.colours, &mut self.users, &self.locale))
+ .map(|c| self.display(file, c))
.collect()
}
+ fn display(&mut self, file: &File, column: &Column) -> Cell {
+ match *column {
+ Column::Permissions => file.permissions().render(&self.colours, &mut self.local),
+ Column::FileSize(f) => file.size().render(&self.colours, &mut self.local),
+ Column::Timestamp(t, y) => file.timestamp(t).render(&self.colours, &mut self.local),
+ Column::HardLinks => file.links().render(&self.colours, &mut self.local),
+ Column::Inode => file.inode().render(&self.colours, &mut self.local),
+ Column::Blocks => file.blocks().render(&self.colours, &mut self.local),
+ Column::User => file.user().render(&self.colours, &mut self.local),
+ Column::Group => file.group().render(&self.colours, &mut self.local),
+ Column::GitStatus => file.git_status().render(&self.colours, &mut self.local),
+ }
+ }
+
/// Get the cells for the given file, and add the result to the table.
fn add_file(&mut self, file: &File, depth: usize, last: bool) {
let row = Row {
depth: depth,
cells: self.cells_for_file(file),
- name: file.file_name_view(&self.colours),
+ name: filename(file, &self.colours),
last: last,
attrs: file.xattrs.clone(),
children: file.this.is_some(),
@@ -251,16 +281,162 @@ impl TreePart {
}
}
-pub struct UserLocale {
- pub time: locale::Time,
- pub numeric: locale::Numeric,
+pub trait Render {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell;
}
-impl UserLocale {
- pub fn new() -> UserLocale {
- UserLocale {
- time: locale::Time::load_user_locale().unwrap_or_else(|_| locale::Time::english()),
- numeric: locale::Numeric::load_user_locale().unwrap_or_else(|_| locale::Numeric::english()),
+impl Render for Permissions {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ let c = colours.perms;
+ let bit = |bit, chr: &'static str, style: Style| {
+ if bit { style.paint(chr) } else { colours.punctuation.paint("-") }
+ };
+
+ let string = ANSIStrings( &[
+ //self.file_type.render(colours, local),
+ bit(self.user_read, "r", c.user_read),
+ bit(self.user_write, "w", c.user_write),
+ bit(self.user_execute, "x", c.user_execute_file),
+ bit(self.group_read, "r", c.group_read),
+ bit(self.group_write, "w", c.group_write),
+ bit(self.group_execute, "x", c.group_execute),
+ bit(self.other_read, "r", c.other_read),
+ bit(self.other_write, "w", c.other_write),
+ bit(self.other_execute, "x", c.other_execute),
+ if self.attribute { c.attribute.paint("@") } else { Plain.paint(" ") },
+ ]).to_string();
+
+ Cell {
+ text: string,
+ length: 11,
+ }
+ }
+}
+
+impl Render for Links {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ let style = if self.multiple { colours.links.multi_link_file }
+ else { colours.links.normal };
+ Cell::paint(style, &local.numeric.format_int(self.count))
+ }
+}
+
+impl Render for Blocks {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ match self {
+ Blocks::Some(blocks) => Cell::paint(colours.blocks, &blocks.to_string()),
+ Blocks::None => Cell::paint(colours.punctuation, "-"),
+ }
+ }
+}
+
+impl Render for Inode {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ Cell::paint(colours.inode, &self.0.to_string())
+ }
+}
+
+impl Render for Size {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ if let Size::Some(offset) = self {
+ let result = match local.size_format {
+ SizeFormat::DecimalBytes => decimal_prefix(offset as f64),
+ SizeFormat::BinaryBytes => binary_prefix(offset as f64),
+ SizeFormat::JustBytes => return Cell::paint(colours.size.numbers, &local.numeric.format_int(offset)),
+ };
+
+ match result {
+ Standalone(bytes) => Cell::paint(colours.size.numbers, &*bytes.to_string()),
+ Prefixed(prefix, n) => {
+ let number = if n < 10f64 { local.numeric.format_float(n, 1) } else { local.numeric.format_int(n as isize) };
+ let symbol = prefix.symbol();
+
+ Cell {
+ text: ANSIStrings( &[ colours.size.unit.paint(&number[..]), colours.size.unit.paint(symbol) ]).to_string(),
+ length: number.len() + symbol.len(),
+ }
+ }
+ }
+ }
+ else {
+ Cell::paint(colours.punctuation, "-")
}
}
}
+
+impl Render for Time {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ let date = LocalDateTime::at(self.0);
+
+ let format = if date.year() == local.current_year {
+ DateFormat::parse("{2>:D} {:M} {2>:h}:{02>:m}").unwrap()
+ }
+ else {
+ DateFormat::parse("{2>:D} {:M} {5>:Y}").unwrap()
+ };
+
+ Cell::paint(colours.date, &format.format(date, &local.time))
+ }
+}
+
+impl Render for Git {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ let render_char = |chr| {
+ match chr {
+ GitStatus::NotModified => colours.punctuation.paint("-"),
+ GitStatus::New => colours.git.renamed.paint("N"),
+ GitStatus::Modified => colours.git.renamed.paint("M"),
+ GitStatus::Deleted => colours.git.renamed.paint("D"),
+ GitStatus::Renamed => colours.git.renamed.paint("R"),
+ GitStatus::TypeChange => colours.git.renamed.paint("T"),
+ }
+ };
+
+ Cell {
+ text: ANSIStrings(&[ render_char(self.staged), render_char(self.unstaged) ]).to_string(),
+ length: 2,
+ }
+ }
+}
+
+impl Render for User {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ let user_name = match local.users.get_user_by_uid(self.0) {
+ Some(user) => user.name,
+ None => self.0.to_string(),
+ };
+
+ let style = if local.users.get_current_uid() == self.0 { colours.users.user_you }
+ else { colours.users.user_someone_else };
+ Cell::paint(style, &*user_name)
+ }
+}
+
+impl Render for Group {
+ fn render(self, colours: &Colours, local: &mut Locals) -> Cell {
+ let mut style = colours.users.group_not_yours;
+
+ let group_name = match local.users.get_group_by_gid(self.0) {
+ Some(group) => {
+ let current_uid = local.users.get_current_uid();
+ if let Some(current_user) = local.users.get_user_by_uid(current_uid) {
+ if current_user.primary_group == group.gid || group.members.contains(&current_user.name) {
+ style = colours.users.group_yours;
+ }
+ }
+ group.name
+ },
+ None => self.0.to_string(),
+ };
+
+ Cell::paint(style, &*group_name)
+ }
+}
+
+pub struct Locals {
+ pub time: locale::Time,
+ pub numeric: locale::Numeric,
+ pub users: OSUsers,
+ pub size_format: SizeFormat,
+ pub current_year: i64,
+}
diff --git a/src/output/lines.rs b/src/output/lines.rs
index b61494f..bd1fcdc 100644
--- a/src/output/lines.rs
+++ b/src/output/lines.rs
@@ -1,5 +1,6 @@
use colours::Colours;
use file::File;
+use super::filename;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Lines {
@@ -10,7 +11,7 @@ pub struct Lines {
impl Lines {
pub fn view(&self, files: &[File]) {
for file in files {
- println!("{}", file.file_name_view(&self.colours));
+ println!("{}", filename(file, &self.colours));
}
}
}
diff --git a/src/output/mod.rs b/src/output/mod.rs
index 8606d23..ebb11ce 100644
--- a/src/output/mod.rs
+++ b/src/output/mod.rs
@@ -2,6 +2,36 @@ mod grid;
pub mod details;
mod lines;
+use colours::Colours;
+use file::File;
+use filetype::file_colour;
+use ansi_term::ANSIStrings;
+
pub use self::grid::Grid;
pub use self::details::Details;
pub use self::lines::Lines;
+
+pub fn filename(file: &File, colours: &Colours) -> String {
+ if file.is_link() {
+ symlink_filename(file, colours)
+ }
+ else {
+ let style = file_colour(colours, file);
+ style.paint(&file.name).to_string()
+ }
+}
+
+fn symlink_filename(file: &File, colours: &Colours) -> String {
+ match file.link_target() {
+ Ok(target) => format!("{} {} {}",
+ file_colour(colours, file).paint(&file.name),
+ colours.punctuation.paint("=>"),
+ ANSIStrings(&[ colours.symlink_path.paint(&target.path_prefix()),
+ file_colour(colours, &target).paint(&target.name) ])),
+
+ Err(filename) => format!("{} {} {}",
+ file_colour(colours, file).paint(&file.name),
+ colours.broken_arrow.paint("=>"),
+ colours.broken_filename.paint(&filename)),
+ }
+}