Skip to content

Commit cae336d

Browse files
committed
Date format supports recent/non-recent format
1 parent 0a3a6f1 commit cae336d

File tree

2 files changed

+118
-40
lines changed

2 files changed

+118
-40
lines changed

src/app.rs

Lines changed: 51 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -290,51 +290,63 @@ fn validate_date_argument(arg: String) -> Result<(), String> {
290290
}
291291

292292
pub fn validate_time_format(formatter: &str) -> Result<(), String> {
293-
let mut chars = formatter.chars();
294-
loop {
295-
match chars.next() {
296-
Some('%') => match chars.next() {
297-
Some('.') => match chars.next() {
298-
Some('f') => (),
293+
let str = formatter.to_string();
294+
let vec: Vec<&str> = str.split('\n').collect();
295+
296+
if vec.len() > 2 {
297+
return Err("invalid format, can only contain one newline separator".to_owned());
298+
}
299+
300+
for s in vec {
301+
let mut chars = s.chars();
302+
303+
loop {
304+
match chars.next() {
305+
Some('%') => match chars.next() {
306+
Some('.') => match chars.next() {
307+
Some('f') => (),
308+
Some(n @ '3') | Some(n @ '6') | Some(n @ '9') => match chars.next() {
309+
Some('f') => (),
310+
Some(c) => {
311+
return Err(format!("invalid format specifier: %.{}{}", n, c))
312+
}
313+
None => return Err("missing format specifier".to_owned()),
314+
},
315+
Some(c) => return Err(format!("invalid format specifier: %.{}", c)),
316+
None => return Err("missing format specifier".to_owned()),
317+
},
318+
Some(n @ ':') | Some(n @ '#') => match chars.next() {
319+
Some('z') => (),
320+
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
321+
None => return Err("missing format specifier".to_owned()),
322+
},
323+
Some(n @ '-') | Some(n @ '_') | Some(n @ '0') => match chars.next() {
324+
Some('C') | Some('d') | Some('e') | Some('f') | Some('G') | Some('g')
325+
| Some('H') | Some('I') | Some('j') | Some('k') | Some('l') | Some('M')
326+
| Some('m') | Some('S') | Some('s') | Some('U') | Some('u') | Some('V')
327+
| Some('W') | Some('w') | Some('Y') | Some('y') => (),
328+
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
329+
None => return Err("missing format specifier".to_owned()),
330+
},
331+
Some('A') | Some('a') | Some('B') | Some('b') | Some('C') | Some('c')
332+
| Some('D') | Some('d') | Some('e') | Some('F') | Some('f') | Some('G')
333+
| Some('g') | Some('H') | Some('h') | Some('I') | Some('j') | Some('k')
334+
| Some('l') | Some('M') | Some('m') | Some('n') | Some('P') | Some('p')
335+
| Some('R') | Some('r') | Some('S') | Some('s') | Some('T') | Some('t')
336+
| Some('U') | Some('u') | Some('V') | Some('v') | Some('W') | Some('w')
337+
| Some('X') | Some('x') | Some('Y') | Some('y') | Some('Z') | Some('z')
338+
| Some('+') | Some('%') => (),
299339
Some(n @ '3') | Some(n @ '6') | Some(n @ '9') => match chars.next() {
300340
Some('f') => (),
301-
Some(c) => return Err(format!("invalid format specifier: %.{}{}", n, c)),
341+
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
302342
None => return Err("missing format specifier".to_owned()),
303343
},
304-
Some(c) => return Err(format!("invalid format specifier: %.{}", c)),
305-
None => return Err("missing format specifier".to_owned()),
306-
},
307-
Some(n @ ':') | Some(n @ '#') => match chars.next() {
308-
Some('z') => (),
309-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
310-
None => return Err("missing format specifier".to_owned()),
311-
},
312-
Some(n @ '-') | Some(n @ '_') | Some(n @ '0') => match chars.next() {
313-
Some('C') | Some('d') | Some('e') | Some('f') | Some('G') | Some('g')
314-
| Some('H') | Some('I') | Some('j') | Some('k') | Some('l') | Some('M')
315-
| Some('m') | Some('S') | Some('s') | Some('U') | Some('u') | Some('V')
316-
| Some('W') | Some('w') | Some('Y') | Some('y') => (),
317-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
318-
None => return Err("missing format specifier".to_owned()),
319-
},
320-
Some('A') | Some('a') | Some('B') | Some('b') | Some('C') | Some('c')
321-
| Some('D') | Some('d') | Some('e') | Some('F') | Some('f') | Some('G')
322-
| Some('g') | Some('H') | Some('h') | Some('I') | Some('j') | Some('k')
323-
| Some('l') | Some('M') | Some('m') | Some('n') | Some('P') | Some('p')
324-
| Some('R') | Some('r') | Some('S') | Some('s') | Some('T') | Some('t')
325-
| Some('U') | Some('u') | Some('V') | Some('v') | Some('W') | Some('w')
326-
| Some('X') | Some('x') | Some('Y') | Some('y') | Some('Z') | Some('z')
327-
| Some('+') | Some('%') => (),
328-
Some(n @ '3') | Some(n @ '6') | Some(n @ '9') => match chars.next() {
329-
Some('f') => (),
330-
Some(c) => return Err(format!("invalid format specifier: %{}{}", n, c)),
344+
Some(c) => return Err(format!("invalid format specifier: %{}", c)),
331345
None => return Err("missing format specifier".to_owned()),
332346
},
333-
Some(c) => return Err(format!("invalid format specifier: %{}", c)),
334-
None => return Err("missing format specifier".to_owned()),
335-
},
336-
None => break,
337-
_ => continue,
347+
None => break,
348+
_ => continue,
349+
}
338350
}
339351
}
340352
Ok(())

src/meta/date.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,17 @@ impl Date {
6565
val.format("%F").to_string()
6666
}
6767
}
68-
DateFlag::Formatted(format) => val.format(format).to_string(),
68+
DateFlag::Formatted(format) => {
69+
let vec: Vec<&str> = format.split("\n").collect();
70+
71+
if vec.len() == 1 || *val > Local::now() - Duration::seconds(15_778_476) {
72+
// non-recent or only one format
73+
val.format(vec[0]).to_string()
74+
} else {
75+
// recent
76+
val.format(vec[1]).to_string()
77+
}
78+
}
6979
}
7080
} else {
7181
String::from("-")
@@ -308,6 +318,62 @@ mod test {
308318
fs::remove_file(file_path).unwrap();
309319
}
310320

321+
#[test]
322+
fn test_recent_format_now() {
323+
let mut file_path = env::temp_dir();
324+
file_path.push("test_recent_format_now.tmp");
325+
326+
let creation_date = Local::now();
327+
let success = cross_platform_touch(&file_path, &creation_date)
328+
.unwrap()
329+
.success();
330+
assert_eq!(true, success, "failed to exec touch");
331+
332+
let colors = Colors::new(ThemeOption::Default);
333+
let date = Date::from(&file_path.metadata().unwrap());
334+
335+
let mut flags = Flags::default();
336+
flags.date = DateFlag::Formatted(String::from("%F\n%H:%M"));
337+
338+
assert_eq!(
339+
creation_date
340+
.format("%F")
341+
.to_string()
342+
.with(Color::AnsiValue(40)),
343+
date.render(&colors, &flags)
344+
);
345+
346+
fs::remove_file(file_path).unwrap();
347+
}
348+
349+
#[test]
350+
fn test_recent_format_year_old() {
351+
let mut file_path = env::temp_dir();
352+
file_path.push("test_recent_format_year_old.tmp");
353+
354+
let creation_date = Local::now() - Duration::days(400);
355+
let success = cross_platform_touch(&file_path, &creation_date)
356+
.unwrap()
357+
.success();
358+
assert_eq!(true, success, "failed to exec touch");
359+
360+
let colors = Colors::new(ThemeOption::Default);
361+
let date = Date::from(&file_path.metadata().unwrap());
362+
363+
let mut flags = Flags::default();
364+
flags.date = DateFlag::Formatted(String::from("%F\n%H:%M"));
365+
366+
assert_eq!(
367+
creation_date
368+
.format("%H:%M")
369+
.to_string()
370+
.with(Color::AnsiValue(36)),
371+
date.render(&colors, &flags)
372+
);
373+
374+
fs::remove_file(file_path).unwrap();
375+
}
376+
311377
#[test]
312378
#[cfg(all(not(windows), target_arch = "x86_64"))]
313379
fn test_bad_date() {

0 commit comments

Comments
 (0)