From 0f9d4f5a0a64c6b763971396f1212091dde5b1e5 Mon Sep 17 00:00:00 2001 From: Mario Manno Date: Fri, 17 Dec 2021 00:14:36 +0100 Subject: [PATCH] Date format supports recent/non-recent format --- src/app.rs | 90 +++++++++++++++++++++++++++--------------------- src/meta/date.rs | 68 +++++++++++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 40 deletions(-) diff --git a/src/app.rs b/src/app.rs index d78af0ff2..8964dcc78 100644 --- a/src/app.rs +++ b/src/app.rs @@ -290,51 +290,63 @@ fn validate_date_argument(arg: String) -> Result<(), String> { } pub fn validate_time_format(formatter: &str) -> Result<(), String> { - let mut chars = formatter.chars(); - loop { - match chars.next() { - Some('%') => match chars.next() { - Some('.') => match chars.next() { - Some('f') => (), + let str = formatter.to_string(); + let vec: Vec<&str> = str.split('\n').collect(); + + if vec.len() > 2 { + return Err("invalid format, can only contain one newline separator".to_owned()); + } + + for s in vec { + let mut chars = s.chars(); + + loop { + match chars.next() { + Some('%') => match chars.next() { + Some('.') => match chars.next() { + Some('f') => (), + Some(n @ '3') | Some(n @ '6') | Some(n @ '9') => match chars.next() { + Some('f') => (), + Some(c) => { + return Err(format!("invalid format specifier: %.{}{}", n, c)) + } + None => return Err("missing format specifier".to_owned()), + }, + Some(c) => return Err(format!("invalid format specifier: %.{}", c)), + None => return Err("missing format specifier".to_owned()), + }, + Some(n @ ':') | Some(n @ '#') => match chars.next() { + Some('z') => (), + Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)), + None => return Err("missing format specifier".to_owned()), + }, + Some(n @ '-') | Some(n @ '_') | Some(n @ '0') => match chars.next() { + Some('C') | Some('d') | Some('e') | Some('f') | Some('G') | Some('g') + | Some('H') | Some('I') | Some('j') | Some('k') | Some('l') | Some('M') + | Some('m') | Some('S') | Some('s') | Some('U') | Some('u') | Some('V') + | Some('W') | Some('w') | Some('Y') | Some('y') => (), + Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)), + None => return Err("missing format specifier".to_owned()), + }, + Some('A') | Some('a') | Some('B') | Some('b') | Some('C') | Some('c') + | Some('D') | Some('d') | Some('e') | Some('F') | Some('f') | Some('G') + | Some('g') | Some('H') | Some('h') | Some('I') | Some('j') | Some('k') + | Some('l') | Some('M') | Some('m') | Some('n') | Some('P') | Some('p') + | Some('R') | Some('r') | Some('S') | Some('s') | Some('T') | Some('t') + | Some('U') | Some('u') | Some('V') | Some('v') | Some('W') | Some('w') + | Some('X') | Some('x') | Some('Y') | Some('y') | Some('Z') | Some('z') + | Some('+') | Some('%') => (), Some(n @ '3') | Some(n @ '6') | Some(n @ '9') => match chars.next() { Some('f') => (), - Some(c) => return Err(format!("invalid format specifier: %.{}{}", n, c)), + Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)), None => return Err("missing format specifier".to_owned()), }, - Some(c) => return Err(format!("invalid format specifier: %.{}", c)), - None => return Err("missing format specifier".to_owned()), - }, - Some(n @ ':') | Some(n @ '#') => match chars.next() { - Some('z') => (), - Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)), - None => return Err("missing format specifier".to_owned()), - }, - Some(n @ '-') | Some(n @ '_') | Some(n @ '0') => match chars.next() { - Some('C') | Some('d') | Some('e') | Some('f') | Some('G') | Some('g') - | Some('H') | Some('I') | Some('j') | Some('k') | Some('l') | Some('M') - | Some('m') | Some('S') | Some('s') | Some('U') | Some('u') | Some('V') - | Some('W') | Some('w') | Some('Y') | Some('y') => (), - Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)), - None => return Err("missing format specifier".to_owned()), - }, - Some('A') | Some('a') | Some('B') | Some('b') | Some('C') | Some('c') - | Some('D') | Some('d') | Some('e') | Some('F') | Some('f') | Some('G') - | Some('g') | Some('H') | Some('h') | Some('I') | Some('j') | Some('k') - | Some('l') | Some('M') | Some('m') | Some('n') | Some('P') | Some('p') - | Some('R') | Some('r') | Some('S') | Some('s') | Some('T') | Some('t') - | Some('U') | Some('u') | Some('V') | Some('v') | Some('W') | Some('w') - | Some('X') | Some('x') | Some('Y') | Some('y') | Some('Z') | Some('z') - | Some('+') | Some('%') => (), - Some(n @ '3') | Some(n @ '6') | Some(n @ '9') => match chars.next() { - Some('f') => (), - Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)), + Some(c) => return Err(format!("invalid format specifier: %{}", c)), None => return Err("missing format specifier".to_owned()), }, - Some(c) => return Err(format!("invalid format specifier: %{}", c)), - None => return Err("missing format specifier".to_owned()), - }, - None => break, - _ => continue, + None => break, + _ => continue, + } } } Ok(()) diff --git a/src/meta/date.rs b/src/meta/date.rs index f6b3a6e54..2f82d32ad 100644 --- a/src/meta/date.rs +++ b/src/meta/date.rs @@ -65,7 +65,17 @@ impl Date { val.format("%F").to_string() } } - DateFlag::Formatted(format) => val.format(format).to_string(), + DateFlag::Formatted(format) => { + let vec: Vec<&str> = format.split('\n').collect(); + + if vec.len() == 1 || *val > Local::now() - Duration::seconds(15_778_476) { + // non-recent or only one format + val.format(vec[0]).to_string() + } else { + // recent + val.format(vec[1]).to_string() + } + } } } else { String::from("-") @@ -308,6 +318,62 @@ mod test { fs::remove_file(file_path).unwrap(); } + #[test] + fn test_recent_format_now() { + let mut file_path = env::temp_dir(); + file_path.push("test_recent_format_now.tmp"); + + let creation_date = Local::now(); + let success = cross_platform_touch(&file_path, &creation_date) + .unwrap() + .success(); + assert_eq!(true, success, "failed to exec touch"); + + let colors = Colors::new(ThemeOption::Default); + let date = Date::from(&file_path.metadata().unwrap()); + + let mut flags = Flags::default(); + flags.date = DateFlag::Formatted(String::from("%F\n%H:%M")); + + assert_eq!( + creation_date + .format("%F") + .to_string() + .with(Color::AnsiValue(40)), + date.render(&colors, &flags) + ); + + fs::remove_file(file_path).unwrap(); + } + + #[test] + fn test_recent_format_year_old() { + let mut file_path = env::temp_dir(); + file_path.push("test_recent_format_year_old.tmp"); + + let creation_date = Local::now() - Duration::days(400); + let success = cross_platform_touch(&file_path, &creation_date) + .unwrap() + .success(); + assert_eq!(true, success, "failed to exec touch"); + + let colors = Colors::new(ThemeOption::Default); + let date = Date::from(&file_path.metadata().unwrap()); + + let mut flags = Flags::default(); + flags.date = DateFlag::Formatted(String::from("%F\n%H:%M")); + + assert_eq!( + creation_date + .format("%H:%M") + .to_string() + .with(Color::AnsiValue(36)), + date.render(&colors, &flags) + ); + + fs::remove_file(file_path).unwrap(); + } + #[test] #[cfg(all(not(windows), target_arch = "x86_64"))] fn test_bad_date() {