dx_forge/api/
events.rs

1//! Global Event Bus & Observability APIs
2
3use anyhow::Result;
4use serde::{Serialize, Deserialize};
5use tokio::sync::broadcast;
6use std::sync::Arc;
7use parking_lot::RwLock;
8
9/// Forge event types
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub enum ForgeEvent {
12    ToolStarted { tool_id: String, timestamp: i64 },
13    ToolCompleted { tool_id: String, duration_ms: u64, timestamp: i64 },
14    PipelineStarted { pipeline_id: String, timestamp: i64 },
15    PipelineCompleted { pipeline_id: String, duration_ms: u64, timestamp: i64 },
16    PackageInstallationBegin { package_id: String, timestamp: i64 },
17    PackageInstallationSuccess { package_id: String, timestamp: i64 },
18    SecurityViolationDetected { description: String, severity: String, timestamp: i64 },
19    MagicalConfigInjection { config_section: String, timestamp: i64 },
20    Custom { event_type: String, data: serde_json::Value, timestamp: i64 },
21}
22
23static mut EVENT_BUS: Option<Arc<RwLock<EventBus>>> = None;
24
25struct EventBus {
26    sender: broadcast::Sender<ForgeEvent>,
27}
28
29impl EventBus {
30    fn new() -> Self {
31        let (sender, _) = broadcast::channel(10000);
32        Self { sender }
33    }
34}
35
36fn get_event_bus() -> Arc<RwLock<EventBus>> {
37    unsafe {
38        if EVENT_BUS.is_none() {
39            EVENT_BUS = Some(Arc::new(RwLock::new(EventBus::new())));
40        }
41        EVENT_BUS.as_ref().unwrap().clone()
42    }
43}
44
45pub fn publish_event(event: ForgeEvent) -> Result<()> {
46    let bus = get_event_bus();
47    let bus = bus.read();
48    let _ = bus.sender.send(event);
49    Ok(())
50}
51
52pub fn subscribe_to_event_stream() -> broadcast::Receiver<ForgeEvent> {
53    let bus = get_event_bus();
54    let bus = bus.read();
55    bus.sender.subscribe()
56}
57
58pub fn emit_tool_started_event(tool_id: &str) -> Result<()> {
59    publish_event(ForgeEvent::ToolStarted {
60        tool_id: tool_id.to_string(),
61        timestamp: chrono::Utc::now().timestamp(),
62    })
63}
64
65pub fn emit_tool_completed_event(tool_id: &str, duration_ms: u64) -> Result<()> {
66    publish_event(ForgeEvent::ToolCompleted {
67        tool_id: tool_id.to_string(),
68        duration_ms,
69        timestamp: chrono::Utc::now().timestamp(),
70    })
71}
72
73pub fn emit_pipeline_started_event(pipeline_id: &str) -> Result<()> {
74    publish_event(ForgeEvent::PipelineStarted {
75        pipeline_id: pipeline_id.to_string(),
76        timestamp: chrono::Utc::now().timestamp(),
77    })
78}
79
80pub fn emit_pipeline_completed_event(pipeline_id: &str, duration_ms: u64) -> Result<()> {
81    publish_event(ForgeEvent::PipelineCompleted {
82        pipeline_id: pipeline_id.to_string(),
83        duration_ms,
84        timestamp: chrono::Utc::now().timestamp(),
85    })
86}
87
88pub fn emit_package_installation_begin(package_id: &str) -> Result<()> {
89    publish_event(ForgeEvent::PackageInstallationBegin {
90        package_id: package_id.to_string(),
91        timestamp: chrono::Utc::now().timestamp(),
92    })
93}
94
95pub fn emit_package_installation_success(package_id: &str) -> Result<()> {
96    publish_event(ForgeEvent::PackageInstallationSuccess {
97        package_id: package_id.to_string(),
98        timestamp: chrono::Utc::now().timestamp(),
99    })
100}
101
102pub fn emit_security_violation_detected(description: &str, severity: &str) -> Result<()> {
103    publish_event(ForgeEvent::SecurityViolationDetected {
104        description: description.to_string(),
105        severity: severity.to_string(),
106        timestamp: chrono::Utc::now().timestamp(),
107    })
108}
109
110pub fn emit_magical_config_injection(config_section: &str) -> Result<()> {
111    publish_event(ForgeEvent::MagicalConfigInjection {
112        config_section: config_section.to_string(),
113        timestamp: chrono::Utc::now().timestamp(),
114    })
115}