...
This commit is contained in:
		
							
								
								
									
										399
									
								
								herodb/src/cmd/dbexample_mcc/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								herodb/src/cmd/dbexample_mcc/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,399 @@
 | 
			
		||||
use chrono::{Utc, Duration};
 | 
			
		||||
use herodb::db::DBBuilder;
 | 
			
		||||
use herodb::models::mcc::{
 | 
			
		||||
    Calendar, Event,
 | 
			
		||||
    Email, Attachment, Envelope,
 | 
			
		||||
    Contact, Message
 | 
			
		||||
};
 | 
			
		||||
use herodb::models::circle::Circle;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::fs;
 | 
			
		||||
 | 
			
		||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
    println!("DB Example MCC: Mail, Calendar, Contacts with Group Support");
 | 
			
		||||
    println!("=======================================================");
 | 
			
		||||
 | 
			
		||||
    // Create a temporary directory for the database
 | 
			
		||||
    let db_path = PathBuf::from("/tmp/dbexample_mcc");
 | 
			
		||||
    if db_path.exists() {
 | 
			
		||||
        fs::remove_dir_all(&db_path)?;
 | 
			
		||||
    }
 | 
			
		||||
    fs::create_dir_all(&db_path)?;
 | 
			
		||||
    println!("Database path: {:?}", db_path);
 | 
			
		||||
 | 
			
		||||
    // Create a database instance with our models registered
 | 
			
		||||
    let db = DBBuilder::new(&db_path)
 | 
			
		||||
        .register_model::<Calendar>()
 | 
			
		||||
        .register_model::<Event>()
 | 
			
		||||
        .register_model::<Email>()
 | 
			
		||||
        .register_model::<Contact>()
 | 
			
		||||
        .register_model::<Message>()
 | 
			
		||||
        .register_model::<Circle>()
 | 
			
		||||
        .build()?;
 | 
			
		||||
 | 
			
		||||
    println!("\n1. Creating Circles (Groups)");
 | 
			
		||||
    println!("---------------------------");
 | 
			
		||||
 | 
			
		||||
    // Create circles (groups)
 | 
			
		||||
    let work_circle = Circle::new(
 | 
			
		||||
        1, 
 | 
			
		||||
        "Work".to_string(), 
 | 
			
		||||
        "Work-related communications".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    let family_circle = Circle::new(
 | 
			
		||||
        2, 
 | 
			
		||||
        "Family".to_string(), 
 | 
			
		||||
        "Family communications".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    let friends_circle = Circle::new(
 | 
			
		||||
        3, 
 | 
			
		||||
        "Friends".to_string(), 
 | 
			
		||||
        "Friends communications".to_string()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Insert circles
 | 
			
		||||
    db.set::<Circle>(&work_circle)?;
 | 
			
		||||
    db.set::<Circle>(&family_circle)?;
 | 
			
		||||
    db.set::<Circle>(&friends_circle)?;
 | 
			
		||||
 | 
			
		||||
    println!("Created circles:");
 | 
			
		||||
    println!("  - Circle #{}: {}", work_circle.id, work_circle.name);
 | 
			
		||||
    println!("  - Circle #{}: {}", family_circle.id, family_circle.name);
 | 
			
		||||
    println!("  - Circle #{}: {}", friends_circle.id, friends_circle.name);
 | 
			
		||||
 | 
			
		||||
    println!("\n2. Creating Contacts with Group Support");
 | 
			
		||||
    println!("------------------------------------");
 | 
			
		||||
 | 
			
		||||
    // Create contacts
 | 
			
		||||
    let mut john = Contact::new(
 | 
			
		||||
        1,
 | 
			
		||||
        "John".to_string(),
 | 
			
		||||
        "Doe".to_string(),
 | 
			
		||||
        "john.doe@example.com".to_string(),
 | 
			
		||||
        "work".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    john.add_group(work_circle.id);
 | 
			
		||||
    
 | 
			
		||||
    let mut alice = Contact::new(
 | 
			
		||||
        2,
 | 
			
		||||
        "Alice".to_string(),
 | 
			
		||||
        "Smith".to_string(),
 | 
			
		||||
        "alice.smith@example.com".to_string(),
 | 
			
		||||
        "family".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    alice.add_group(family_circle.id);
 | 
			
		||||
    
 | 
			
		||||
    let mut bob = Contact::new(
 | 
			
		||||
        3,
 | 
			
		||||
        "Bob".to_string(),
 | 
			
		||||
        "Johnson".to_string(),
 | 
			
		||||
        "bob.johnson@example.com".to_string(),
 | 
			
		||||
        "friends".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    bob.add_group(friends_circle.id);
 | 
			
		||||
    bob.add_group(work_circle.id); // Bob is both a friend and a work contact
 | 
			
		||||
 | 
			
		||||
    // Insert contacts
 | 
			
		||||
    db.set::<Contact>(&john)?;
 | 
			
		||||
    db.set::<Contact>(&alice)?;
 | 
			
		||||
    db.set::<Contact>(&bob)?;
 | 
			
		||||
 | 
			
		||||
    println!("Created contacts:");
 | 
			
		||||
    println!("  - {}: {} (Groups: {:?})", john.full_name(), john.email, john.groups);
 | 
			
		||||
    println!("  - {}: {} (Groups: {:?})", alice.full_name(), alice.email, alice.groups);
 | 
			
		||||
    println!("  - {}: {} (Groups: {:?})", bob.full_name(), bob.email, bob.groups);
 | 
			
		||||
 | 
			
		||||
    println!("\n3. Creating Calendars with Group Support");
 | 
			
		||||
    println!("-------------------------------------");
 | 
			
		||||
 | 
			
		||||
    // Create calendars
 | 
			
		||||
    let mut work_calendar = Calendar::new(
 | 
			
		||||
        1,
 | 
			
		||||
        "Work Calendar".to_string(),
 | 
			
		||||
        "Work-related events".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    work_calendar.add_group(work_circle.id);
 | 
			
		||||
    
 | 
			
		||||
    let mut personal_calendar = Calendar::new(
 | 
			
		||||
        2,
 | 
			
		||||
        "Personal Calendar".to_string(),
 | 
			
		||||
        "Personal events".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    personal_calendar.add_group(family_circle.id);
 | 
			
		||||
    personal_calendar.add_group(friends_circle.id);
 | 
			
		||||
 | 
			
		||||
    // Insert calendars
 | 
			
		||||
    db.set::<Calendar>(&work_calendar)?;
 | 
			
		||||
    db.set::<Calendar>(&personal_calendar)?;
 | 
			
		||||
 | 
			
		||||
    println!("Created calendars:");
 | 
			
		||||
    println!("  - {}: {} (Groups: {:?})", work_calendar.id, work_calendar.title, work_calendar.groups);
 | 
			
		||||
    println!("  - {}: {} (Groups: {:?})", personal_calendar.id, personal_calendar.title, personal_calendar.groups);
 | 
			
		||||
 | 
			
		||||
    println!("\n4. Creating Events with Group Support");
 | 
			
		||||
    println!("----------------------------------");
 | 
			
		||||
 | 
			
		||||
    // Create events
 | 
			
		||||
    let now = Utc::now();
 | 
			
		||||
    let tomorrow = now + Duration::days(1);
 | 
			
		||||
    let next_week = now + Duration::days(7);
 | 
			
		||||
 | 
			
		||||
    let mut work_meeting = Event::new(
 | 
			
		||||
        1,
 | 
			
		||||
        work_calendar.id,
 | 
			
		||||
        "Team Meeting".to_string(),
 | 
			
		||||
        "Weekly team sync".to_string(),
 | 
			
		||||
        "Conference Room A".to_string(),
 | 
			
		||||
        tomorrow,
 | 
			
		||||
        tomorrow + Duration::hours(1),
 | 
			
		||||
        "organizer@example.com".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    work_meeting.add_group(work_circle.id);
 | 
			
		||||
    work_meeting.add_attendee(john.email.clone());
 | 
			
		||||
    work_meeting.add_attendee(bob.email.clone());
 | 
			
		||||
 | 
			
		||||
    let mut family_dinner = Event::new(
 | 
			
		||||
        2,
 | 
			
		||||
        personal_calendar.id,
 | 
			
		||||
        "Family Dinner".to_string(),
 | 
			
		||||
        "Weekly family dinner".to_string(),
 | 
			
		||||
        "Home".to_string(),
 | 
			
		||||
        next_week,
 | 
			
		||||
        next_week + Duration::hours(2),
 | 
			
		||||
        "me@example.com".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    family_dinner.add_group(family_circle.id);
 | 
			
		||||
    family_dinner.add_attendee(alice.email.clone());
 | 
			
		||||
 | 
			
		||||
    // Insert events
 | 
			
		||||
    db.set::<Event>(&work_meeting)?;
 | 
			
		||||
    db.set::<Event>(&family_dinner)?;
 | 
			
		||||
 | 
			
		||||
    println!("Created events:");
 | 
			
		||||
    println!("  - {}: {} on {} (Groups: {:?})", 
 | 
			
		||||
        work_meeting.id, 
 | 
			
		||||
        work_meeting.title, 
 | 
			
		||||
        work_meeting.start_time.format("%Y-%m-%d %H:%M"), 
 | 
			
		||||
        work_meeting.groups
 | 
			
		||||
    );
 | 
			
		||||
    println!("  - {}: {} on {} (Groups: {:?})", 
 | 
			
		||||
        family_dinner.id, 
 | 
			
		||||
        family_dinner.title, 
 | 
			
		||||
        family_dinner.start_time.format("%Y-%m-%d %H:%M"), 
 | 
			
		||||
        family_dinner.groups
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    println!("\n5. Creating Emails with Group Support");
 | 
			
		||||
    println!("----------------------------------");
 | 
			
		||||
 | 
			
		||||
    // Create emails
 | 
			
		||||
    let mut work_email = Email::new(
 | 
			
		||||
        1,
 | 
			
		||||
        101,
 | 
			
		||||
        1,
 | 
			
		||||
        "INBOX".to_string(),
 | 
			
		||||
        "Here are the meeting notes from yesterday's discussion.".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    work_email.add_group(work_circle.id);
 | 
			
		||||
    
 | 
			
		||||
    let work_attachment = Attachment {
 | 
			
		||||
        filename: "meeting_notes.pdf".to_string(),
 | 
			
		||||
        content_type: "application/pdf".to_string(),
 | 
			
		||||
        hash: "abc123def456".to_string(),
 | 
			
		||||
        size: 1024,
 | 
			
		||||
    };
 | 
			
		||||
    work_email.add_attachment(work_attachment);
 | 
			
		||||
    
 | 
			
		||||
    let work_envelope = Envelope {
 | 
			
		||||
        date: now.timestamp(),
 | 
			
		||||
        subject: "Meeting Notes".to_string(),
 | 
			
		||||
        from: vec!["john.doe@example.com".to_string()],
 | 
			
		||||
        sender: vec!["john.doe@example.com".to_string()],
 | 
			
		||||
        reply_to: vec!["john.doe@example.com".to_string()],
 | 
			
		||||
        to: vec!["me@example.com".to_string()],
 | 
			
		||||
        cc: vec!["bob.johnson@example.com".to_string()],
 | 
			
		||||
        bcc: vec![],
 | 
			
		||||
        in_reply_to: "".to_string(),
 | 
			
		||||
        message_id: "msg123@example.com".to_string(),
 | 
			
		||||
    };
 | 
			
		||||
    work_email.set_envelope(work_envelope);
 | 
			
		||||
 | 
			
		||||
    let mut family_email = Email::new(
 | 
			
		||||
        2,
 | 
			
		||||
        102,
 | 
			
		||||
        2,
 | 
			
		||||
        "INBOX".to_string(),
 | 
			
		||||
        "Looking forward to seeing you at dinner next week!".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    family_email.add_group(family_circle.id);
 | 
			
		||||
    
 | 
			
		||||
    let family_envelope = Envelope {
 | 
			
		||||
        date: now.timestamp(),
 | 
			
		||||
        subject: "Family Dinner".to_string(),
 | 
			
		||||
        from: vec!["alice.smith@example.com".to_string()],
 | 
			
		||||
        sender: vec!["alice.smith@example.com".to_string()],
 | 
			
		||||
        reply_to: vec!["alice.smith@example.com".to_string()],
 | 
			
		||||
        to: vec!["me@example.com".to_string()],
 | 
			
		||||
        cc: vec![],
 | 
			
		||||
        bcc: vec![],
 | 
			
		||||
        in_reply_to: "".to_string(),
 | 
			
		||||
        message_id: "msg456@example.com".to_string(),
 | 
			
		||||
    };
 | 
			
		||||
    family_email.set_envelope(family_envelope);
 | 
			
		||||
 | 
			
		||||
    // Insert emails
 | 
			
		||||
    db.set::<Email>(&work_email)?;
 | 
			
		||||
    db.set::<Email>(&family_email)?;
 | 
			
		||||
 | 
			
		||||
    println!("Created emails:");
 | 
			
		||||
    println!("  - From: {}, Subject: {} (Groups: {:?})", 
 | 
			
		||||
        work_email.envelope.as_ref().unwrap().from[0], 
 | 
			
		||||
        work_email.envelope.as_ref().unwrap().subject,
 | 
			
		||||
        work_email.groups
 | 
			
		||||
    );
 | 
			
		||||
    println!("  - From: {}, Subject: {} (Groups: {:?})", 
 | 
			
		||||
        family_email.envelope.as_ref().unwrap().from[0], 
 | 
			
		||||
        family_email.envelope.as_ref().unwrap().subject,
 | 
			
		||||
        family_email.groups
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    println!("\n6. Creating Messages (Chat) with Group Support");
 | 
			
		||||
    println!("-----------------------------------------");
 | 
			
		||||
 | 
			
		||||
    // Create messages
 | 
			
		||||
    let mut work_chat = Message::new(
 | 
			
		||||
        1,
 | 
			
		||||
        "thread_work_123".to_string(),
 | 
			
		||||
        "john.doe@example.com".to_string(),
 | 
			
		||||
        "Can we move the meeting to 3pm?".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    work_chat.add_group(work_circle.id);
 | 
			
		||||
    work_chat.add_recipient("me@example.com".to_string());
 | 
			
		||||
    work_chat.add_recipient("bob.johnson@example.com".to_string());
 | 
			
		||||
 | 
			
		||||
    let mut friends_chat = Message::new(
 | 
			
		||||
        2,
 | 
			
		||||
        "thread_friends_456".to_string(),
 | 
			
		||||
        "bob.johnson@example.com".to_string(),
 | 
			
		||||
        "Are we still on for the game this weekend?".to_string()
 | 
			
		||||
    );
 | 
			
		||||
    friends_chat.add_group(friends_circle.id);
 | 
			
		||||
    friends_chat.add_recipient("me@example.com".to_string());
 | 
			
		||||
    friends_chat.add_reaction("👍".to_string());
 | 
			
		||||
 | 
			
		||||
    // Insert messages
 | 
			
		||||
    db.set::<Message>(&work_chat)?;
 | 
			
		||||
    db.set::<Message>(&friends_chat)?;
 | 
			
		||||
 | 
			
		||||
    println!("Created messages:");
 | 
			
		||||
    println!("  - From: {}, Content: {} (Groups: {:?})", 
 | 
			
		||||
        work_chat.sender_id, 
 | 
			
		||||
        work_chat.content,
 | 
			
		||||
        work_chat.groups
 | 
			
		||||
    );
 | 
			
		||||
    println!("  - From: {}, Content: {} (Groups: {:?}, Reactions: {:?})", 
 | 
			
		||||
        friends_chat.sender_id, 
 | 
			
		||||
        friends_chat.content,
 | 
			
		||||
        friends_chat.groups,
 | 
			
		||||
        friends_chat.meta.reactions
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    println!("\n7. Demonstrating Utility Methods");
 | 
			
		||||
    println!("------------------------------");
 | 
			
		||||
 | 
			
		||||
    // Filter contacts by group
 | 
			
		||||
    println!("\nFiltering contacts by work group (ID: {}):", work_circle.id);
 | 
			
		||||
    let all_contacts = db.list::<Contact>()?;
 | 
			
		||||
    for contact in all_contacts {
 | 
			
		||||
        if contact.filter_by_groups(&[work_circle.id]) {
 | 
			
		||||
            println!("  - {} ({})", contact.full_name(), contact.email);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Search emails by subject
 | 
			
		||||
    println!("\nSearching emails with subject containing 'Meeting':");
 | 
			
		||||
    let all_emails = db.list::<Email>()?;
 | 
			
		||||
    for email in all_emails {
 | 
			
		||||
        if email.search_by_subject("Meeting") {
 | 
			
		||||
            println!("  - Subject: {}, From: {}", 
 | 
			
		||||
                email.envelope.as_ref().unwrap().subject,
 | 
			
		||||
                email.envelope.as_ref().unwrap().from[0]
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get events for a calendar
 | 
			
		||||
    println!("\nGetting events for Work Calendar (ID: {}):", work_calendar.id);
 | 
			
		||||
    let all_events = db.list::<Event>()?;
 | 
			
		||||
    let work_events: Vec<Event> = all_events
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .filter(|event| event.calendar_id == work_calendar.id)
 | 
			
		||||
        .collect();
 | 
			
		||||
    for event in work_events {
 | 
			
		||||
        println!("  - {}: {} on {}", 
 | 
			
		||||
            event.id, 
 | 
			
		||||
            event.title, 
 | 
			
		||||
            event.start_time.format("%Y-%m-%d %H:%M")
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get attendee contacts for an event
 | 
			
		||||
    println!("\nGetting attendee contacts for Team Meeting (ID: {}):", work_meeting.id);
 | 
			
		||||
    let all_contacts = db.list::<Contact>()?;
 | 
			
		||||
    let attendee_contacts: Vec<Contact> = all_contacts
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .filter(|contact| work_meeting.attendees.contains(&contact.email))
 | 
			
		||||
        .collect();
 | 
			
		||||
    for contact in attendee_contacts {
 | 
			
		||||
        println!("  - {} ({})", contact.full_name(), contact.email);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Convert email to message
 | 
			
		||||
    println!("\nConverting work email to message:");
 | 
			
		||||
    let email_to_message = work_email.to_message(3, "thread_converted_789".to_string());
 | 
			
		||||
    println!("  - Original Email Subject: {}", work_email.envelope.as_ref().unwrap().subject);
 | 
			
		||||
    println!("  - Converted Message Content: {}", email_to_message.content.split('\n').next().unwrap_or(""));
 | 
			
		||||
    println!("  - Converted Message Groups: {:?}", email_to_message.groups);
 | 
			
		||||
 | 
			
		||||
    // Insert the converted message
 | 
			
		||||
    db.set::<Message>(&email_to_message)?;
 | 
			
		||||
 | 
			
		||||
    println!("\n8. Relationship Management");
 | 
			
		||||
    println!("------------------------");
 | 
			
		||||
 | 
			
		||||
    // Get the calendar for an event
 | 
			
		||||
    println!("\nGetting calendar for Family Dinner event (ID: {}):", family_dinner.id);
 | 
			
		||||
    let event_calendar = db.get::<Calendar>(&family_dinner.calendar_id.to_string())?;
 | 
			
		||||
    println!("  - Calendar: {} ({})", event_calendar.title, event_calendar.description);
 | 
			
		||||
 | 
			
		||||
    // Get events for a contact
 | 
			
		||||
    println!("\nGetting events where John Doe is an attendee:");
 | 
			
		||||
    let all_events = db.list::<Event>()?;
 | 
			
		||||
    let john_events: Vec<Event> = all_events
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .filter(|event| event.attendees.contains(&john.email))
 | 
			
		||||
        .collect();
 | 
			
		||||
    for event in john_events {
 | 
			
		||||
        println!("  - {}: {} on {}",
 | 
			
		||||
            event.id,
 | 
			
		||||
            event.title,
 | 
			
		||||
            event.start_time.format("%Y-%m-%d %H:%M")
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get messages in the same thread
 | 
			
		||||
    println!("\nGetting all messages in the work chat thread:");
 | 
			
		||||
    let all_messages = db.list::<Message>()?;
 | 
			
		||||
    let thread_messages: Vec<Message> = all_messages
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .filter(|message| message.thread_id == work_chat.thread_id)
 | 
			
		||||
        .collect();
 | 
			
		||||
    for message in thread_messages {
 | 
			
		||||
        println!("  - From: {}, Content: {}", message.sender_id, message.content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("\nExample completed successfully!");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::core::{SledModel, Storable};
 | 
			
		||||
use crate::db::{SledModel, Storable};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
/// Role represents the role of a member in a circle
 | 
			
		||||
 
 | 
			
		||||
@@ -6,4 +6,4 @@ pub use circle::{Circle, Member, Role};
 | 
			
		||||
pub use name::{Name, Record, RecordType};
 | 
			
		||||
 | 
			
		||||
// Re-export database components
 | 
			
		||||
pub use crate::core::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
 
 | 
			
		||||
@@ -5,5 +5,5 @@ pub mod name;
 | 
			
		||||
pub use circle::{Circle, Member, Role};
 | 
			
		||||
pub use name::{Name, Record, RecordType};
 | 
			
		||||
 | 
			
		||||
// Re-export database components from core module
 | 
			
		||||
pub use crate::core::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
// Re-export database components from db module
 | 
			
		||||
pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::core::{SledModel, Storable};
 | 
			
		||||
use crate::db::{SledModel, Storable};
 | 
			
		||||
 | 
			
		||||
/// Record types for a DNS record
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										316
									
								
								herodb/src/models/mcc/MCC_ENHANCEMENT_PLAN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								herodb/src/models/mcc/MCC_ENHANCEMENT_PLAN.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,316 @@
 | 
			
		||||
# MCC Models Enhancement Plan
 | 
			
		||||
 | 
			
		||||
## 1. Current State Analysis
 | 
			
		||||
 | 
			
		||||
The current MCC module consists of:
 | 
			
		||||
- **Mail**: Email, Attachment, Envelope models
 | 
			
		||||
- **Calendar**: Calendar model
 | 
			
		||||
- **Event**: Event, EventMeta models
 | 
			
		||||
- **Contacts**: Contact model
 | 
			
		||||
 | 
			
		||||
All models implement the `Storable` and `SledModel` traits for database integration.
 | 
			
		||||
 | 
			
		||||
## 2. Planned Enhancements
 | 
			
		||||
 | 
			
		||||
### 2.1 Add Group Support to All Models
 | 
			
		||||
 | 
			
		||||
Add a `groups: Vec<u32>` field to each model to enable linking to multiple groups defined in the Circle module.
 | 
			
		||||
 | 
			
		||||
### 2.2 Create New Message Model
 | 
			
		||||
 | 
			
		||||
Create a new `message.rs` file with a Message model for chat functionality:
 | 
			
		||||
- Different structure from Email
 | 
			
		||||
- Include thread_id, sender_id, content fields
 | 
			
		||||
- Include metadata for chat-specific features
 | 
			
		||||
- Implement Storable and SledModel traits
 | 
			
		||||
 | 
			
		||||
### 2.3 Add Utility Methods
 | 
			
		||||
 | 
			
		||||
Add utility methods to each model for:
 | 
			
		||||
- **Filtering/Searching**: Methods to filter by groups, search by content/subject
 | 
			
		||||
- **Format Conversion**: Methods to convert between formats (e.g., Email to Message)
 | 
			
		||||
- **Relationship Management**: Methods to manage relationships between models
 | 
			
		||||
 | 
			
		||||
## 3. Implementation Plan
 | 
			
		||||
 | 
			
		||||
```mermaid
 | 
			
		||||
flowchart TD
 | 
			
		||||
    A[Review Current Models] --> B[Add groups field to all models]
 | 
			
		||||
    B --> C[Create Message model]
 | 
			
		||||
    C --> D[Add utility methods]
 | 
			
		||||
    D --> E[Update mod.rs and lib.rs]
 | 
			
		||||
    E --> F[Update README.md]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 3.1 Detailed Changes
 | 
			
		||||
 | 
			
		||||
#### 3.1.1 Mail Model (`mail.rs`)
 | 
			
		||||
 | 
			
		||||
- Add `groups: Vec<u32>` field to `Email` struct
 | 
			
		||||
- Add utility methods:
 | 
			
		||||
  - `filter_by_groups(groups: &[u32]) -> bool`
 | 
			
		||||
  - `search_by_subject(query: &str) -> bool`
 | 
			
		||||
  - `search_by_content(query: &str) -> bool`
 | 
			
		||||
  - `to_message(&self) -> Message` (conversion method)
 | 
			
		||||
 | 
			
		||||
#### 3.1.2 Calendar Model (`calendar.rs`)
 | 
			
		||||
 | 
			
		||||
- Add `groups: Vec<u32>` field to `Calendar` struct
 | 
			
		||||
- Add utility methods:
 | 
			
		||||
  - `filter_by_groups(groups: &[u32]) -> bool`
 | 
			
		||||
  - `get_events(&self, db: &SledDB<Event>) -> SledDBResult<Vec<Event>>` (relationship method)
 | 
			
		||||
 | 
			
		||||
#### 3.1.3 Event Model (`event.rs`)
 | 
			
		||||
 | 
			
		||||
- Add `groups: Vec<u32>` field to `Event` struct
 | 
			
		||||
- Add utility methods:
 | 
			
		||||
  - `filter_by_groups(groups: &[u32]) -> bool`
 | 
			
		||||
  - `get_calendar(&self, db: &SledDB<Calendar>) -> SledDBResult<Calendar>` (relationship method)
 | 
			
		||||
  - `get_attendee_contacts(&self, db: &SledDB<Contact>) -> SledDBResult<Vec<Contact>>` (relationship method)
 | 
			
		||||
 | 
			
		||||
#### 3.1.4 Contacts Model (`contacts.rs`)
 | 
			
		||||
 | 
			
		||||
- Add `groups: Vec<u32>` field to `Contact` struct
 | 
			
		||||
- Add utility methods:
 | 
			
		||||
  - `filter_by_groups(groups: &[u32]) -> bool`
 | 
			
		||||
  - `search_by_name(query: &str) -> bool`
 | 
			
		||||
  - `search_by_email(query: &str) -> bool`
 | 
			
		||||
  - `get_events(&self, db: &SledDB<Event>) -> SledDBResult<Vec<Event>>` (relationship method)
 | 
			
		||||
 | 
			
		||||
#### 3.1.5 New Message Model (`message.rs`)
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::core::{SledModel, Storable};
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
 | 
			
		||||
/// MessageStatus represents the status of a message
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub enum MessageStatus {
 | 
			
		||||
    Sent,
 | 
			
		||||
    Delivered,
 | 
			
		||||
    Read,
 | 
			
		||||
    Failed,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// MessageMeta contains metadata for a chat message
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct MessageMeta {
 | 
			
		||||
    pub created_at: DateTime<Utc>,
 | 
			
		||||
    pub updated_at: DateTime<Utc>,
 | 
			
		||||
    pub status: MessageStatus,
 | 
			
		||||
    pub is_edited: bool,
 | 
			
		||||
    pub reactions: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Message represents a chat message
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct Message {
 | 
			
		||||
    pub id: u32,                // Unique identifier
 | 
			
		||||
    pub thread_id: String,      // Thread/conversation identifier
 | 
			
		||||
    pub sender_id: String,      // Sender identifier
 | 
			
		||||
    pub recipients: Vec<String>, // List of recipient identifiers
 | 
			
		||||
    pub content: String,        // Message content
 | 
			
		||||
    pub attachments: Vec<String>, // References to attachments
 | 
			
		||||
    pub groups: Vec<u32>,       // Groups this message belongs to
 | 
			
		||||
    pub meta: MessageMeta,      // Message metadata
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Message {
 | 
			
		||||
    /// Create a new message
 | 
			
		||||
    pub fn new(id: u32, thread_id: String, sender_id: String, content: String) -> Self {
 | 
			
		||||
        let now = Utc::now();
 | 
			
		||||
        Self {
 | 
			
		||||
            id,
 | 
			
		||||
            thread_id,
 | 
			
		||||
            sender_id,
 | 
			
		||||
            recipients: Vec::new(),
 | 
			
		||||
            content,
 | 
			
		||||
            attachments: Vec::new(),
 | 
			
		||||
            groups: Vec::new(),
 | 
			
		||||
            meta: MessageMeta {
 | 
			
		||||
                created_at: now,
 | 
			
		||||
                updated_at: now,
 | 
			
		||||
                status: MessageStatus::Sent,
 | 
			
		||||
                is_edited: false,
 | 
			
		||||
                reactions: Vec::new(),
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a recipient to this message
 | 
			
		||||
    pub fn add_recipient(&mut self, recipient: String) {
 | 
			
		||||
        self.recipients.push(recipient);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add an attachment to this message
 | 
			
		||||
    pub fn add_attachment(&mut self, attachment: String) {
 | 
			
		||||
        self.attachments.push(attachment);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a group to this message
 | 
			
		||||
    pub fn add_group(&mut self, group_id: u32) {
 | 
			
		||||
        if !self.groups.contains(&group_id) {
 | 
			
		||||
            self.groups.push(group_id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Filter by groups
 | 
			
		||||
    pub fn filter_by_groups(&self, groups: &[u32]) -> bool {
 | 
			
		||||
        groups.iter().any(|g| self.groups.contains(g))
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Search by content
 | 
			
		||||
    pub fn search_by_content(&self, query: &str) -> bool {
 | 
			
		||||
        self.content.to_lowercase().contains(&query.to_lowercase())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Update message status
 | 
			
		||||
    pub fn update_status(&mut self, status: MessageStatus) {
 | 
			
		||||
        self.meta.status = status;
 | 
			
		||||
        self.meta.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Edit message content
 | 
			
		||||
    pub fn edit_content(&mut self, new_content: String) {
 | 
			
		||||
        self.content = new_content;
 | 
			
		||||
        self.meta.is_edited = true;
 | 
			
		||||
        self.meta.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a reaction to the message
 | 
			
		||||
    pub fn add_reaction(&mut self, reaction: String) {
 | 
			
		||||
        self.meta.reactions.push(reaction);
 | 
			
		||||
        self.meta.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement Storable trait (provides default dump/load)
 | 
			
		||||
impl Storable for Message {}
 | 
			
		||||
 | 
			
		||||
// Implement SledModel trait
 | 
			
		||||
impl SledModel for Message {
 | 
			
		||||
    fn get_id(&self) -> String {
 | 
			
		||||
        self.id.to_string()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn db_prefix() -> &'static str {
 | 
			
		||||
        "message"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### 3.1.6 Update Module Files
 | 
			
		||||
 | 
			
		||||
Update `mod.rs` and `lib.rs` to include the new Message model.
 | 
			
		||||
 | 
			
		||||
#### 3.1.7 Update README.md
 | 
			
		||||
 | 
			
		||||
Update the README.md to include information about the Message model and the new utility methods.
 | 
			
		||||
 | 
			
		||||
## 4. Data Model Diagram
 | 
			
		||||
 | 
			
		||||
```mermaid
 | 
			
		||||
classDiagram
 | 
			
		||||
    class Email {
 | 
			
		||||
        +u32 id
 | 
			
		||||
        +u32 uid
 | 
			
		||||
        +u32 seq_num
 | 
			
		||||
        +String mailbox
 | 
			
		||||
        +String message
 | 
			
		||||
        +Vec~Attachment~ attachments
 | 
			
		||||
        +Vec~String~ flags
 | 
			
		||||
        +i64 receivetime
 | 
			
		||||
        +Option~Envelope~ envelope
 | 
			
		||||
        +Vec~u32~ groups
 | 
			
		||||
        +filter_by_groups()
 | 
			
		||||
        +search_by_subject()
 | 
			
		||||
        +search_by_content()
 | 
			
		||||
        +to_message()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    class Calendar {
 | 
			
		||||
        +u32 id
 | 
			
		||||
        +String title
 | 
			
		||||
        +String description
 | 
			
		||||
        +Vec~u32~ groups
 | 
			
		||||
        +filter_by_groups()
 | 
			
		||||
        +get_events()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    class Event {
 | 
			
		||||
        +u32 id
 | 
			
		||||
        +u32 calendar_id
 | 
			
		||||
        +String title
 | 
			
		||||
        +String description
 | 
			
		||||
        +String location
 | 
			
		||||
        +DateTime start_time
 | 
			
		||||
        +DateTime end_time
 | 
			
		||||
        +bool all_day
 | 
			
		||||
        +String recurrence
 | 
			
		||||
        +Vec~String~ attendees
 | 
			
		||||
        +String organizer
 | 
			
		||||
        +String status
 | 
			
		||||
        +EventMeta meta
 | 
			
		||||
        +Vec~u32~ groups
 | 
			
		||||
        +filter_by_groups()
 | 
			
		||||
        +get_calendar()
 | 
			
		||||
        +get_attendee_contacts()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    class Contact {
 | 
			
		||||
        +u32 id
 | 
			
		||||
        +i64 created_at
 | 
			
		||||
        +i64 modified_at
 | 
			
		||||
        +String first_name
 | 
			
		||||
        +String last_name
 | 
			
		||||
        +String email
 | 
			
		||||
        +String group
 | 
			
		||||
        +Vec~u32~ groups
 | 
			
		||||
        +filter_by_groups()
 | 
			
		||||
        +search_by_name()
 | 
			
		||||
        +search_by_email()
 | 
			
		||||
        +get_events()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    class Message {
 | 
			
		||||
        +u32 id
 | 
			
		||||
        +String thread_id
 | 
			
		||||
        +String sender_id
 | 
			
		||||
        +Vec~String~ recipients
 | 
			
		||||
        +String content
 | 
			
		||||
        +Vec~String~ attachments
 | 
			
		||||
        +Vec~u32~ groups
 | 
			
		||||
        +MessageMeta meta
 | 
			
		||||
        +filter_by_groups()
 | 
			
		||||
        +search_by_content()
 | 
			
		||||
        +update_status()
 | 
			
		||||
        +edit_content()
 | 
			
		||||
        +add_reaction()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    class Circle {
 | 
			
		||||
        +u32 id
 | 
			
		||||
        +String name
 | 
			
		||||
        +String description
 | 
			
		||||
        +Vec~Member~ members
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    Calendar "1" -- "many" Event: contains
 | 
			
		||||
    Contact "many" -- "many" Event: attends
 | 
			
		||||
    Circle "1" -- "many" Email: groups
 | 
			
		||||
    Circle "1" -- "many" Calendar: groups
 | 
			
		||||
    Circle "1" -- "many" Event: groups
 | 
			
		||||
    Circle "1" -- "many" Contact: groups
 | 
			
		||||
    Circle "1" -- "many" Message: groups
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 5. Testing Strategy
 | 
			
		||||
 | 
			
		||||
1. Unit tests for each model to verify:
 | 
			
		||||
   - Group field functionality
 | 
			
		||||
   - New utility methods
 | 
			
		||||
   - Serialization/deserialization with the new fields
 | 
			
		||||
2. Integration tests to verify:
 | 
			
		||||
   - Database operations with the updated models
 | 
			
		||||
   - Relationships between models
 | 
			
		||||
@@ -21,6 +21,14 @@ The Mail models provide email and IMAP functionality:
 | 
			
		||||
- **Attachment**: Represents a file attachment with file information
 | 
			
		||||
- **Envelope**: Represents an IMAP envelope structure with message headers
 | 
			
		||||
 | 
			
		||||
### Message (`message.rs`)
 | 
			
		||||
 | 
			
		||||
The Message models provide chat functionality:
 | 
			
		||||
 | 
			
		||||
- **Message**: Main struct for chat messages with thread and recipient information
 | 
			
		||||
- **MessageMeta**: Contains metadata for message status, editing, and reactions
 | 
			
		||||
- **MessageStatus**: Enum representing the status of a message (Sent, Delivered, Read, Failed)
 | 
			
		||||
 | 
			
		||||
### Calendar (`calendar.rs`)
 | 
			
		||||
 | 
			
		||||
The Calendar model represents a container for calendar events:
 | 
			
		||||
@@ -40,6 +48,31 @@ The Contacts model provides contact management:
 | 
			
		||||
 | 
			
		||||
- **Contact**: Main struct for contact information with personal details and grouping
 | 
			
		||||
 | 
			
		||||
## Group Support
 | 
			
		||||
 | 
			
		||||
All models now support linking to multiple groups (Circle IDs):
 | 
			
		||||
 | 
			
		||||
- Each model has a `groups: Vec<u32>` field to store multiple group IDs
 | 
			
		||||
- Utility methods for adding, removing, and filtering by groups
 | 
			
		||||
- Groups are defined in the Circle module
 | 
			
		||||
 | 
			
		||||
## Utility Methods
 | 
			
		||||
 | 
			
		||||
Each model provides utility methods for:
 | 
			
		||||
 | 
			
		||||
### Filtering/Searching
 | 
			
		||||
- `filter_by_groups(groups: &[u32]) -> bool`: Filter by groups
 | 
			
		||||
- `search_by_subject/content/name/email(query: &str) -> bool`: Search by various fields
 | 
			
		||||
 | 
			
		||||
### Format Conversion
 | 
			
		||||
- `to_message()`: Convert Email to Message
 | 
			
		||||
 | 
			
		||||
### Relationship Management
 | 
			
		||||
- `get_events()`: Get events associated with a calendar or contact
 | 
			
		||||
- `get_calendar()`: Get the calendar an event belongs to
 | 
			
		||||
- `get_attendee_contacts()`: Get contacts for event attendees
 | 
			
		||||
- `get_thread_messages()`: Get all messages in the same thread
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
These models are used by the MCC module to manage emails, calendar events, and contacts. They are typically accessed through the database handlers that implement the generic SledDB interface.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::core::{SledModel, Storable};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBResult};
 | 
			
		||||
use crate::models::mcc::event::Event;
 | 
			
		||||
 | 
			
		||||
/// Calendar represents a calendar container for events
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
@@ -7,6 +8,7 @@ pub struct Calendar {
 | 
			
		||||
    pub id: u32,             // Unique identifier
 | 
			
		||||
    pub title: String,       // Calendar title
 | 
			
		||||
    pub description: String, // Calendar details
 | 
			
		||||
    pub groups: Vec<u32>,    // Groups this calendar belongs to (references Circle IDs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Calendar {
 | 
			
		||||
@@ -16,8 +18,37 @@ impl Calendar {
 | 
			
		||||
            id,
 | 
			
		||||
            title,
 | 
			
		||||
            description,
 | 
			
		||||
            groups: Vec::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a group to this calendar
 | 
			
		||||
    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 calendar
 | 
			
		||||
    pub fn remove_group(&mut self, group_id: u32) {
 | 
			
		||||
        self.groups.retain(|&id| id != group_id);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Filter by groups - returns true if this calendar belongs to any of the specified groups
 | 
			
		||||
    pub fn filter_by_groups(&self, groups: &[u32]) -> bool {
 | 
			
		||||
        groups.iter().any(|g| self.groups.contains(g))
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get all events associated with this calendar
 | 
			
		||||
    pub fn get_events(&self, db: &SledDB<Event>) -> SledDBResult<Vec<Event>> {
 | 
			
		||||
        let all_events = db.list()?;
 | 
			
		||||
        let calendar_events = all_events
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .filter(|event| event.calendar_id == self.id)
 | 
			
		||||
            .collect();
 | 
			
		||||
        
 | 
			
		||||
        Ok(calendar_events)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement Storable trait (provides default dump/load)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::core::{SledModel, Storable};
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBResult};
 | 
			
		||||
use crate::models::mcc::event::Event;
 | 
			
		||||
use chrono::Utc;
 | 
			
		||||
 | 
			
		||||
/// Contact represents a contact entry in an address book
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
@@ -14,6 +15,7 @@ pub struct Contact {
 | 
			
		||||
    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 {
 | 
			
		||||
@@ -28,9 +30,49 @@ impl Contact {
 | 
			
		||||
            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: &SledDB<Event>) -> SledDBResult<Vec<Event>> {
 | 
			
		||||
        let all_events = db.list()?;
 | 
			
		||||
        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 {
 | 
			
		||||
@@ -52,6 +94,12 @@ impl Contact {
 | 
			
		||||
        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)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::core::{SledModel, Storable};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBResult};
 | 
			
		||||
use crate::models::mcc::calendar::Calendar;
 | 
			
		||||
use crate::models::mcc::contacts::Contact;
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
 | 
			
		||||
/// EventMeta contains additional metadata for a calendar event
 | 
			
		||||
@@ -27,6 +29,7 @@ pub struct Event {
 | 
			
		||||
    pub organizer: String,     // Organizer email
 | 
			
		||||
    pub status: String,        // "CONFIRMED", "CANCELLED", "TENTATIVE"
 | 
			
		||||
    pub meta: EventMeta,       // Additional metadata
 | 
			
		||||
    pub groups: Vec<u32>,      // Groups this event belongs to (references Circle IDs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Event {
 | 
			
		||||
@@ -60,9 +63,43 @@ impl Event {
 | 
			
		||||
                etag: String::new(),
 | 
			
		||||
                color: String::new(),
 | 
			
		||||
            },
 | 
			
		||||
            groups: Vec::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a group to this event
 | 
			
		||||
    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 event
 | 
			
		||||
    pub fn remove_group(&mut self, group_id: u32) {
 | 
			
		||||
        self.groups.retain(|&id| id != group_id);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Filter by groups - returns true if this event belongs to any of the specified groups
 | 
			
		||||
    pub fn filter_by_groups(&self, groups: &[u32]) -> bool {
 | 
			
		||||
        groups.iter().any(|g| self.groups.contains(g))
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get the calendar this event belongs to
 | 
			
		||||
    pub fn get_calendar(&self, db: &SledDB<Calendar>) -> SledDBResult<Calendar> {
 | 
			
		||||
        db.get(&self.calendar_id.to_string())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get contacts for all attendees of this event
 | 
			
		||||
    pub fn get_attendee_contacts(&self, db: &SledDB<Contact>) -> SledDBResult<Vec<Contact>> {
 | 
			
		||||
        let all_contacts = db.list()?;
 | 
			
		||||
        let attendee_contacts = all_contacts
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .filter(|contact| self.attendees.contains(&contact.email))
 | 
			
		||||
            .collect();
 | 
			
		||||
        
 | 
			
		||||
        Ok(attendee_contacts)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add an attendee to this event
 | 
			
		||||
    pub fn add_attendee(&mut self, attendee: String) {
 | 
			
		||||
        self.attendees.push(attendee);
 | 
			
		||||
@@ -77,6 +114,16 @@ impl Event {
 | 
			
		||||
    pub fn set_status(&mut self, status: &str) {
 | 
			
		||||
        self.status = status.to_string();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Search by title - returns true if the title contains the query (case-insensitive)
 | 
			
		||||
    pub fn search_by_title(&self, query: &str) -> bool {
 | 
			
		||||
        self.title.to_lowercase().contains(&query.to_lowercase())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Search by description - returns true if the description contains the query (case-insensitive)
 | 
			
		||||
    pub fn search_by_description(&self, query: &str) -> bool {
 | 
			
		||||
        self.description.to_lowercase().contains(&query.to_lowercase())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement Storable trait (provides default dump/load)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,14 @@ pub mod calendar;
 | 
			
		||||
pub mod event;
 | 
			
		||||
pub mod mail;
 | 
			
		||||
pub mod contacts;
 | 
			
		||||
pub mod message;
 | 
			
		||||
 | 
			
		||||
// Re-export all model types for convenience
 | 
			
		||||
pub use calendar::Calendar;
 | 
			
		||||
pub use event::{Event, EventMeta};
 | 
			
		||||
pub use mail::{Email, Attachment, Envelope};
 | 
			
		||||
pub use contacts::Contact;
 | 
			
		||||
pub use message::{Message, MessageMeta, MessageStatus};
 | 
			
		||||
 | 
			
		||||
// Re-export database components from core module
 | 
			
		||||
pub use crate::core::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
// Re-export database components from db module
 | 
			
		||||
pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::core::{SledModel, Storable};
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDBResult, SledDB};
 | 
			
		||||
use chrono::Utc;
 | 
			
		||||
 | 
			
		||||
/// Email represents an email message with all its metadata and content
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
@@ -17,6 +17,7 @@ pub struct Email {
 | 
			
		||||
    pub flags: Vec<String>,          // IMAP flags like \Seen, \Deleted, etc.
 | 
			
		||||
    pub receivetime: i64,            // Unix timestamp when the email was received
 | 
			
		||||
    pub envelope: Option<Envelope>,  // IMAP envelope information (contains From, To, Subject, etc.)
 | 
			
		||||
    pub groups: Vec<u32>,           // Groups this email belongs to (references Circle IDs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Attachment represents an email attachment
 | 
			
		||||
@@ -56,6 +57,7 @@ impl Email {
 | 
			
		||||
            flags: Vec::new(),
 | 
			
		||||
            receivetime: chrono::Utc::now().timestamp(),
 | 
			
		||||
            envelope: None,
 | 
			
		||||
            groups: Vec::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
@@ -64,10 +66,86 @@ impl Email {
 | 
			
		||||
        self.attachments.push(attachment);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a group to this email
 | 
			
		||||
    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 email
 | 
			
		||||
    pub fn remove_group(&mut self, group_id: u32) {
 | 
			
		||||
        self.groups.retain(|&id| id != group_id);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Filter by groups - returns true if this email 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 subject - returns true if the subject contains the query (case-insensitive)
 | 
			
		||||
    pub fn search_by_subject(&self, query: &str) -> bool {
 | 
			
		||||
        if let Some(env) = &self.envelope {
 | 
			
		||||
            env.subject.to_lowercase().contains(&query.to_lowercase())
 | 
			
		||||
        } else {
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Search by content - returns true if the message content contains the query (case-insensitive)
 | 
			
		||||
    pub fn search_by_content(&self, query: &str) -> bool {
 | 
			
		||||
        self.message.to_lowercase().contains(&query.to_lowercase())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Set the envelope for this email
 | 
			
		||||
    pub fn set_envelope(&mut self, envelope: Envelope) {
 | 
			
		||||
        self.envelope = Some(envelope);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Convert this email to a Message (for chat)
 | 
			
		||||
    pub fn to_message(&self, id: u32, thread_id: String) -> crate::models::mcc::message::Message {
 | 
			
		||||
        use crate::models::mcc::message::Message;
 | 
			
		||||
        
 | 
			
		||||
        let now = Utc::now();
 | 
			
		||||
        let sender = if let Some(env) = &self.envelope {
 | 
			
		||||
            if !env.from.is_empty() {
 | 
			
		||||
                env.from[0].clone()
 | 
			
		||||
            } else {
 | 
			
		||||
                "unknown@example.com".to_string()
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            "unknown@example.com".to_string()
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let subject = if let Some(env) = &self.envelope {
 | 
			
		||||
            env.subject.clone()
 | 
			
		||||
        } else {
 | 
			
		||||
            "No Subject".to_string()
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let recipients = if let Some(env) = &self.envelope {
 | 
			
		||||
            env.to.clone()
 | 
			
		||||
        } else {
 | 
			
		||||
            Vec::new()
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let content = if !subject.is_empty() {
 | 
			
		||||
            format!("{}\n\n{}", subject, self.message)
 | 
			
		||||
        } else {
 | 
			
		||||
            self.message.clone()
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let mut message = Message::new(id, thread_id, sender, content);
 | 
			
		||||
        message.recipients = recipients;
 | 
			
		||||
        message.groups = self.groups.clone();
 | 
			
		||||
        
 | 
			
		||||
        // Convert attachments to references
 | 
			
		||||
        for attachment in &self.attachments {
 | 
			
		||||
            message.add_attachment(attachment.filename.clone());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        message
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement Storable trait (provides default dump/load)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								herodb/src/models/mcc/message.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								herodb/src/models/mcc/message.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::db::{SledModel, Storable, SledDB, SledDBResult};
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
 | 
			
		||||
/// MessageStatus represents the status of a message
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub enum MessageStatus {
 | 
			
		||||
    Sent,
 | 
			
		||||
    Delivered,
 | 
			
		||||
    Read,
 | 
			
		||||
    Failed,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// MessageMeta contains metadata for a chat message
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct MessageMeta {
 | 
			
		||||
    pub created_at: DateTime<Utc>,
 | 
			
		||||
    pub updated_at: DateTime<Utc>,
 | 
			
		||||
    pub status: MessageStatus,
 | 
			
		||||
    pub is_edited: bool,
 | 
			
		||||
    pub reactions: Vec<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Message represents a chat message
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct Message {
 | 
			
		||||
    pub id: u32,                // Unique identifier
 | 
			
		||||
    pub thread_id: String,      // Thread/conversation identifier
 | 
			
		||||
    pub sender_id: String,      // Sender identifier
 | 
			
		||||
    pub recipients: Vec<String>, // List of recipient identifiers
 | 
			
		||||
    pub content: String,        // Message content
 | 
			
		||||
    pub attachments: Vec<String>, // References to attachments
 | 
			
		||||
    pub groups: Vec<u32>,       // Groups this message belongs to (references Circle IDs)
 | 
			
		||||
    pub meta: MessageMeta,      // Message metadata
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Message {
 | 
			
		||||
    /// Create a new message
 | 
			
		||||
    pub fn new(id: u32, thread_id: String, sender_id: String, content: String) -> Self {
 | 
			
		||||
        let now = Utc::now();
 | 
			
		||||
        Self {
 | 
			
		||||
            id,
 | 
			
		||||
            thread_id,
 | 
			
		||||
            sender_id,
 | 
			
		||||
            recipients: Vec::new(),
 | 
			
		||||
            content,
 | 
			
		||||
            attachments: Vec::new(),
 | 
			
		||||
            groups: Vec::new(),
 | 
			
		||||
            meta: MessageMeta {
 | 
			
		||||
                created_at: now,
 | 
			
		||||
                updated_at: now,
 | 
			
		||||
                status: MessageStatus::Sent,
 | 
			
		||||
                is_edited: false,
 | 
			
		||||
                reactions: Vec::new(),
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a recipient to this message
 | 
			
		||||
    pub fn add_recipient(&mut self, recipient: String) {
 | 
			
		||||
        self.recipients.push(recipient);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add an attachment to this message
 | 
			
		||||
    pub fn add_attachment(&mut self, attachment: String) {
 | 
			
		||||
        self.attachments.push(attachment);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a group to this message
 | 
			
		||||
    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 message
 | 
			
		||||
    pub fn remove_group(&mut self, group_id: u32) {
 | 
			
		||||
        self.groups.retain(|&id| id != group_id);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Filter by groups - returns true if this message 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 content - returns true if the content contains the query (case-insensitive)
 | 
			
		||||
    pub fn search_by_content(&self, query: &str) -> bool {
 | 
			
		||||
        self.content.to_lowercase().contains(&query.to_lowercase())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Update message status
 | 
			
		||||
    pub fn update_status(&mut self, status: MessageStatus) {
 | 
			
		||||
        self.meta.status = status;
 | 
			
		||||
        self.meta.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Edit message content
 | 
			
		||||
    pub fn edit_content(&mut self, new_content: String) {
 | 
			
		||||
        self.content = new_content;
 | 
			
		||||
        self.meta.is_edited = true;
 | 
			
		||||
        self.meta.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Add a reaction to the message
 | 
			
		||||
    pub fn add_reaction(&mut self, reaction: String) {
 | 
			
		||||
        self.meta.reactions.push(reaction);
 | 
			
		||||
        self.meta.updated_at = Utc::now();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get all messages in the same thread
 | 
			
		||||
    pub fn get_thread_messages(&self, db: &SledDB<Message>) -> SledDBResult<Vec<Message>> {
 | 
			
		||||
        let all_messages = db.list()?;
 | 
			
		||||
        let thread_messages = all_messages
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .filter(|msg| msg.thread_id == self.thread_id)
 | 
			
		||||
            .collect();
 | 
			
		||||
        
 | 
			
		||||
        Ok(thread_messages)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Implement Storable trait (provides default dump/load)
 | 
			
		||||
impl Storable for Message {}
 | 
			
		||||
 | 
			
		||||
// Implement SledModel trait
 | 
			
		||||
impl SledModel for Message {
 | 
			
		||||
    fn get_id(&self) -> String {
 | 
			
		||||
        self.id.to_string()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn db_prefix() -> &'static str {
 | 
			
		||||
        "message"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,12 +2,14 @@ pub mod calendar;
 | 
			
		||||
pub mod event;
 | 
			
		||||
pub mod mail;
 | 
			
		||||
pub mod contacts;
 | 
			
		||||
pub mod message;
 | 
			
		||||
 | 
			
		||||
// Re-export all model types for convenience
 | 
			
		||||
pub use calendar::Calendar;
 | 
			
		||||
pub use event::{Event, EventMeta};
 | 
			
		||||
pub use mail::{Email, Attachment, Envelope};
 | 
			
		||||
pub use contacts::Contact;
 | 
			
		||||
pub use message::{Message, MessageMeta, MessageStatus};
 | 
			
		||||
 | 
			
		||||
// Re-export database components from core module
 | 
			
		||||
pub use crate::core::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
// Re-export database components from db module
 | 
			
		||||
pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB};
 | 
			
		||||
@@ -1 +1,3 @@
 | 
			
		||||
pub mod biz;
 | 
			
		||||
pub mod biz;
 | 
			
		||||
pub mod mcc;
 | 
			
		||||
pub mod circle;
 | 
			
		||||
		Reference in New Issue
	
	Block a user