use super::{ChatError, Group, GroupId, Message, MessageContent, MessageId};
use crate::network::NetworkIntegration;
use anyhow::Result;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct ChatService {
groups: Arc<RwLock<HashMap<GroupId, Group>>>,
message_store: Arc<sled::Db>,
network: Arc<NetworkIntegration>,
}
impl std::fmt::Debug for ChatService {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChatService")
.field("groups", &"Arc<RwLock<HashMap<GroupId, Group>>>")
.field("message_store", &"Arc<sled::Db>")
.field("network", &"Arc<NetworkIntegration>")
.finish()
}
}
impl ChatService {
pub async fn new(network: Arc<NetworkIntegration>) -> Result<Self> {
let db_path = dirs::data_dir()
.ok_or_else(|| anyhow::anyhow!("Failed to get data directory"))?
.join("communitas")
.join("messages");
std::fs::create_dir_all(&db_path)?;
let message_store = Arc::new(sled::open(db_path)?);
Ok(Self {
groups: Arc::new(RwLock::new(HashMap::new())),
message_store,
network,
})
}
pub async fn create_group(&self, name: &str) -> Result<Group> {
let group = Group::new(name.to_string());
let mut groups = self.groups.write().await;
groups.insert(group.id.clone(), group.clone());
self.network.store_group(&group).await?;
Ok(group)
}
pub async fn send_message(&self, group_id: &str, content: &str) -> Result<MessageId> {
let groups = self.groups.read().await;
let group_id_obj = GroupId(group_id.to_string());
let group = groups.get(&group_id_obj)
.ok_or_else(|| ChatError::GroupNotFound(group_id.to_string()))?;
let message = Message::new(
self.network.get_identity().await?,
MessageContent::Text(content.to_string()),
);
self.store_message(&message, group_id).await?;
self.network.broadcast_message(&message, group).await?;
Ok(message.id.clone())
}
async fn store_message(&self, message: &Message, group_id: &str) -> Result<()> {
let key = format!("{}:{}", group_id, message.id.0);
let value = serde_json::to_vec(message)?;
self.message_store.insert(key, value)?;
Ok(())
}
}