db/herodb/src/models/mcc/contacts.rs
2025-04-20 09:34:18 +02:00

122 lines
3.7 KiB
Rust

use serde::{Deserialize, Serialize};
use crate::db::{Model, Storable, DB, DbError, DbResult};
use crate::models::mcc::event::Event;
use chrono::Utc;
/// Contact represents a contact entry in an address book
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contact {
// Database ID
pub id: u32, // Database ID (assigned by DBHandler)
// Content fields
pub created_at: i64, // Unix epoch timestamp
pub modified_at: i64, // Unix epoch timestamp
pub first_name: String,
pub last_name: String,
pub email: String,
pub group: String, // Reference to a dns name, each group has a globally unique dns
pub groups: Vec<u32>, // Groups this contact belongs to (references Circle IDs)
}
impl Contact {
/// Create a new contact
pub fn new(id: u32, first_name: String, last_name: String, email: String, group: String) -> Self {
let now = Utc::now().timestamp();
Self {
id,
created_at: now,
modified_at: now,
first_name,
last_name,
email,
group,
groups: Vec::new(),
}
}
/// Add a group to this contact
pub fn add_group(&mut self, group_id: u32) {
if !self.groups.contains(&group_id) {
self.groups.push(group_id);
}
}
/// Remove a group from this contact
pub fn remove_group(&mut self, group_id: u32) {
self.groups.retain(|&id| id != group_id);
}
/// Filter by groups - returns true if this contact belongs to any of the specified groups
pub fn filter_by_groups(&self, groups: &[u32]) -> bool {
groups.iter().any(|g| self.groups.contains(g))
}
/// Search by name - returns true if the name contains the query (case-insensitive)
pub fn search_by_name(&self, query: &str) -> bool {
let full_name = self.full_name().to_lowercase();
query.to_lowercase().split_whitespace().all(|word| full_name.contains(word))
}
/// Search by email - returns true if the email contains the query (case-insensitive)
pub fn search_by_email(&self, query: &str) -> bool {
self.email.to_lowercase().contains(&query.to_lowercase())
}
/// Get events where this contact is an attendee
pub fn get_events(&self, db: &DB) -> DbResult<Vec<Event>> {
let all_events = db.list::<Event>()?;
let contact_events = all_events
.into_iter()
.filter(|event| event.attendees.contains(&self.email))
.collect();
Ok(contact_events)
}
/// Update the contact's information
pub fn update(&mut self, first_name: Option<String>, last_name: Option<String>, email: Option<String>, group: Option<String>) {
if let Some(first_name) = first_name {
self.first_name = first_name;
}
if let Some(last_name) = last_name {
self.last_name = last_name;
}
if let Some(email) = email {
self.email = email;
}
if let Some(group) = group {
self.group = group;
}
self.modified_at = Utc::now().timestamp();
}
/// Update the contact's groups
pub fn update_groups(&mut self, groups: Vec<u32>) {
self.groups = groups;
self.modified_at = Utc::now().timestamp();
}
/// Get the full name of the contact
pub fn full_name(&self) -> String {
format!("{} {}", self.first_name, self.last_name)
}
}
// Implement Storable trait (provides default dump/load)
impl Storable for Contact {}
// Implement SledModel trait
impl Model for Contact {
fn get_id(&self) -> u32 {
self.id
}
fn db_prefix() -> &'static str {
"contact"
}
}