Browse Source

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.
Ben S 11 years ago
parent
commit
0d25a90ef1
6 changed files with 139 additions and 25 deletions
  1. 48 12
      Cargo.lock
  2. 8 1
      Cargo.toml
  3. 11 9
      src/column.rs
  4. 21 1
      src/file.rs
  5. 6 1
      src/main.rs
  6. 45 1
      src/options.rs

+ 48 - 12
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)",
 ]
 

+ 8 - 1
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
+optional = true
+
+[dependencies.datetime]
+path = "../rust-datetime"
+
+[dependencies.datetime_macros]
+path = "../rust-datetime/datetime-macros"

+ 11 - 9
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",
         }
     }
 }

+ 21 - 1
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

+ 6 - 1
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")]

+ 45 - 1
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<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() {