use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalEvent {
pub event_id: String,
pub event_type: String,
pub source: String,
pub timestamp: u64,
pub data: serde_json::Value,
pub metadata: HashMap<String, String>,
}
impl GlobalEvent {
pub fn new(event_type: impl Into<String>, source: impl Into<String>) -> Self {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
Self {
event_id: uuid::Uuid::new_v4().to_string(),
event_type: event_type.into(),
source: source.into(),
timestamp,
data: serde_json::Value::Null,
metadata: HashMap::new(),
}
}
pub fn with_data(mut self, data: impl Into<serde_json::Value>) -> Self {
self.data = data.into();
self
}
pub fn with_metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
pub fn get_data<T: for<'de> Deserialize<'de>>(&self) -> Option<T> {
serde_json::from_value(self.data.clone()).ok()
}
pub fn is_type(&self, event_type: &str) -> bool {
self.event_type == event_type
}
pub fn is_from(&self, source: &str) -> bool {
self.source == source
}
pub fn matches_prefix(&self, prefix: &str) -> bool {
self.event_type.starts_with(prefix)
}
}
pub struct EventBuilder {
event: GlobalEvent,
}
impl EventBuilder {
pub fn new(event_type: impl Into<String>, source: impl Into<String>) -> Self {
Self {
event: GlobalEvent::new(event_type, source),
}
}
pub fn id(mut self, id: impl Into<String>) -> Self {
self.event.event_id = id.into();
self
}
pub fn data(mut self, data: impl Into<serde_json::Value>) -> Self {
self.event.data = data.into();
self
}
pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.event.metadata.insert(key.into(), value.into());
self
}
pub fn timestamp(mut self, timestamp: u64) -> Self {
self.event.timestamp = timestamp;
self
}
pub fn build(self) -> GlobalEvent {
self.event
}
}
pub mod lifecycle {
pub const CREATED: &str = "lifecycle:created";
pub const INITIALIZED: &str = "lifecycle:initialized";
pub const STARTED: &str = "lifecycle:started";
pub const STOPPED: &str = "lifecycle:stopped";
pub const SHUTDOWN: &str = "lifecycle:shutdown";
pub const DESTROYED: &str = "lifecycle:destroyed";
}
pub mod execution {
pub const STARTED: &str = "execution:started";
pub const COMPLETED: &str = "execution:completed";
pub const FAILED: &str = "execution:failed";
pub const INTERRUPTED: &str = "execution:interrupted";
pub const TIMEOUT: &str = "execution:timeout";
}
pub mod message {
pub const SENT: &str = "message:sent";
pub const RECEIVED: &str = "message:received";
pub const DELIVERED: &str = "message:delivered";
pub const FAILED: &str = "message:failed";
}
pub mod plugin {
pub const LOADED: &str = "plugin:loaded";
pub const UNLOADED: &str = "plugin:unloaded";
pub const ERROR: &str = "plugin:error";
}
pub mod state {
pub const CHANGED: &str = "state:changed";
pub const ERROR: &str = "state:error";
pub const PAUSED: &str = "state:paused";
pub const RESUMED: &str = "state:resumed";
}
pub fn lifecycle_event(event_type: &str, source: &str) -> GlobalEvent {
GlobalEvent::new(event_type, source)
}
pub fn execution_event(event_type: &str, source: &str, data: serde_json::Value) -> GlobalEvent {
GlobalEvent::new(event_type, source).with_data(data)
}
pub fn state_changed_event(source: &str, old_state: &str, new_state: &str) -> GlobalEvent {
GlobalEvent::new(state::CHANGED, source).with_data(serde_json::json!({
"old_state": old_state,
"new_state": new_state
}))
}
pub fn error_event(source: &str, error: &str) -> GlobalEvent {
GlobalEvent::new("error", source).with_data(serde_json::json!({
"error": error
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_event_creation() {
let event = GlobalEvent::new("test:event", "agent1");
assert_eq!(event.event_type, "test:event");
assert_eq!(event.source, "agent1");
assert!(!event.event_id.is_empty());
}
#[test]
fn test_event_with_data() {
let data = serde_json::json!({ "key": "value" });
let event = GlobalEvent::new("test:event", "agent1").with_data(data.clone());
assert_eq!(event.data, data);
}
#[test]
fn test_event_builder() {
let event = EventBuilder::new("test:event", "agent1")
.id("custom-id")
.data(serde_json::json!({ "test": true }))
.metadata("meta1", "value1")
.timestamp(12345)
.build();
assert_eq!(event.event_id, "custom-id");
assert_eq!(event.timestamp, 12345);
assert_eq!(event.metadata.get("meta1"), Some(&"value1".to_string()));
}
#[test]
fn test_event_type_checks() {
let event = GlobalEvent::new("lifecycle:initialized", "agent1");
assert!(event.is_type("lifecycle:initialized"));
assert!(event.is_from("agent1"));
assert!(event.matches_prefix("lifecycle:"));
assert!(!event.matches_prefix("execution:"));
}
#[test]
fn test_helper_functions() {
let event = state_changed_event("agent1", "ready", "executing");
assert!(event.is_type(state::CHANGED));
assert_eq!(event.source, "agent1");
let data: serde_json::Value = event.data;
assert_eq!(data["old_state"], "ready");
assert_eq!(data["new_state"], "executing");
}
}