use anyhow::anyhow;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use regex::{Captures, Regex};
use std::sync::LazyLock;
static RE_NAIVE_FILENAME: LazyLock<Regex> = LazyLock::new(|| {
regex::Regex::new(
r"(\d{4})[-_]?(\d{2})[-_]?(\d{2})(\D+(\d{2})[-_:]?(\d{2})[-_:]?(\d{2}))?[-_]?",
)
.expect("Failed to compile regex")
});
#[derive(Debug, Default)]
pub struct NaiveFileNameParser {}
impl FileNameToDateTransformer for NaiveFileNameParser {
fn get_regex(&self) -> &Regex {
&RE_NAIVE_FILENAME
}
fn transform(&self, capture: &Captures) -> anyhow::Result<Option<NaiveDateTime>> {
let year = capture
.get(1)
.ok_or_else(|| anyhow!("Regex did not find year group"))?
.as_str()
.parse::<i32>()?;
let month = capture
.get(2)
.ok_or_else(|| anyhow!("Regex did not find month group"))?
.as_str()
.parse::<u32>()?;
let day = capture
.get(3)
.ok_or_else(|| anyhow!("Regex did not find day group"))?
.as_str()
.parse::<u32>()?;
let time = if let (Some(hour), Some(minute), Some(second)) =
(capture.get(5), capture.get(6), capture.get(7))
{
NaiveTime::from_hms_opt(
hour.as_str().parse::<u32>()?,
minute.as_str().parse::<u32>()?,
second.as_str().parse::<u32>()?,
)
.ok_or_else(|| anyhow!("Invalid time"))?
} else {
NaiveTime::MIN
};
let date =
NaiveDate::from_ymd_opt(year, month, day).ok_or_else(|| anyhow!("Invalid date"))?;
Ok(Some(NaiveDateTime::new(date, time)))
}
}
pub trait FileNameToDateTransformer {
fn get_regex(&self) -> ®ex::Regex;
fn transform(&self, capture: ®ex::Captures) -> anyhow::Result<Option<NaiveDateTime>>;
fn try_transform_name(&self, name: &str) -> anyhow::Result<Option<(NaiveDateTime, String)>> {
let all_matches = self.get_regex().captures_iter(name);
for current_match in all_matches {
let matched = current_match.get(0).map_or("", |m| m.as_str());
match self.transform(¤t_match) {
Ok(Some(dt)) => return Ok(Some((dt, name.replace(matched, "")))),
Ok(None) => {}
Err(e) => {
log::error!("Error: {e:?}");
}
}
}
Ok(None) }
}