diff --git a/src/dir.rs b/src/dir.rs index 5bc570e..7bbeda7 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -1,6 +1,5 @@ use feature::Git; -use file::File; -use file; +use file::{File, fields}; use std::io; use std::fs; @@ -65,11 +64,11 @@ impl Dir { } /// Get a string describing the Git status of the given file. - pub fn git_status(&self, path: &Path, prefix_lookup: bool) -> file::Git { + pub fn git_status(&self, path: &Path, prefix_lookup: bool) -> fields::Git { match (&self.git, prefix_lookup) { (&Some(ref git), false) => git.status(path), (&Some(ref git), true) => git.dir_status(path), - (&None, _) => file::Git::empty() + (&None, _) => fields::Git::empty() } } } diff --git a/src/feature/git.rs b/src/feature/git.rs index 1b58b65..59c3a8b 100644 --- a/src/feature/git.rs +++ b/src/feature/git.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use git2; -use file; +use file::fields; /// Container of Git statuses for all the files in this folder's Git repository. pub struct Git { @@ -28,48 +28,48 @@ impl Git { } /// Get the status for the file at the given path, if present. - pub fn status(&self, path: &Path) -> file::Git { + pub fn status(&self, path: &Path) -> fields::Git { let status = self.statuses.iter() .find(|p| p.0.as_path() == path); match status { - Some(&(_, s)) => file::Git { staged: index_status(s), unstaged: working_tree_status(s) }, - None => file::Git { staged: file::GitStatus::NotModified, unstaged: file::GitStatus::NotModified } + Some(&(_, s)) => fields::Git { staged: index_status(s), unstaged: working_tree_status(s) }, + None => fields::Git { staged: fields::GitStatus::NotModified, unstaged: fields::GitStatus::NotModified } } } /// Get the combined status for all the files whose paths begin with the /// path that gets passed in. This is used for getting the status of /// directories, which don't really have an 'official' status. - pub fn dir_status(&self, dir: &Path) -> file::Git { + pub fn dir_status(&self, dir: &Path) -> fields::Git { let s = self.statuses.iter() .filter(|p| p.0.starts_with(dir)) .fold(git2::Status::empty(), |a, b| a | b.1); - file::Git { staged: index_status(s), unstaged: working_tree_status(s) } + fields::Git { staged: index_status(s), unstaged: working_tree_status(s) } } } /// The character to display if the file has been modified, but not staged. -fn working_tree_status(status: git2::Status) -> file::GitStatus { +fn working_tree_status(status: git2::Status) -> fields::GitStatus { match status { - s if s.contains(git2::STATUS_WT_NEW) => file::GitStatus::New, - s if s.contains(git2::STATUS_WT_MODIFIED) => file::GitStatus::Modified, - s if s.contains(git2::STATUS_WT_DELETED) => file::GitStatus::Deleted, - s if s.contains(git2::STATUS_WT_RENAMED) => file::GitStatus::Renamed, - s if s.contains(git2::STATUS_WT_TYPECHANGE) => file::GitStatus::TypeChange, - _ => file::GitStatus::NotModified, + s if s.contains(git2::STATUS_WT_NEW) => fields::GitStatus::New, + s if s.contains(git2::STATUS_WT_MODIFIED) => fields::GitStatus::Modified, + s if s.contains(git2::STATUS_WT_DELETED) => fields::GitStatus::Deleted, + s if s.contains(git2::STATUS_WT_RENAMED) => fields::GitStatus::Renamed, + s if s.contains(git2::STATUS_WT_TYPECHANGE) => fields::GitStatus::TypeChange, + _ => fields::GitStatus::NotModified, } } /// The character to display if the file has been modified, and the change /// has been staged. -fn index_status(status: git2::Status) -> file::GitStatus { +fn index_status(status: git2::Status) -> fields::GitStatus { match status { - s if s.contains(git2::STATUS_INDEX_NEW) => file::GitStatus::New, - s if s.contains(git2::STATUS_INDEX_MODIFIED) => file::GitStatus::Modified, - s if s.contains(git2::STATUS_INDEX_DELETED) => file::GitStatus::Deleted, - s if s.contains(git2::STATUS_INDEX_RENAMED) => file::GitStatus::Renamed, - s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => file::GitStatus::TypeChange, - _ => file::GitStatus::NotModified, + s if s.contains(git2::STATUS_INDEX_NEW) => fields::GitStatus::New, + s if s.contains(git2::STATUS_INDEX_MODIFIED) => fields::GitStatus::Modified, + s if s.contains(git2::STATUS_INDEX_DELETED) => fields::GitStatus::Deleted, + s if s.contains(git2::STATUS_INDEX_RENAMED) => fields::GitStatus::Renamed, + s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => fields::GitStatus::TypeChange, + _ => fields::GitStatus::NotModified, } } diff --git a/src/file.rs b/src/file.rs index 200d65f..cd1589e 100644 --- a/src/file.rs +++ b/src/file.rs @@ -3,7 +3,6 @@ use std::env::current_dir; use std::fs; use std::io; use std::os::unix; -use std::os::unix::raw::{blkcnt_t, gid_t, ino_t, nlink_t, time_t, uid_t}; use std::os::unix::fs::{MetadataExt, PermissionsExt}; use std::path::{Component, Path, PathBuf}; @@ -13,66 +12,7 @@ use dir::Dir; use options::TimeType; use feature::Attribute; -pub enum Type { - File, Directory, Pipe, Link, Special, -} - -pub struct Permissions { - pub file_type: Type, - pub user_read: bool, - pub user_write: bool, - pub user_execute: bool, - pub group_read: bool, - pub group_write: bool, - pub group_execute: bool, - pub other_read: bool, - pub other_write: bool, - pub other_execute: bool, - pub attribute: bool, -} - -pub struct Links { - pub count: nlink_t, - pub multiple: bool, -} - -pub struct Inode(pub ino_t); - -pub enum Blocks { - Some(blkcnt_t), - None, -} - -pub struct User(pub uid_t); - -pub struct Group(pub gid_t); - -pub enum Size { - Some(u64), - None, -} - -pub struct Time(pub time_t); - -pub enum GitStatus { - NotModified, - New, - Modified, - Deleted, - Renamed, - TypeChange, -} - -pub struct Git { - pub staged: GitStatus, - pub unstaged: GitStatus, -} - -impl Git { - pub fn empty() -> Git { - Git { staged: GitStatus::NotModified, unstaged: GitStatus::NotModified } - } -} +use self::fields as f; /// A **File** is a wrapper around one of Rust's Path objects, along with /// associated data about the file. @@ -222,34 +162,34 @@ impl<'a> File<'a> { /// This is important, because a file with multiple links is uncommon, /// while you can come across directories and other types with multiple /// links much more often. - pub fn links(&self) -> Links { + pub fn links(&self) -> f::Links { let count = self.stat.as_raw().nlink(); - Links { + f::Links { count: count, multiple: self.is_file() && count > 1, } } - pub fn inode(&self) -> Inode { - Inode(self.stat.as_raw().ino()) + pub fn inode(&self) -> f::Inode { + f::Inode(self.stat.as_raw().ino()) } - pub fn blocks(&self) -> Blocks { + pub fn blocks(&self) -> f::Blocks { if self.is_file() || self.is_link() { - Blocks::Some(self.stat.as_raw().blocks()) + f::Blocks::Some(self.stat.as_raw().blocks()) } else { - Blocks::None + f::Blocks::None } } - pub fn user(&self) -> User { - User(self.stat.as_raw().uid()) + pub fn user(&self) -> f::User { + f::User(self.stat.as_raw().uid()) } - pub fn group(&self) -> Group { - Group(self.stat.as_raw().gid()) + pub fn group(&self) -> f::Group { + f::Group(self.stat.as_raw().gid()) } /// This file's size, formatted using the given way, as a coloured string. @@ -258,52 +198,52 @@ impl<'a> File<'a> { /// some filesystems, I've never looked at one of those numbers and gained /// any information from it, so by emitting "-" instead, the table is less /// cluttered with numbers. - pub fn size(&self) -> Size { + pub fn size(&self) -> f::Size { if self.is_directory() { - Size::None + f::Size::None } else { - Size::Some(self.stat.len()) + f::Size::Some(self.stat.len()) } } - pub fn timestamp(&self, time_type: TimeType) -> Time { + pub fn timestamp(&self, time_type: TimeType) -> f::Time { let time_in_seconds = match time_type { TimeType::FileAccessed => self.stat.as_raw().atime(), TimeType::FileModified => self.stat.as_raw().mtime(), TimeType::FileCreated => self.stat.as_raw().ctime(), }; - Time(time_in_seconds) + f::Time(time_in_seconds) } /// This file's type, represented by a coloured character. /// /// Although the file type can usually be guessed from the colour of the /// file, `ls` puts this character there, so people will expect it. - fn type_char(&self) -> Type { + fn type_char(&self) -> f::Type { if self.is_file() { - Type::File + f::Type::File } else if self.is_directory() { - Type::Directory + f::Type::Directory } else if self.is_pipe() { - Type::Pipe + f::Type::Pipe } else if self.is_link() { - Type::Link + f::Type::Link } else { - Type::Special + f::Type::Special } } - pub fn permissions(&self) -> Permissions { + pub fn permissions(&self) -> f::Permissions { let bits = self.stat.permissions().mode(); let has_bit = |bit| { bits & bit == bit }; - Permissions { + f::Permissions { file_type: self.type_char(), user_read: has_bit(unix::fs::USER_READ), user_write: has_bit(unix::fs::USER_WRITE), @@ -364,9 +304,9 @@ impl<'a> File<'a> { choices.contains(&&self.name[..]) } - pub fn git_status(&self) -> Git { + pub fn git_status(&self) -> f::Git { match self.dir { - None => Git { staged: GitStatus::NotModified, unstaged: GitStatus::NotModified }, + None => f::Git { staged: f::GitStatus::NotModified, unstaged: f::GitStatus::NotModified }, Some(d) => { let cwd = match current_dir() { Err(_) => Path::new(".").join(&self.path), @@ -404,6 +344,71 @@ fn ext<'a>(name: &'a str) -> Option { name.rfind('.').map(|p| name[p+1..].to_ascii_lowercase()) } +pub mod fields { + use std::os::unix::raw::{blkcnt_t, gid_t, ino_t, nlink_t, time_t, uid_t}; + + pub enum Type { + File, Directory, Pipe, Link, Special, + } + + pub struct Permissions { + pub file_type: Type, + pub user_read: bool, + pub user_write: bool, + pub user_execute: bool, + pub group_read: bool, + pub group_write: bool, + pub group_execute: bool, + pub other_read: bool, + pub other_write: bool, + pub other_execute: bool, + pub attribute: bool, + } + + pub struct Links { + pub count: nlink_t, + pub multiple: bool, + } + + pub struct Inode(pub ino_t); + + pub enum Blocks { + Some(blkcnt_t), + None, + } + + pub struct User(pub uid_t); + + pub struct Group(pub gid_t); + + pub enum Size { + Some(u64), + None, + } + + pub struct Time(pub time_t); + + pub enum GitStatus { + NotModified, + New, + Modified, + Deleted, + Renamed, + TypeChange, + } + + pub struct Git { + pub staged: GitStatus, + pub unstaged: GitStatus, + } + + impl Git { + pub fn empty() -> Git { + Git { staged: GitStatus::NotModified, unstaged: GitStatus::NotModified } + } + } +} + #[cfg(broken_test)] pub mod test { pub use super::*; diff --git a/src/output/details.rs b/src/output/details.rs index 9be8cab..50b57ae 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -2,7 +2,8 @@ use colours::Colours; use column::{Alignment, Column, Cell}; use feature::Attribute; use dir::Dir; -use file::{Blocks, File, Git, GitStatus, Group, Inode, Links, Permissions, Size, Time, Type, User}; +use file::File; +use file::fields as f; use options::{Columns, FileFilter, RecurseOptions, SizeFormat}; use users::{OSUsers, Users}; @@ -197,21 +198,21 @@ impl Table { } } - fn render_permissions(&self, permissions: Permissions) -> Cell { + fn render_permissions(&self, permissions: f::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("-") } }; 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("?"), + f::Type::File => self.colours.filetypes.normal.paint("."), + f::Type::Directory => self.colours.filetypes.directory.paint("d"), + f::Type::Pipe => self.colours.filetypes.special.paint("|"), + f::Type::Link => self.colours.filetypes.symlink.paint("l"), + f::Type::Special => self.colours.filetypes.special.paint("?"), }; - let x_colour = if let Type::File = permissions.file_type { c.user_execute_file } + let x_colour = if let f::Type::File = permissions.file_type { c.user_execute_file } else { c.user_execute_other }; let string = ANSIStrings( &[ @@ -234,26 +235,26 @@ impl Table { } } - fn render_links(&self, links: Links) -> Cell { + fn render_links(&self, links: f::Links) -> Cell { let style = if links.multiple { self.colours.links.multi_link_file } else { self.colours.links.normal }; Cell::paint(style, &self.numeric.format_int(links.count)) } - fn render_blocks(&self, blocks: Blocks) -> Cell { + fn render_blocks(&self, blocks: f::Blocks) -> Cell { match blocks { - Blocks::Some(blocks) => Cell::paint(self.colours.blocks, &blocks.to_string()), - Blocks::None => Cell::paint(self.colours.punctuation, "-"), + f::Blocks::Some(blocks) => Cell::paint(self.colours.blocks, &blocks.to_string()), + f::Blocks::None => Cell::paint(self.colours.punctuation, "-"), } } - fn render_inode(&self, inode: Inode) -> Cell { + fn render_inode(&self, inode: f::Inode) -> Cell { Cell::paint(self.colours.inode, &inode.0.to_string()) } - fn render_size(&self, size: Size, size_format: SizeFormat) -> Cell { - if let Size::Some(offset) = size { + fn render_size(&self, size: f::Size, size_format: SizeFormat) -> Cell { + if let f::Size::Some(offset) = size { let result = match size_format { SizeFormat::DecimalBytes => decimal_prefix(offset as f64), SizeFormat::BinaryBytes => binary_prefix(offset as f64), @@ -278,7 +279,7 @@ impl Table { } } - fn render_time(&self, timestamp: Time) -> Cell { + fn render_time(&self, timestamp: f::Time) -> Cell { let date = LocalDateTime::at(timestamp.0); let format = if date.year() == self.current_year { @@ -291,15 +292,15 @@ impl Table { Cell::paint(self.colours.date, &format.format(date, &self.time)) } - fn render_git_status(&self, git: Git) -> Cell { + fn render_git_status(&self, git: f::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"), + f::GitStatus::NotModified => self.colours.punctuation.paint("-"), + f::GitStatus::New => self.colours.git.renamed.paint("N"), + f::GitStatus::Modified => self.colours.git.renamed.paint("M"), + f::GitStatus::Deleted => self.colours.git.renamed.paint("D"), + f::GitStatus::Renamed => self.colours.git.renamed.paint("R"), + f::GitStatus::TypeChange => self.colours.git.renamed.paint("T"), } }; @@ -309,7 +310,7 @@ impl Table { } } - fn render_user(&mut self, user: User) -> Cell { + fn render_user(&mut self, user: f::User) -> Cell { let user_name = match self.users.get_user_by_uid(user.0) { Some(user) => user.name, None => user.0.to_string(), @@ -320,7 +321,7 @@ impl Table { Cell::paint(style, &*user_name) } - fn render_group(&mut self, group: Group) -> Cell { + fn render_group(&mut self, group: f::Group) -> Cell { let mut style = self.colours.users.group_not_yours; let group_name = match self.users.get_group_by_gid(group.0) {