From 422efee06b455ef941cabf679578ce37ac9380e1 Mon Sep 17 00:00:00 2001 From: SPQV MF Date: Thu, 26 Oct 2023 02:38:55 +0800 Subject: [PATCH] feat!: support different custom time style for non-recent/recent files Extend the `+` custom time style, accept an optional `...` to support setting a different custom time style for _recent_ files. BREAKING CHANGE: (1) The behavior changes if the previous format string already contains ``. Previously ``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='+'` will now get a panic message. Resolves: #563 --- src/options/view.rs | 36 +++++++++++++++++++++++++++++++++--- src/output/time.rs | 31 ++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/options/view.rs b/src/options/view.rs index f8ff1ff5..33a8da0b 100644 --- a/src/options/view.rs +++ b/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)), } } diff --git a/src/output/time.rs b/src/output/time.rs index bbfe3494..f5b281e9 100644 --- a/src/output/time.rs +++ b/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, + }, } impl TimeFormat { pub fn format(self, time: &DateTime) -> 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) -> String { time.format("%Y-%m-%d %H:%M:%S.%f %z").to_string() } -fn custom(time: &DateTime, fmt: &str) -> String { - time.format(fmt).to_string() +fn custom(time: &DateTime, 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 = Lazy::new(|| Local::now().year());