...
This commit is contained in:
parent
9e8fefbf1f
commit
8595bb3950
@ -30,3 +30,7 @@ path = "examples/rhai_demo.rs"
|
||||
[[bin]]
|
||||
name = "dbexample2"
|
||||
path = "src/cmd/dbexample2/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "dbexample_mcc"
|
||||
path = "src/cmd/dbexample_mcc/main.rs"
|
||||
|
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 mcc;
|
||||
pub mod circle;
|
Loading…
Reference in New Issue
Block a user