use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum Target {
Node(String),
Topic(String),
Broadcast,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Envelope {
pub id: Uuid,
pub from: String,
pub to: Target,
pub payload: serde_json::Value,
pub timestamp: DateTime<Utc>,
}
impl Envelope {
pub fn new(from: impl Into<String>, to: Target, payload: serde_json::Value) -> Self {
Self {
id: Uuid::new_v4(),
from: from.into(),
to,
payload,
timestamp: Utc::now(),
}
}
#[inline]
#[must_use]
pub fn is_broadcast(&self) -> bool {
matches!(self.to, Target::Broadcast)
}
#[inline]
#[must_use]
pub fn is_topic(&self) -> bool {
matches!(self.to, Target::Topic(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn envelope_roundtrip() {
let env = Envelope::new(
"node-1",
Target::Topic("crew.events".into()),
serde_json::json!({"status": "ok"}),
);
let json = serde_json::to_string(&env).unwrap();
let decoded: Envelope = serde_json::from_str(&json).unwrap();
assert_eq!(decoded.id, env.id);
assert_eq!(decoded.from, "node-1");
assert!(decoded.is_topic());
}
#[test]
fn broadcast_detection() {
let env = Envelope::new("a", Target::Broadcast, serde_json::Value::Null);
assert!(env.is_broadcast());
assert!(!env.is_topic());
}
#[test]
fn node_target() {
let env = Envelope::new("a", Target::Node("b".into()), serde_json::Value::Null);
assert!(!env.is_broadcast());
assert!(!env.is_topic());
}
}