mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-12-28 02:35:03 +00:00
Move File fields to their own module
This commit is contained in:
parent
2a3045ddfa
commit
085067d18e
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
179
src/file.rs
179
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<String> {
|
||||
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::*;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user