1use anyhow::Result;
4use serde::{Serialize, Deserialize};
5use tokio::sync::broadcast;
6use std::sync::{Arc, OnceLock};
7use parking_lot::RwLock;
8
9#[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}