use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GlobalMessage {
Direct {
sender: String,
recipient: String,
content: MessageContent,
},
Broadcast {
sender: String,
topic: String,
content: MessageContent,
},
Request {
sender: String,
recipient: String,
request_id: String,
content: MessageContent,
expect_reply: bool,
},
Response {
responder: String,
request_id: String,
content: MessageContent,
},
PubSub {
publisher: String,
topic: String,
content: MessageContent,
},
}
impl GlobalMessage {
pub fn sender(&self) -> &str {
match self {
Self::Direct { sender, .. }
| Self::Broadcast { sender, .. }
| Self::Request { sender, .. } => sender,
Self::Response { responder, .. } => responder,
Self::PubSub { publisher, .. } => publisher,
}
}
pub fn message_type(&self) -> &'static str {
match self {
Self::Direct { .. } => "direct",
Self::Broadcast { .. } => "broadcast",
Self::Request { .. } => "request",
Self::Response { .. } => "response",
Self::PubSub { .. } => "pubsub",
}
}
pub fn direct(
sender: impl Into<String>,
recipient: impl Into<String>,
content: MessageContent,
) -> Self {
Self::Direct {
sender: sender.into(),
recipient: recipient.into(),
content,
}
}
pub fn broadcast(
sender: impl Into<String>,
topic: impl Into<String>,
content: MessageContent,
) -> Self {
Self::Broadcast {
sender: sender.into(),
topic: topic.into(),
content,
}
}
pub fn request(
sender: impl Into<String>,
recipient: impl Into<String>,
request_id: impl Into<String>,
content: MessageContent,
) -> Self {
Self::Request {
sender: sender.into(),
recipient: recipient.into(),
request_id: request_id.into(),
content,
expect_reply: true,
}
}
pub fn response(
responder: impl Into<String>,
request_id: impl Into<String>,
content: MessageContent,
) -> Self {
Self::Response {
responder: responder.into(),
request_id: request_id.into(),
content,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageContent {
Text(String),
Json(serde_json::Value),
Binary(Vec<u8>),
Structured {
msg_type: String,
data: serde_json::Value,
},
}
impl MessageContent {
pub fn text(text: impl Into<String>) -> Self {
Self::Text(text.into())
}
pub fn json(value: serde_json::Value) -> Self {
Self::Json(value)
}
pub fn binary(data: Vec<u8>) -> Self {
Self::Binary(data)
}
pub fn structured(msg_type: impl Into<String>, data: serde_json::Value) -> Self {
Self::Structured {
msg_type: msg_type.into(),
data,
}
}
pub fn to_text(&self) -> String {
match self {
Self::Text(s) => s.clone(),
Self::Json(v) => v.to_string(),
Self::Binary(b) => format!("[binary {} bytes]", b.len()),
Self::Structured { msg_type, data } => format!("{}: {}", msg_type, data),
}
}
pub fn as_text(&self) -> Option<&str> {
match self {
Self::Text(s) => Some(s),
_ => None,
}
}
pub fn as_json(&self) -> Option<&serde_json::Value> {
match self {
Self::Json(v) => Some(v),
Self::Structured { data, .. } => Some(data),
_ => None,
}
}
pub fn as_binary(&self) -> Option<&[u8]> {
match self {
Self::Binary(b) => Some(b),
_ => None,
}
}
}
impl From<String> for MessageContent {
fn from(s: String) -> Self {
Self::Text(s)
}
}
impl From<&str> for MessageContent {
fn from(s: &str) -> Self {
Self::Text(s.to_string())
}
}
impl From<serde_json::Value> for MessageContent {
fn from(v: serde_json::Value) -> Self {
Self::Json(v)
}
}
impl From<Vec<u8>> for MessageContent {
fn from(v: Vec<u8>) -> Self {
Self::Binary(v)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MessageMetadata {
pub id: String,
pub timestamp: u64,
pub properties: HashMap<String, String>,
}
impl Default for MessageMetadata {
fn default() -> Self {
Self {
id: uuid::Uuid::new_v4().to_string(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u64,
properties: HashMap::new(),
}
}
}
impl MessageMetadata {
pub fn new() -> Self {
Self::default()
}
pub fn with_property(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.properties.insert(key.into(), value.into());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_content_text() {
let content = MessageContent::text("Hello, World!");
assert_eq!(content.as_text(), Some("Hello, World!"));
assert_eq!(content.to_text(), "Hello, World!");
}
#[test]
fn test_message_content_json() {
let json = serde_json::json!({ "key": "value" });
let content = MessageContent::json(json.clone());
assert_eq!(content.as_json(), Some(&json));
}
#[test]
fn test_global_message_direct() {
let msg = GlobalMessage::direct("agent1", "agent2", MessageContent::text("test"));
assert_eq!(msg.sender(), "agent1");
assert_eq!(msg.message_type(), "direct");
}
#[test]
fn test_global_message_request_response() {
let request =
GlobalMessage::request("client", "server", "req-123", MessageContent::text("ping"));
let response = GlobalMessage::response("server", "req-123", MessageContent::text("pong"));
assert_eq!(request.message_type(), "request");
assert_eq!(response.message_type(), "response");
}
#[test]
fn test_message_from_conversions() {
let _: MessageContent = "hello".into();
let _: MessageContent = String::from("world").into();
let _: MessageContent = serde_json::json!(42).into();
let _: MessageContent = vec![1, 2, 3].into();
}
}