use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::handler::ConnectionId;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum MessageType {
#[serde(rename = "text")]
Text { content: String },
#[serde(rename = "binary")]
Binary { data: Vec<u8> },
#[serde(rename = "json")]
Json { payload: serde_json::Value },
#[serde(rename = "system")]
System { message: String },
#[serde(rename = "error")]
Error { code: String, message: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub id: Uuid,
pub from: Option<ConnectionId>,
pub to: Option<ConnectionId>,
pub room: Option<String>,
pub message_type: MessageType,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub metadata: std::collections::HashMap<String, String>,
}
impl Message {
pub fn text(content: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4(),
from: None,
to: None,
room: None,
message_type: MessageType::Text {
content: content.into(),
},
timestamp: chrono::Utc::now(),
metadata: std::collections::HashMap::new(),
}
}
pub fn json(payload: serde_json::Value) -> Self {
Self {
id: Uuid::new_v4(),
from: None,
to: None,
room: None,
message_type: MessageType::Json { payload },
timestamp: chrono::Utc::now(),
metadata: std::collections::HashMap::new(),
}
}
pub fn system(message: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4(),
from: None,
to: None,
room: None,
message_type: MessageType::System {
message: message.into(),
},
timestamp: chrono::Utc::now(),
metadata: std::collections::HashMap::new(),
}
}
pub fn error(code: impl Into<String>, message: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4(),
from: None,
to: None,
room: None,
message_type: MessageType::Error {
code: code.into(),
message: message.into(),
},
timestamp: chrono::Utc::now(),
metadata: std::collections::HashMap::new(),
}
}
pub fn from(mut self, conn_id: ConnectionId) -> Self {
self.from = Some(conn_id);
self
}
pub fn to(mut self, conn_id: ConnectionId) -> Self {
self.to = Some(conn_id);
self
}
pub fn in_room(mut self, room: impl Into<String>) -> Self {
self.room = Some(room.into());
self
}
pub fn with_metadata(mut self, key: String, value: String) -> Self {
self.metadata.insert(key, value);
self
}
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
}
pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json)
}
}
#[derive(Debug, Clone)]
pub struct BroadcastOptions {
pub exclude: Vec<ConnectionId>,
pub only: Option<Vec<ConnectionId>>,
pub room: Option<String>,
}
impl Default for BroadcastOptions {
fn default() -> Self {
Self {
exclude: Vec::new(),
only: None,
room: None,
}
}
}
impl BroadcastOptions {
pub fn new() -> Self {
Self::default()
}
pub fn exclude(mut self, conn_ids: Vec<ConnectionId>) -> Self {
self.exclude = conn_ids;
self
}
pub fn only(mut self, conn_ids: Vec<ConnectionId>) -> Self {
self.only = Some(conn_ids);
self
}
pub fn in_room(mut self, room: impl Into<String>) -> Self {
self.room = Some(room.into());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_creation() {
let msg = Message::text("Hello, World!")
.from(Uuid::new_v4())
.in_room("general");
assert!(matches!(msg.message_type, MessageType::Text { .. }));
assert!(msg.from.is_some());
assert_eq!(msg.room, Some("general".to_string()));
}
#[test]
fn test_message_serialization() {
let msg = Message::json(serde_json::json!({
"action": "ping"
}));
let json = msg.to_json().unwrap();
let deserialized = Message::from_json(&json).unwrap();
assert!(matches!(deserialized.message_type, MessageType::Json { .. }));
}
}