pub mod card;
pub mod invite;
use crate::identity::AgentId;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GroupInfo {
pub name: String,
pub description: String,
pub creator: AgentId,
pub created_at: u64,
pub mls_group_id: String,
pub metadata_topic: String,
pub chat_topic_prefix: String,
pub display_names: HashMap<String, String>,
}
impl GroupInfo {
#[must_use]
pub fn new(name: String, description: String, creator: AgentId, mls_group_id: String) -> Self {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
let metadata_topic = format!("x0x.group.{}.meta", &mls_group_id[..16]);
let chat_topic_prefix = format!("x0x.group.{}.chat", &mls_group_id[..16]);
Self {
name,
description,
creator,
created_at: now,
mls_group_id,
metadata_topic,
chat_topic_prefix,
display_names: HashMap::new(),
}
}
pub fn set_display_name(&mut self, agent_id_hex: String, name: String) {
self.display_names.insert(agent_id_hex, name);
}
#[must_use]
pub fn display_name(&self, agent_id_hex: &str) -> String {
self.display_names
.get(agent_id_hex)
.cloned()
.unwrap_or_else(|| {
if agent_id_hex.len() >= 8 {
format!("{}…", &agent_id_hex[..8])
} else {
agent_id_hex.to_string()
}
})
}
#[must_use]
pub fn general_chat_topic(&self) -> String {
format!("{}/general", self.chat_topic_prefix)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn agent(n: u8) -> AgentId {
AgentId([n; 32])
}
#[test]
fn test_group_info_new() {
let info = GroupInfo::new(
"Test Group".to_string(),
"A test".to_string(),
agent(1),
"aabb".repeat(8),
);
assert_eq!(info.name, "Test Group");
assert!(info.created_at > 0);
assert!(info.metadata_topic.starts_with("x0x.group."));
assert!(info.chat_topic_prefix.starts_with("x0x.group."));
}
#[test]
fn test_display_name() {
let mut info = GroupInfo::new(
"Test".to_string(),
String::new(),
agent(1),
"aabb".repeat(8),
);
let agent_hex = hex::encode([1u8; 32]);
let name = info.display_name(&agent_hex);
assert!(name.ends_with('…'));
assert_eq!(name.chars().count(), 9);
info.set_display_name(agent_hex.clone(), "Alice".to_string());
assert_eq!(info.display_name(&agent_hex), "Alice");
}
#[test]
fn test_general_chat_topic() {
let info = GroupInfo::new(
"Test".to_string(),
String::new(),
agent(1),
"aabb".repeat(8),
);
assert!(info.general_chat_topic().ends_with("/general"));
}
#[test]
fn test_serialization() {
let info = GroupInfo::new(
"Test".to_string(),
"desc".to_string(),
agent(1),
"aabb".repeat(8),
);
let json = serde_json::to_string(&info).expect("serialize");
let restored: GroupInfo = serde_json::from_str(&json).expect("deserialize");
assert_eq!(info.name, restored.name);
assert_eq!(info.mls_group_id, restored.mls_group_id);
}
}