pub(crate) fn parse_date_flexible(s: &str) -> Option<chrono::NaiveDate> {
use chrono::NaiveDate;
let s = s.trim();
NaiveDate::parse_from_str(s, "%Y-%m-%d")
.ok()
.or_else(|| {
chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S")
.ok()
.map(|dt| dt.date())
})
.or_else(|| {
let base = s.split('.').next().unwrap_or(s);
let base = base.split('+').next().unwrap_or(base);
chrono::NaiveDateTime::parse_from_str(base, "%Y-%m-%dT%H:%M:%S")
.ok()
.map(|dt| dt.date())
})
.or_else(|| {
s.get(..10)
.and_then(|head| NaiveDate::parse_from_str(head, "%Y-%m-%d").ok())
})
}
pub(crate) fn parse_scalar_i64(raw: &str) -> crate::error::Result<i64> {
let t = raw.trim();
t.parse::<i64>()
.or_else(|_| t.parse::<f64>().map(|x| x as i64))
.map_err(|_| anyhow::anyhow!("invalid numeric scalar: {:?}", t))
}
#[cfg(test)]
mod tests {
use super::*;
fn d(s: &str) -> chrono::NaiveDate {
chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d").unwrap()
}
#[test]
fn parse_date_flexible_handles_db_scalar_forms() {
assert_eq!(parse_date_flexible("2023-06-15"), Some(d("2023-06-15")));
assert_eq!(
parse_date_flexible("2023-06-15 14:32:00"),
Some(d("2023-06-15"))
);
assert_eq!(
parse_date_flexible("2023-06-15T14:32:00"),
Some(d("2023-06-15"))
);
assert_eq!(
parse_date_flexible("2023-06-15T14:32:00.123456"),
Some(d("2023-06-15"))
);
assert_eq!(
parse_date_flexible("2024-12-30 00:00:00.123456+00"),
Some(d("2024-12-30"))
);
}
#[test]
fn parse_date_flexible_rejects_non_dates() {
assert!(parse_date_flexible("").is_none());
assert!(parse_date_flexible("not-a-date").is_none());
assert!(parse_date_flexible("12345").is_none());
}
#[test]
fn parse_scalar_i64_accepts_int_and_float_forms() {
assert_eq!(parse_scalar_i64("42").unwrap(), 42);
assert_eq!(parse_scalar_i64(" -7 ").unwrap(), -7);
assert_eq!(parse_scalar_i64("42.0").unwrap(), 42);
assert_eq!(parse_scalar_i64("42.9").unwrap(), 42); }
#[test]
fn parse_scalar_i64_rejects_non_numeric() {
assert!(parse_scalar_i64("").is_err());
assert!(parse_scalar_i64("not-a-number").is_err());
}
}