use chrono::NaiveDate;
use std::path::Path;
fn fti_digits(s: &str) -> bool {
s.len() >= 2 && s.chars().take(2).all(|i| i.is_ascii_digit())
}
pub fn parse_diary_date(par: &str, fin: &str) -> Option<NaiveDate> {
if !fti_digits(fin) || par.len() < 4 {
return None;
}
let y = par.parse::<i32>().ok()?;
let m = fin[..2].parse::<u32>().unwrap();
// verify the day info
let mut dayinf = &fin[2..];
if dayinf.starts_with(|i| matches!(i, '-' | '_')) {
dayinf = &dayinf[1..];
}
if !fti_digits(dayinf) {
return None;
}
let d = dayinf[..2].parse::<u32>().unwrap();
chrono::NaiveDate::from_ymd_opt(y, m, d)
}
/// tries to parse a diary entry path to extract the reference date
/// should be usually given a path containing at least 2 components
pub fn parse_diary_date_from_path(x: &Path) -> Option<NaiveDate> {
let mut fin = x;
let mut fins = fin.file_name()?.to_str()?;
// we allow 1 additional path component after the date part
if !fti_digits(fins) {
fin = x.parent()?;
fins = fin.file_name()?.to_str()?;
if !fti_digits(fins) {
return None;
}
}
let par = fin.parent()?.file_name()?.to_str()?;
parse_diary_date(par, fins)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn diary_standard() {
assert_eq!(parse_diary_date_from_path(Path::new("201/01_01")), None);
assert_eq!(parse_diary_date_from_path(Path::new("2016/0")), None);
assert_eq!(
parse_diary_date_from_path(Path::new("2016/08_28")),
Some(NaiveDate::from_ymd(2016, 08, 28))
);
assert_eq!(
parse_diary_date_from_path(Path::new("teller/2016/08_28")),
Some(NaiveDate::from_ymd(2016, 08, 28))
);
assert_eq!(
parse_diary_date_from_path(Path::new("teller/2016/08_28nox/fluppig.jpg")),
Some(NaiveDate::from_ymd(2016, 08, 28))
);
assert_eq!(
parse_diary_date_from_path(Path::new("/blog/2017/1124y_vf.html")),
Some(NaiveDate::from_ymd(2017, 11, 24))
);
}
}