mascarpone/src/models/birthday.rs

94 lines
2.5 KiB
Rust
Raw Normal View History

2025-11-27 13:45:21 -06:00
use chrono::Local;
use sqlx::sqlite::SqliteRow;
use sqlx::{FromRow, Row};
use std::fmt::Display;
use std::str::FromStr;
use crate::models::YearOptionalDate;
#[derive(Debug, Clone)]
pub struct Text {
// language: Option<String>,
pub value: String,
}
#[derive(Debug, Clone)]
pub enum Birthday {
Date(YearOptionalDate),
Text(Text),
}
impl Display for Birthday {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let str = match self {
Birthday::Date(date) => date.to_string(),
Birthday::Text(t) => t.value.clone(),
};
write!(f, "{}", str)
}
}
impl Birthday {
pub fn next_occurrence(&self) -> Option<chrono::NaiveDate> {
match &self {
Birthday::Text(_) => None,
Birthday::Date(date) => Some(date.next_month_day_occurrence()?),
}
}
pub fn until_next(&self) -> Option<chrono::TimeDelta> {
self.next_occurrence()
.map(|when| when.signed_duration_since(Local::now().date_naive()))
}
/// None if this is a text birthday or doesn't have a year
pub fn age(&self) -> Option<u32> {
match &self {
Birthday::Text(_) => None,
Birthday::Date(date) => date
.to_date_naive()
.map(|birthdate| Local::now().date_naive().years_since(birthdate))?,
}
}
pub fn serialize(&self) -> String {
match &self {
Birthday::Text(text) => text.value.clone(),
Birthday::Date(date) => date.serialize(),
}
}
}
impl FromStr for Birthday {
type Err = ();
fn from_str(str: &str) -> Result<Self, Self::Err> {
if let Some(date) = YearOptionalDate::from_str(str).ok() {
Ok(Birthday::Date(date))
} else {
Ok(Birthday::Text(super::birthday::Text {
value: str.to_string(),
}))
}
}
}
impl FromRow<'_, SqliteRow> for Birthday {
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
let birthday_str = row.try_get("birthday")?;
Ok(Birthday::from_str(birthday_str).unwrap())
}
}
use sqlx::{Database, Decode, Sqlite};
impl<'r> Decode<'r, Sqlite> for Birthday
where
&'r str: Decode<'r, Sqlite>,
{
fn decode(
value: <Sqlite as Database>::ValueRef<'r>,
) -> Result<Birthday, Box<dyn std::error::Error + 'static + Send + Sync>> {
let value = <&str as Decode<Sqlite>>::decode(value)?;
Ok(Birthday::from_str(value).unwrap())
}
}