|
@@ -161,6 +161,20 @@ impl Table {
|
|
|
self.rows.push(row);
|
|
self.rows.push(row);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// 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: filename(file, &self.colours),
|
|
|
|
|
+ last: last,
|
|
|
|
|
+ attrs: file.xattrs.clone(),
|
|
|
|
|
+ children: file.this.is_some(),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ self.rows.push(row)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/// Use the list of columns to find which cells should be produced for
|
|
/// Use the list of columns to find which cells should be produced for
|
|
|
/// this file, per-column.
|
|
/// this file, per-column.
|
|
|
fn cells_for_file(&mut self, file: &File) -> Vec<Cell> {
|
|
fn cells_for_file(&mut self, file: &File) -> Vec<Cell> {
|
|
@@ -171,30 +185,158 @@ impl Table {
|
|
|
|
|
|
|
|
fn display(&mut self, file: &File, column: &Column) -> Cell {
|
|
fn display(&mut self, file: &File, column: &Column) -> Cell {
|
|
|
match *column {
|
|
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),
|
|
|
|
|
|
|
+ Column::Permissions => self.render_permissions(file.permissions()),
|
|
|
|
|
+ Column::FileSize(f) => self.render_size(file.size()),
|
|
|
|
|
+ Column::Timestamp(t, y) => self.render_time(file.timestamp(t)),
|
|
|
|
|
+ Column::HardLinks => self.render_links(file.links()),
|
|
|
|
|
+ Column::Inode => self.render_inode(file.inode()),
|
|
|
|
|
+ Column::Blocks => self.render_blocks(file.blocks()),
|
|
|
|
|
+ Column::User => self.render_user(file.user()),
|
|
|
|
|
+ Column::Group => self.render_group(file.group()),
|
|
|
|
|
+ Column::GitStatus => self.render_git_status(file.git_status()),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /// 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: filename(file, &self.colours),
|
|
|
|
|
- last: last,
|
|
|
|
|
- attrs: file.xattrs.clone(),
|
|
|
|
|
- children: file.this.is_some(),
|
|
|
|
|
|
|
+ fn render_permissions(&self, permissions: Permissions) -> Cell {
|
|
|
|
|
+ let c = self.colours.perms;
|
|
|
|
|
+ let bit = |bit, chr: &'static str, style: Style| {
|
|
|
|
|
+ if bit { style.paint(chr) } else { self.colours.punctuation.paint("-") }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- self.rows.push(row)
|
|
|
|
|
|
|
+ let file_type = match permissions.file_type {
|
|
|
|
|
+ Type::File => self.colours.filetypes.normal.paint("."),
|
|
|
|
|
+ Type::Directory => self.colours.filetypes.directory.paint("d"),
|
|
|
|
|
+ Type::Pipe => self.colours.filetypes.special.paint("|"),
|
|
|
|
|
+ Type::Link => self.colours.filetypes.symlink.paint("l"),
|
|
|
|
|
+ Type::Special => self.colours.filetypes.special.paint("?"),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let x_colour = if let Type::File = permissions.file_type { c.user_execute_file }
|
|
|
|
|
+ else { c.user_execute_other };
|
|
|
|
|
+
|
|
|
|
|
+ let string = ANSIStrings( &[
|
|
|
|
|
+ file_type,
|
|
|
|
|
+ bit(permissions.user_read, "r", c.user_read),
|
|
|
|
|
+ bit(permissions.user_write, "w", c.user_write),
|
|
|
|
|
+ bit(permissions.user_execute, "x", x_colour),
|
|
|
|
|
+ bit(permissions.group_read, "r", c.group_read),
|
|
|
|
|
+ bit(permissions.group_write, "w", c.group_write),
|
|
|
|
|
+ bit(permissions.group_execute, "x", c.group_execute),
|
|
|
|
|
+ bit(permissions.other_read, "r", c.other_read),
|
|
|
|
|
+ bit(permissions.other_write, "w", c.other_write),
|
|
|
|
|
+ bit(permissions.other_execute, "x", c.other_execute),
|
|
|
|
|
+ if permissions.attribute { c.attribute.paint("@") } else { Plain.paint(" ") },
|
|
|
|
|
+ ]).to_string();
|
|
|
|
|
+
|
|
|
|
|
+ Cell {
|
|
|
|
|
+ text: string,
|
|
|
|
|
+ length: 11,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_links(&self, links: Links) -> Cell {
|
|
|
|
|
+ let style = if links.multiple { self.colours.links.multi_link_file }
|
|
|
|
|
+ else { self.colours.links.normal };
|
|
|
|
|
+
|
|
|
|
|
+ Cell::paint(style, &self.local.numeric.format_int(links.count))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_blocks(&self, blocks: Blocks) -> Cell {
|
|
|
|
|
+ match blocks {
|
|
|
|
|
+ Blocks::Some(blocks) => Cell::paint(self.colours.blocks, &blocks.to_string()),
|
|
|
|
|
+ Blocks::None => Cell::paint(self.colours.punctuation, "-"),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_inode(&self, inode: Inode) -> Cell {
|
|
|
|
|
+ Cell::paint(self.colours.inode, &inode.0.to_string())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_size(&self, size: Size) -> Cell {
|
|
|
|
|
+ if let Size::Some(offset) = size {
|
|
|
|
|
+ let result = match self.local.size_format {
|
|
|
|
|
+ SizeFormat::DecimalBytes => decimal_prefix(offset as f64),
|
|
|
|
|
+ SizeFormat::BinaryBytes => binary_prefix(offset as f64),
|
|
|
|
|
+ SizeFormat::JustBytes => return Cell::paint(self.colours.size.numbers, &self.local.numeric.format_int(offset)),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ match result {
|
|
|
|
|
+ Standalone(bytes) => Cell::paint(self.colours.size.numbers, &*bytes.to_string()),
|
|
|
|
|
+ Prefixed(prefix, n) => {
|
|
|
|
|
+ let number = if n < 10f64 { self.local.numeric.format_float(n, 1) } else { self.local.numeric.format_int(n as isize) };
|
|
|
|
|
+ let symbol = prefix.symbol();
|
|
|
|
|
+
|
|
|
|
|
+ Cell {
|
|
|
|
|
+ text: ANSIStrings( &[ self.colours.size.numbers.paint(&number[..]), self.colours.size.unit.paint(symbol) ]).to_string(),
|
|
|
|
|
+ length: number.len() + symbol.len(),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ Cell::paint(self.colours.punctuation, "-")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_time(&self, timestamp: Time) -> Cell {
|
|
|
|
|
+ let date = LocalDateTime::at(timestamp.0);
|
|
|
|
|
+
|
|
|
|
|
+ let format = if date.year() == self.local.current_year {
|
|
|
|
|
+ DateFormat::parse("{2>:D} {:M} {2>:h}:{02>:m}").unwrap()
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ DateFormat::parse("{2>:D} {:M} {5>:Y}").unwrap()
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Cell::paint(self.colours.date, &format.format(date, &self.local.time))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_git_status(&self, git: Git) -> Cell {
|
|
|
|
|
+ let render_char = |chr| {
|
|
|
|
|
+ match chr {
|
|
|
|
|
+ GitStatus::NotModified => self.colours.punctuation.paint("-"),
|
|
|
|
|
+ GitStatus::New => self.colours.git.renamed.paint("N"),
|
|
|
|
|
+ GitStatus::Modified => self.colours.git.renamed.paint("M"),
|
|
|
|
|
+ GitStatus::Deleted => self.colours.git.renamed.paint("D"),
|
|
|
|
|
+ GitStatus::Renamed => self.colours.git.renamed.paint("R"),
|
|
|
|
|
+ GitStatus::TypeChange => self.colours.git.renamed.paint("T"),
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Cell {
|
|
|
|
|
+ text: ANSIStrings(&[ render_char(git.staged), render_char(git.unstaged) ]).to_string(),
|
|
|
|
|
+ length: 2,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_user(&mut self, user: User) -> Cell {
|
|
|
|
|
+ let user_name = match self.local.users.get_user_by_uid(user.0) {
|
|
|
|
|
+ Some(user) => user.name,
|
|
|
|
|
+ None => user.0.to_string(),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let style = if self.local.users.get_current_uid() == user.0 { self.colours.users.user_you }
|
|
|
|
|
+ else { self.colours.users.user_someone_else };
|
|
|
|
|
+ Cell::paint(style, &*user_name)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn render_group(&mut self, group: Group) -> Cell {
|
|
|
|
|
+ let mut style = self.colours.users.group_not_yours;
|
|
|
|
|
+
|
|
|
|
|
+ let group_name = match self.local.users.get_group_by_gid(group.0) {
|
|
|
|
|
+ Some(group) => {
|
|
|
|
|
+ let current_uid = self.local.users.get_current_uid();
|
|
|
|
|
+ if let Some(current_user) = self.local.users.get_user_by_uid(current_uid) {
|
|
|
|
|
+ if current_user.primary_group == group.gid || group.members.contains(¤t_user.name) {
|
|
|
|
|
+ style = self.colours.users.group_yours;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ group.name
|
|
|
|
|
+ },
|
|
|
|
|
+ None => group.0.to_string(),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ Cell::paint(style, &*group_name)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Print the table to standard output, consuming it in the process.
|
|
/// Print the table to standard output, consuming it in the process.
|
|
@@ -281,169 +423,6 @@ impl TreePart {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-pub trait Render {
|
|
|
|
|
- fn render(self, colours: &Colours, local: &mut Locals) -> Cell;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-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 file_type = match self.file_type {
|
|
|
|
|
- Type::File => colours.filetypes.normal.paint("."),
|
|
|
|
|
- Type::Directory => colours.filetypes.directory.paint("d"),
|
|
|
|
|
- Type::Pipe => colours.filetypes.special.paint("|"),
|
|
|
|
|
- Type::Link => colours.filetypes.symlink.paint("l"),
|
|
|
|
|
- Type::Special => colours.filetypes.special.paint("?"),
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- let x_colour = if let Type::File = self.file_type { c.user_execute_file }
|
|
|
|
|
- else { c.user_execute_other};
|
|
|
|
|
-
|
|
|
|
|
- let string = ANSIStrings( &[
|
|
|
|
|
- file_type,
|
|
|
|
|
- bit(self.user_read, "r", c.user_read),
|
|
|
|
|
- bit(self.user_write, "w", c.user_write),
|
|
|
|
|
- bit(self.user_execute, "x", x_colour),
|
|
|
|
|
- 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.numbers.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(¤t_user.name) {
|
|
|
|
|
- style = colours.users.group_yours;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- group.name
|
|
|
|
|
- },
|
|
|
|
|
- None => self.0.to_string(),
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- Cell::paint(style, &*group_name)
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
pub struct Locals {
|
|
pub struct Locals {
|
|
|
pub time: locale::Time,
|
|
pub time: locale::Time,
|
|
|
pub numeric: locale::Numeric,
|
|
pub numeric: locale::Numeric,
|