Bläddra i källkod

feat!: support different custom time style for non-recent/recent files

Extend the `+<FORMAT>` custom time style, accept an optional
`...<newline><FORMAT_2>` to support setting a different custom time
style for _recent_ files.

BREAKING CHANGE: (1) The behavior changes if the previous format string
already contains `<newline>`.  Previously `<newline>`s are used as-is,
now the first newline will be interpreted as separation for _non-recent_
and _recnet_ format; any content after the second newline, if any, are
simply ignored.  (2) The implementation rejects some previously accepted
inputs, primarily empty _non-recent_ format string.  `--time-style=+`
and `--time-style='+<newline>'` will now get a panic message.

Resolves: #563
SPQV MF 2 år sedan
förälder
incheckning
422efee06b
2 ändrade filer med 55 tillägg och 12 borttagningar
  1. 33 3
      src/options/view.rs
  2. 22 9
      src/output/time.rs

+ 33 - 3
src/options/view.rs

@@ -335,9 +335,39 @@ impl TimeFormat {
             "iso" => Ok(Self::ISOFormat),
             "long-iso" => Ok(Self::LongISO),
             "full-iso" => Ok(Self::FullISO),
-            fmt if fmt.starts_with('+') => Ok(Self::Custom {
-                fmt: fmt[1..].to_owned(),
-            }),
+            fmt if fmt.starts_with('+') => {
+                let mut lines = fmt[1..].lines();
+
+                // line 1 will be None when:
+                //   - there is nothing after `+`
+                // line 1 will be empty when:
+                //   - `+` is followed immediately by `\n`
+                let empty_non_recent_format_msg = "Custom timestamp format is empty, \
+                    please supply a chrono format string after the plus sign.";
+                let non_recent = lines.next().expect(empty_non_recent_format_msg);
+                let non_recent = if non_recent.is_empty() {
+                    panic!("{}", empty_non_recent_format_msg)
+                } else {
+                    non_recent.to_owned()
+                };
+
+                // line 2 will be None when:
+                //   - there is not a single `\n`
+                //   - there is nothing after the first `\n`
+                // line 2 will be empty when:
+                //   - there exist at least 2 `\n`, and no content between the 1st and 2nd `\n`
+                let empty_recent_format_msg = "Custom timestamp format for recent files is empty, \
+                    please supply a chrono format string at the second line.";
+                let recent = lines.next().map(|rec| {
+                    if rec.is_empty() {
+                        panic!("{}", empty_recent_format_msg)
+                    } else {
+                        rec.to_owned()
+                    }
+                });
+
+                Ok(Self::Custom { non_recent, recent })
+            }
             _ => Err(OptionsError::BadArgument(&flags::TIME_STYLE, word)),
         }
     }

+ 22 - 9
src/output/time.rs

@@ -47,19 +47,24 @@ pub enum TimeFormat {
     Relative,
 
     /// Use a custom format
-    Custom { fmt: String },
+    Custom {
+        non_recent: String,
+        recent: Option<String>,
+    },
 }
 
 impl TimeFormat {
     pub fn format(self, time: &DateTime<FixedOffset>) -> String {
         #[rustfmt::skip]
         return match self {
-            Self::DefaultFormat  => default(time),
-            Self::ISOFormat      => iso(time),
-            Self::LongISO        => long(time),
-            Self::FullISO        => full(time),
-            Self::Relative       => relative(time),
-            Self::Custom { fmt } => custom(time, &fmt),
+            Self::DefaultFormat                 => default(time),
+            Self::ISOFormat                     => iso(time),
+            Self::LongISO                       => long(time),
+            Self::FullISO                       => full(time),
+            Self::Relative                      => relative(time),
+            Self::Custom { non_recent, recent } => custom(
+                time, non_recent.as_str(), recent.as_deref()
+            ),
         };
     }
 }
@@ -115,8 +120,16 @@ fn full(time: &DateTime<FixedOffset>) -> String {
     time.format("%Y-%m-%d %H:%M:%S.%f %z").to_string()
 }
 
-fn custom(time: &DateTime<FixedOffset>, fmt: &str) -> String {
-    time.format(fmt).to_string()
+fn custom(time: &DateTime<FixedOffset>, non_recent_fmt: &str, recent_fmt: Option<&str>) -> String {
+    if let Some(recent_fmt) = recent_fmt {
+        if time.year() == *CURRENT_YEAR {
+            time.format(recent_fmt).to_string()
+        } else {
+            time.format(non_recent_fmt).to_string()
+        }
+    } else {
+        time.format(non_recent_fmt).to_string()
+    }
 }
 
 static CURRENT_YEAR: Lazy<i32> = Lazy::new(|| Local::now().year());