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
This commit is contained in:
SPQV MF 2023-10-26 02:38:55 +08:00 committed by Christina Sørensen
parent 54abab7849
commit 422efee06b
2 changed files with 55 additions and 12 deletions

View file

@ -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)),
}
}

View file

@ -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());