diff --git a/Cargo.lock b/Cargo.lock index ec4b514..30229fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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)", ] diff --git a/Cargo.toml b/Cargo.toml index f7029bd..65e3c66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 \ No newline at end of file +optional = true + +[dependencies.datetime] +path = "../rust-datetime" + +[dependencies.datetime_macros] +path = "../rust-datetime/datetime-macros" diff --git a/src/column.rs b/src/column.rs index 030570a..8b46d9c 100644 --- a/src/column.rs +++ b/src/column.rs @@ -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", } } } diff --git a/src/file.rs b/src/file.rs index bc968c7..729b346 100644 --- a/src/file.rs +++ b/src/file.rs @@ -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 diff --git a/src/main.rs b/src/main.rs index 2e384ff..97c51ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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")] diff --git a/src/options.rs b/src/options.rs index ddf87bd..2ee43d1 100644 --- a/src/options.rs +++ b/src/options.rs @@ -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 { + 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 { 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() {