mascarpone/src/models/contact.rs

87 lines
2.1 KiB
Rust
Raw Normal View History

2025-11-27 13:45:21 -06:00
use chrono::{DateTime, NaiveDate, Utc};
use sqlx::sqlite::SqliteRow;
use sqlx::{FromRow, Row};
use std::str::FromStr;
use super::Birthday;
use crate::db::DbId;
#[derive(Clone, Debug)]
pub struct Contact {
pub id: DbId,
pub birthday: Option<Birthday>,
pub manually_freshened_at: Option<DateTime<Utc>>,
}
#[derive(Clone, Debug)]
pub struct HydratedContact {
pub contact: Contact,
pub last_mention_date: Option<NaiveDate>,
pub names: Vec<String>,
}
impl std::ops::Deref for HydratedContact {
type Target = Contact;
fn deref(&self) -> &Self::Target {
&self.contact
}
}
impl HydratedContact {
pub fn display_name(&self) -> String {
if let Some(name) = self.names.first() {
name.clone()
} else {
"(unnamed)".to_string()
}
}
}
pub type ContactTrie = radix_trie::Trie<String, DbId>;
impl FromRow<'_, SqliteRow> for Contact {
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
let id: DbId = row.try_get("id")?;
let birthday = Birthday::from_row(row).ok();
let manually_freshened_at = row
.try_get::<String, &str>("manually_freshened_at")
.ok()
.and_then(|str| {
DateTime::parse_from_str(&str, "%+")
.ok()
.map(|d| d.to_utc())
});
Ok(Self {
id,
birthday,
manually_freshened_at,
})
}
}
impl FromRow<'_, SqliteRow> for HydratedContact {
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
let contact = Contact::from_row(row)?;
let names_str: String = row.try_get("names").unwrap_or("".to_string());
let names = if names_str.is_empty() {
vec![]
} else {
names_str.split('\x1c').map(|s| s.to_string()).collect()
};
let last_mention_date = row
.try_get::<String, &str>("last_mention_date")
.ok()
.and_then(|str| NaiveDate::from_str(&str).ok());
Ok(Self {
contact,
names,
last_mention_date,
})
}
}