use crate::error::{MyIdError, MyIdResult};
use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BirthDate(NaiveDate);
impl BirthDate {
const FORMAT: &str = "%Y-%m-%d";
const EXPECTED_LEN: usize = 10;
pub fn parse(value: impl AsRef<str>) -> MyIdResult<Self> {
let raw = value.as_ref().trim();
if raw.len() != Self::EXPECTED_LEN {
return Err(MyIdError::validation(format!(
"birth_date must be YYYY-MM-DD format, got: {raw}"
)));
}
let date = NaiveDate::parse_from_str(raw, Self::FORMAT)
.map_err(|_| MyIdError::validation(format!("birth_date is not a valid date: {raw}")))?;
if date > chrono::Local::now().date_naive() {
return Err(MyIdError::validation(format!(
"birth_date cannot be in the future: {raw}"
)));
}
Ok(Self(date))
}
pub fn as_date(&self) -> NaiveDate {
self.0
}
}
impl std::fmt::Display for BirthDate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.format(Self::FORMAT))
}
}
impl TryFrom<String> for BirthDate {
type Error = MyIdError;
fn try_from(value: String) -> MyIdResult<Self> {
Self::parse(value)
}
}
impl From<BirthDate> for String {
fn from(value: BirthDate) -> Self {
value.to_string()
}
}
impl Serialize for BirthDate {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for BirthDate {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s = String::deserialize(deserializer)?;
Self::parse(&s).map_err(serde::de::Error::custom)
}
}