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, OnceLock};
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 EVENT_BUS: OnceLock<Arc<RwLock<EventBus>>> = OnceLock::new();
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    EVENT_BUS.get_or_init(|| Arc::new(RwLock::new(EventBus::new()))).clone()
38}
39
40pub fn publish_event(event: ForgeEvent) -> Result<()> {
41    let bus = get_event_bus();
42    let bus = bus.read();
43    let _ = bus.sender.send(event);
44    Ok(())
45}
46
47pub fn subscribe_to_event_stream() -> broadcast::Receiver<ForgeEvent> {
48    let bus = get_event_bus();
49    let bus = bus.read();
50    bus.sender.subscribe()
51}
52
53pub fn emit_tool_started_event(tool_id: &str) -> Result<()> {
54    publish_event(ForgeEvent::ToolStarted {
55        tool_id: tool_id.to_string(),
56        timestamp: chrono::Utc::now().timestamp(),
57    })
58}
59
60pub fn emit_tool_completed_event(tool_id: &str, duration_ms: u64) -> Result<()> {
61    publish_event(ForgeEvent::ToolCompleted {
62        tool_id: tool_id.to_string(),
63        duration_ms,
64        timestamp: chrono::Utc::now().timestamp(),
65    })
66}
67
68pub fn emit_pipeline_started_event(pipeline_id: &str) -> Result<()> {
69    publish_event(ForgeEvent::PipelineStarted {
70        pipeline_id: pipeline_id.to_string(),
71        timestamp: chrono::Utc::now().timestamp(),
72    })
73}
74
75pub fn emit_pipeline_completed_event(pipeline_id: &str, duration_ms: u64) -> Result<()> {
76    publish_event(ForgeEvent::PipelineCompleted {
77        pipeline_id: pipeline_id.to_string(),
78        duration_ms,
79        timestamp: chrono::Utc::now().timestamp(),
80    })
81}
82
83pub fn emit_package_installation_begin(package_id: &str) -> Result<()> {
84    publish_event(ForgeEvent::PackageInstallationBegin {
85        package_id: package_id.to_string(),
86        timestamp: chrono::Utc::now().timestamp(),
87    })
88}
89
90pub fn emit_package_installation_success(package_id: &str) -> Result<()> {
91    publish_event(ForgeEvent::PackageInstallationSuccess {
92        package_id: package_id.to_string(),
93        timestamp: chrono::Utc::now().timestamp(),
94    })
95}
96
97pub fn emit_security_violation_detected(description: &str, severity: &str) -> Result<()> {
98    publish_event(ForgeEvent::SecurityViolationDetected {
99        description: description.to_string(),
100        severity: severity.to_string(),
101        timestamp: chrono::Utc::now().timestamp(),
102    })
103}
104
105pub fn emit_magical_config_injection(config_section: &str) -> Result<()> {
106    publish_event(ForgeEvent::MagicalConfigInjection {
107        config_section: config_section.to_string(),
108        timestamp: chrono::Utc::now().timestamp(),
109    })
110}