Ver código fonte

Document and test time formats

Benjamin Sago 8 anos atrás
pai
commit
b2947ed590
2 arquivos alterados com 90 adições e 9 exclusões
  1. 53 9
      src/options/view.rs
  2. 37 0
      src/output/time.rs

+ 53 - 9
src/options/view.rs

@@ -413,7 +413,10 @@ mod test {
     use super::*;
     use std::ffi::OsString;
     use options::flags;
-    use options::parser::Flag;
+    use options::parser::{Flag, Arg};
+
+    use options::test::parse_for_test;
+    use options::test::Strictnesses::*;
 
     pub fn os(input: &'static str) -> OsString {
         let mut os = OsString::new();
@@ -421,36 +424,77 @@ mod test {
         os
     }
 
+    static TEST_ARGS: &[&Arg] = &[ &flags::BINARY, &flags::BYTES,    &flags::TIME_STYLE,
+                                   &flags::TIME,   &flags::MODIFIED, &flags::CREATED, &flags::ACCESSED,
+                                   &flags::COLOR,  &flags::COLOUR ];
+
     macro_rules! test {
         ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => $result:expr) => {
             #[test]
             fn $name() {
-                use options::parser::Arg;
-                use options::test::parse_for_test;
-                use options::test::Strictnesses::*;
-
-                static TEST_ARGS: &[&Arg] = &[ &flags::BINARY, &flags::BYTES,
-                                               &flags::TIME,   &flags::MODIFIED, &flags::CREATED, &flags::ACCESSED,
-                                               &flags::COLOR,  &flags::COLOUR ];
-
                 for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
                     assert_eq!(result, $result);
                 }
             }
         };
+
+        ($name:ident: $type:ident <- $inputs:expr; $stricts:expr => like $pat:pat) => {
+            #[test]
+            fn $name() {
+                for result in parse_for_test($inputs.as_ref(), TEST_ARGS, $stricts, |mf| $type::deduce(mf)) {
+                    println!("Testing {:?}", result);
+                    match result {
+                        $pat => assert!(true),
+                        _    => assert!(false),
+                    }
+                }
+            }
+        };
     }
 
 
     mod size_formats {
         use super::*;
 
+        // Default behaviour
         test!(empty:   SizeFormat <- [];                       Both => Ok(SizeFormat::DecimalBytes));
+
+        // Individual flags
         test!(binary:  SizeFormat <- ["--binary"];             Both => Ok(SizeFormat::BinaryBytes));
         test!(bytes:   SizeFormat <- ["--bytes"];              Both => Ok(SizeFormat::JustBytes));
+
+        // Errors
         test!(both:    SizeFormat <- ["--binary", "--bytes"];  Both => Err(Misfire::Conflict(&flags::BINARY, &flags::BYTES)));
     }
 
 
+    mod time_formats {
+        use super::*;
+
+        // These tests use pattern matching because TimeFormat doesn’t
+        // implement PartialEq.
+
+        // Default behaviour
+        test!(empty:     TimeFormat <- [];                            Both => like Ok(TimeFormat::DefaultFormat(_)));
+
+        // Individual settings
+        test!(default:   TimeFormat <- ["--time-style=default"];      Both => like Ok(TimeFormat::DefaultFormat(_)));
+        test!(iso:       TimeFormat <- ["--time-style", "iso"];       Both => like Ok(TimeFormat::ISOFormat(_)));
+        test!(long_iso:  TimeFormat <- ["--time-style=long-iso"];     Both => like Ok(TimeFormat::LongISO));
+        test!(full_iso:  TimeFormat <- ["--time-style", "full-iso"];  Both => like Ok(TimeFormat::FullISO));
+
+        // Overriding
+        test!(actually:  TimeFormat <- ["--time-style=default",     "--time-style", "iso"];    Last => like Ok(TimeFormat::ISOFormat(_)));
+        test!(actual_2:  TimeFormat <- ["--time-style=default",     "--time-style", "iso"];    Complain => like Err(Misfire::Duplicate(Flag::Long("time-style"), Flag::Long("time-style"))));
+
+        test!(nevermind: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"];  Last => like Ok(TimeFormat::FullISO));
+        test!(nevermore: TimeFormat <- ["--time-style", "long-iso", "--time-style=full-iso"];  Complain => like Err(Misfire::Duplicate(Flag::Long("time-style"), Flag::Long("time-style"))));
+
+        // Errors
+        test!(daily:     TimeFormat <- ["--time-style=24-hour"];      Both => like Err(Misfire::BadArgument(_, _, _)));
+    }
+
+
     mod time_types {
         use super::*;
 

+ 37 - 0
src/output/time.rs

@@ -1,3 +1,5 @@
+//! Timestamp formatting.
+
 use datetime::{LocalDateTime, TimeZone, DatePiece, TimePiece};
 use datetime::fmt::DateFormat;
 use locale;
@@ -6,13 +8,48 @@ use std::cmp;
 use fs::fields::Time;
 
 
+/// Every timestamp in exa needs to be rendered by a **time format**.
+/// Formatting times is tricky, because how a timestamp is rendered can
+/// depend on one or more of the following:
+///
+/// - The user’s locale, for printing the month name as “Feb”, or as “fév”,
+///   or as “2月”;
+/// - The current year, because certain formats will be less precise when
+///   dealing with dates far in the past;
+/// - The formatting style that the user asked for on the command-line.
+///
+/// Because not all formatting styles need the same data, they all have their
+/// own enum variants. It’s not worth looking the locale up if the formatter
+/// prints month names as numbers.
+///
+/// Currently exa does not support *custom* styles, where the user enters a
+/// format string in an environment variable or something. Just these four.
+#[derive(Debug)]
 pub enum TimeFormat {
+
+    /// The **default format** uses the user’s locale to print month names,
+    /// and specifies the timestamp down to the minute for recent times, and
+    /// day for older times.
     DefaultFormat(DefaultFormat),
+
+    /// Use the **ISO format**, which specifies the timestamp down to the
+    /// minute for recent times, and day for older times. It uses a number
+    /// for the month so it doesn’t need a locale.
     ISOFormat(ISOFormat),
+
+    /// Use the **long ISO format**, which specifies the timestamp down to the
+    /// minute using only numbers, without needing the locale or year.
     LongISO,
+
+    /// Use the **full ISO format**, which specifies the timestamp down to the
+    /// millisecond and includes its offset down to the minute. This too uses
+    /// only numbers so doesn’t require any special consideration.
     FullISO,
 }
 
+// There are two different formatting functions because local and zoned
+// timestamps are separate types.
+
 impl TimeFormat {
     pub fn format_local(&self, time: Time) -> String {
         match *self {