Initial work on date/time columns for files

Using the datetime crate, add an extra column to the --long view that
prints out the modified, accessed, or created timestamp for each file.
Also, let the user pick which one they want to see based on the --time
command-line option.
This commit is contained in:
Ben S 2015-02-09 16:33:27 +00:00
parent 3289b82e2f
commit 0d25a90ef1
6 changed files with 139 additions and 25 deletions

60
Cargo.lock generated
View File

@ -3,10 +3,13 @@ name = "exa"
version = "0.1.0"
dependencies = [
"ansi_term 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
"datetime 0.1.0",
"datetime_macros 0.1.0",
"getopts 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"natord 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pad 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"users 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -24,9 +27,31 @@ name = "bitflags"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "datetime"
version = "0.1.0"
dependencies = [
"pad 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"regex_macros 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "datetime_macros"
version = "0.1.0"
dependencies = [
"datetime 0.1.0",
"pad 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gcc"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "getopts"
version = "0.2.2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"log 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -34,17 +59,17 @@ dependencies = [
[[package]]
name = "git2"
version = "0.1.15"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libgit2-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -54,7 +79,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libssh2-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"libz-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -72,7 +97,7 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libz-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -106,18 +131,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "openssl-sys"
version = "0.3.1"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"gcc 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"libressl-pnacl-sys 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pad"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pkg-config"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pkg-config"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pnacl-build-helper"
version = "1.3.2"
@ -143,7 +179,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "url"
version = "0.2.19"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
@ -155,6 +191,6 @@ name = "users"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -11,6 +11,7 @@ ansi_term = "0.4.5"
getopts = "0.2.1"
natord = "1.0.7"
number_prefix = "0.2.3"
pad = "0.1.1"
users = "0.2.3"
[features]
@ -19,4 +20,10 @@ git = [ "git2" ]
[dependencies.git2]
version = "0.1.13"
optional = true
optional = true
[dependencies.datetime]
path = "../rust-datetime"
[dependencies.datetime_macros]
path = "../rust-datetime/datetime-macros"

View File

@ -2,12 +2,13 @@ use std::iter::repeat;
use ansi_term::Style;
use options::SizeFormat;
use options::{SizeFormat, TimeType};
#[derive(PartialEq, Debug, Copy)]
pub enum Column {
Permissions,
FileSize(SizeFormat),
Timestamp(TimeType),
Blocks,
User,
Group,
@ -42,14 +43,15 @@ impl Column {
/// to have a header row printed.
pub fn header(&self) -> &'static str {
match *self {
Column::Permissions => "Permissions",
Column::FileSize(_) => "Size",
Column::Blocks => "Blocks",
Column::User => "User",
Column::Group => "Group",
Column::HardLinks => "Links",
Column::Inode => "inode",
Column::GitStatus => "Git",
Column::Permissions => "Permissions",
Column::FileSize(_) => "Size",
Column::Timestamp(t) => t.header(),
Column::Blocks => "Blocks",
Column::User => "User",
Column::Group => "Group",
Column::HardLinks => "Links",
Column::Inode => "inode",
Column::GitStatus => "Git",
}
}
}

View File

@ -8,13 +8,18 @@ use ansi_term::Colour::{Red, Green, Yellow, Blue, Purple, Cyan, Fixed};
use users::Users;
use pad::Alignment;
use number_prefix::{binary_prefix, decimal_prefix, Prefixed, Standalone, PrefixNames};
use datetime;
use datetime::local::LocalDateTime;
use column::{Column, Cell};
use column::Column::*;
use dir::Dir;
use filetype::HasType;
use options::SizeFormat;
use options::{SizeFormat, TimeType};
/// This grey value is directly in between white and black, so it's guaranteed
/// to show up on either backgrounded terminal.
@ -96,6 +101,7 @@ impl<'a> File<'a> {
match *column {
Permissions => self.permissions_string(),
FileSize(f) => self.file_size(f),
Timestamp(t) => self.timestamp(t),
HardLinks => self.hard_links(),
Inode => self.inode(),
Blocks => self.blocks(),
@ -297,6 +303,20 @@ impl<'a> File<'a> {
}
}
fn timestamp(&self, time_type: TimeType) -> Cell {
let format = date_format!("{:Y} {10>:M} {2>:D}");
// Need to convert these values from milliseconds into seconds.
let time_in_seconds = match time_type {
TimeType::FileAccessed => self.stat.accessed,
TimeType::FileModified => self.stat.modified,
TimeType::FileCreated => self.stat.created,
} as i64 / 1000;
let date = LocalDateTime::at(time_in_seconds).date();
Cell::paint(Blue.normal(), format.format(date).as_slice())
}
/// This file's type, represented by a coloured character.
///
/// Although the file type can usually be guessed from the colour of the

View File

@ -1,9 +1,14 @@
#![feature(collections, core, env, io, libc, os, path, std_misc)]
#![feature(collections, core, env, io, libc, os, path, plugin, std_misc)]
#[plugin] #[no_link]
extern crate datetime_macros;
extern crate ansi_term;
extern crate datetime;
extern crate getopts;
extern crate natord;
extern crate number_prefix;
extern crate pad;
extern crate users;
#[cfg(feature="git")]

View File

@ -56,6 +56,7 @@ impl Options {
opts.optflag("R", "recurse", "recurse into directories");
opts.optopt ("s", "sort", "field to sort by", "WORD");
opts.optflag("S", "blocks", "show number of file system blocks");
opts.optopt ("t", "time", "timestamp field to show", "WORD");
opts.optflag("T", "tree", "recurse into subdirectories in a tree view");
opts.optflag("x", "across", "sort multi-column view entries across");
opts.optflag("?", "help", "show list of command-line options");
@ -205,7 +206,7 @@ impl View {
else {
let details = Details {
columns: try!(Columns::deduce(matches)),
header: matches.opt_present("tree"),
header: matches.opt_present("header"),
tree: matches.opt_present("recurse"),
filter: filter,
};
@ -279,6 +280,45 @@ impl SizeFormat {
}
}
#[derive(PartialEq, Debug, Copy)]
pub enum TimeType {
FileAccessed,
FileModified,
FileCreated,
}
impl TimeType {
/// Find which field to use based on a user-supplied word.
fn deduce(matches: &getopts::Matches) -> Result<TimeType, Misfire> {
let possible_word = matches.opt_str("time");
if let Some(word) = possible_word {
match word.as_slice() {
"mod" | "modified" => Ok(TimeType::FileModified),
"acc" | "accessed" => Ok(TimeType::FileAccessed),
"cr" | "created" => Ok(TimeType::FileCreated),
field => Err(TimeType::none(field)),
}
}
else {
Ok(TimeType::FileModified)
}
}
/// How to display an error when the word didn't match with anything.
fn none(field: &str) -> Misfire {
Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--time {}", field)))
}
pub fn header(&self) -> &'static str {
match *self {
TimeType::FileAccessed => "Date Accessed",
TimeType::FileModified => "Date Modified",
TimeType::FileCreated => "Date Created",
}
}
}
/// What to do when encountering a directory?
#[derive(PartialEq, Debug, Copy)]
pub enum DirAction {
@ -304,6 +344,7 @@ impl DirAction {
#[derive(PartialEq, Copy, Debug)]
pub struct Columns {
size_format: SizeFormat,
time_type: TimeType,
inode: bool,
links: bool,
blocks: bool,
@ -314,6 +355,7 @@ impl Columns {
pub fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
Ok(Columns {
size_format: try!(SizeFormat::deduce(matches)),
time_type: try!(TimeType::deduce(matches)),
inode: matches.opt_present("inode"),
links: matches.opt_present("links"),
blocks: matches.opt_present("blocks"),
@ -346,6 +388,8 @@ impl Columns {
columns.push(Group);
}
columns.push(Timestamp(self.time_type));
if cfg!(feature="git") {
if let Some(d) = dir {
if d.has_git_repo() {