cuenv_events/
lib.rs

1//! Structured event system for cuenv.
2//!
3//! This crate provides a unified event system that enables multiple UI frontends
4//! (CLI, TUI, Web) to subscribe to a single event stream. Events are emitted using
5//! tracing macros and captured by a custom tracing Layer.
6//!
7//! # Architecture
8//!
9//! ```text
10//! ┌─────────────────────────────────────────────────────────────────────────┐
11//! │                           cuenv-events crate                            │
12//! │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌─────────────┐ │
13//! │  │ Event Schema │  │ EventBus     │  │ Tracing Layer│  │ Renderers   │ │
14//! │  │ (typed)      │  │ (broadcast)  │  │ (capture)    │  │ (CLI/JSON)  │ │
15//! │  └──────────────┘  └──────────────┘  └──────────────┘  └─────────────┘ │
16//! └─────────────────────────────────────────────────────────────────────────┘
17//! ```
18//!
19//! # Usage
20//!
21//! ```rust,ignore
22//! use cuenv_events::{EventBus, CuenvEventLayer, emit_task_started};
23//! use tracing_subscriber::layer::SubscriberExt;
24//! use tracing_subscriber::util::SubscriberInitExt;
25//!
26//! // Create event bus and layer
27//! let bus = EventBus::new();
28//! let layer = CuenvEventLayer::new(bus.sender().inner);
29//!
30//! // Initialize tracing with the layer
31//! tracing_subscriber::registry()
32//!     .with(layer)
33//!     .init();
34//!
35//! // Emit events using macros
36//! emit_task_started!("build", "cargo build", false);
37//! ```
38
39pub mod bus;
40pub mod event;
41pub mod layer;
42pub mod metadata;
43pub mod renderers;
44
45// Re-exports for convenience
46pub use bus::{EventBus, EventReceiver, EventSender, SendError};
47pub use event::{
48    CiEvent, CommandEvent, CuenvEvent, EventCategory, EventSource, InteractiveEvent, OutputEvent,
49    Stream, SystemEvent, TaskEvent,
50};
51pub use layer::CuenvEventLayer;
52pub use metadata::{MetadataContext, correlation_id, set_correlation_id};
53pub use renderers::{CliRenderer, JsonRenderer};
54
55// ============================================================================
56// Emit Macros
57// ============================================================================
58
59/// Emit a task started event.
60///
61/// # Example
62/// ```rust,ignore
63/// emit_task_started!("build", "cargo build", true);
64/// ```
65#[macro_export]
66macro_rules! emit_task_started {
67    ($name:expr, $command:expr, $hermetic:expr) => {
68        ::tracing::info!(
69            target: "cuenv::task",
70            event_type = "task.started",
71            task_name = %$name,
72            command = %$command,
73            hermetic = $hermetic,
74        )
75    };
76}
77
78/// Emit a task cache hit event.
79///
80/// # Example
81/// ```rust,ignore
82/// emit_task_cache_hit!("build", "abc123");
83/// ```
84#[macro_export]
85macro_rules! emit_task_cache_hit {
86    ($name:expr, $cache_key:expr) => {
87        ::tracing::info!(
88            target: "cuenv::task",
89            event_type = "task.cache_hit",
90            task_name = %$name,
91            cache_key = %$cache_key,
92        )
93    };
94}
95
96/// Emit a task cache miss event.
97#[macro_export]
98macro_rules! emit_task_cache_miss {
99    ($name:expr) => {
100        ::tracing::info!(
101            target: "cuenv::task",
102            event_type = "task.cache_miss",
103            task_name = %$name,
104        )
105    };
106}
107
108/// Emit a task output event.
109///
110/// # Example
111/// ```rust,ignore
112/// emit_task_output!("build", "stdout", "Compiling...");
113/// ```
114#[macro_export]
115macro_rules! emit_task_output {
116    ($name:expr, $stream:expr, $content:expr) => {
117        ::tracing::info!(
118            target: "cuenv::task",
119            event_type = "task.output",
120            task_name = %$name,
121            stream = $stream,
122            content = %$content,
123        )
124    };
125}
126
127/// Emit a task completed event.
128///
129/// # Example
130/// ```rust,ignore
131/// emit_task_completed!("build", true, Some(0), 1234);
132/// ```
133#[macro_export]
134macro_rules! emit_task_completed {
135    ($name:expr, $success:expr, $exit_code:expr, $duration_ms:expr) => {
136        ::tracing::info!(
137            target: "cuenv::task",
138            event_type = "task.completed",
139            task_name = %$name,
140            success = $success,
141            exit_code = ?$exit_code,
142            duration_ms = $duration_ms,
143        )
144    };
145}
146
147/// Emit a task group started event.
148#[macro_export]
149macro_rules! emit_task_group_started {
150    ($name:expr, $sequential:expr, $task_count:expr) => {
151        ::tracing::info!(
152            target: "cuenv::task",
153            event_type = "task.group_started",
154            task_name = %$name,
155            sequential = $sequential,
156            task_count = $task_count,
157        )
158    };
159}
160
161/// Emit a task group completed event.
162#[macro_export]
163macro_rules! emit_task_group_completed {
164    ($name:expr, $success:expr, $duration_ms:expr) => {
165        ::tracing::info!(
166            target: "cuenv::task",
167            event_type = "task.group_completed",
168            task_name = %$name,
169            success = $success,
170            duration_ms = $duration_ms,
171        )
172    };
173}
174
175// CI Events
176
177/// Emit a CI context detected event.
178#[macro_export]
179macro_rules! emit_ci_context {
180    ($provider:expr, $event_type:expr, $ref_name:expr) => {
181        ::tracing::info!(
182            target: "cuenv::ci",
183            event_type = "ci.context_detected",
184            provider = %$provider,
185            ci_event_type = %$event_type,
186            ref_name = %$ref_name,
187        )
188    };
189}
190
191/// Emit a CI changed files found event.
192#[macro_export]
193macro_rules! emit_ci_changed_files {
194    ($count:expr) => {
195        ::tracing::info!(
196            target: "cuenv::ci",
197            event_type = "ci.changed_files",
198            count = $count,
199        )
200    };
201}
202
203/// Emit a CI projects discovered event.
204#[macro_export]
205macro_rules! emit_ci_projects_discovered {
206    ($count:expr) => {
207        ::tracing::info!(
208            target: "cuenv::ci",
209            event_type = "ci.projects_discovered",
210            count = $count,
211        )
212    };
213}
214
215/// Emit a CI project skipped event.
216#[macro_export]
217macro_rules! emit_ci_project_skipped {
218    ($path:expr, $reason:expr) => {
219        ::tracing::info!(
220            target: "cuenv::ci",
221            event_type = "ci.project_skipped",
222            path = %$path,
223            reason = %$reason,
224        )
225    };
226}
227
228/// Emit a CI task executing event.
229#[macro_export]
230macro_rules! emit_ci_task_executing {
231    ($project:expr, $task:expr) => {
232        ::tracing::info!(
233            target: "cuenv::ci",
234            event_type = "ci.task_executing",
235            project = %$project,
236            task = %$task,
237        )
238    };
239}
240
241/// Emit a CI task result event.
242#[macro_export]
243macro_rules! emit_ci_task_result {
244    ($project:expr, $task:expr, $success:expr) => {
245        ::tracing::info!(
246            target: "cuenv::ci",
247            event_type = "ci.task_result",
248            project = %$project,
249            task = %$task,
250            success = $success,
251        )
252    };
253    ($project:expr, $task:expr, $success:expr, $error:expr) => {
254        ::tracing::info!(
255            target: "cuenv::ci",
256            event_type = "ci.task_result",
257            project = %$project,
258            task = %$task,
259            success = $success,
260            error = %$error,
261        )
262    };
263}
264
265/// Emit a CI report generated event.
266#[macro_export]
267macro_rules! emit_ci_report {
268    ($path:expr) => {
269        ::tracing::info!(
270            target: "cuenv::ci",
271            event_type = "ci.report_generated",
272            path = %$path,
273        )
274    };
275}
276
277// Command Events
278
279/// Emit a command started event.
280#[macro_export]
281macro_rules! emit_command_started {
282    ($command:expr) => {
283        ::tracing::info!(
284            target: "cuenv::command",
285            event_type = "command.started",
286            command = %$command,
287        )
288    };
289    ($command:expr, $args:expr) => {
290        ::tracing::info!(
291            target: "cuenv::command",
292            event_type = "command.started",
293            command = %$command,
294            args = ?$args,
295        )
296    };
297}
298
299/// Emit a command progress event.
300#[macro_export]
301macro_rules! emit_command_progress {
302    ($command:expr, $progress:expr, $message:expr) => {
303        ::tracing::info!(
304            target: "cuenv::command",
305            event_type = "command.progress",
306            command = %$command,
307            progress = $progress,
308            message = %$message,
309        )
310    };
311}
312
313/// Emit a command completed event.
314#[macro_export]
315macro_rules! emit_command_completed {
316    ($command:expr, $success:expr, $duration_ms:expr) => {
317        ::tracing::info!(
318            target: "cuenv::command",
319            event_type = "command.completed",
320            command = %$command,
321            success = $success,
322            duration_ms = $duration_ms,
323        )
324    };
325}
326
327// Interactive Events
328
329/// Emit a prompt requested event.
330#[macro_export]
331macro_rules! emit_prompt_requested {
332    ($prompt_id:expr, $message:expr, $options:expr) => {
333        ::tracing::info!(
334            target: "cuenv::interactive",
335            event_type = "interactive.prompt_requested",
336            prompt_id = %$prompt_id,
337            message = %$message,
338            options = ?$options,
339        )
340    };
341}
342
343/// Emit a prompt resolved event.
344#[macro_export]
345macro_rules! emit_prompt_resolved {
346    ($prompt_id:expr, $response:expr) => {
347        ::tracing::info!(
348            target: "cuenv::interactive",
349            event_type = "interactive.prompt_resolved",
350            prompt_id = %$prompt_id,
351            response = %$response,
352        )
353    };
354}
355
356/// Emit a wait progress event.
357#[macro_export]
358macro_rules! emit_wait_progress {
359    ($target:expr, $elapsed_secs:expr) => {
360        ::tracing::info!(
361            target: "cuenv::interactive",
362            event_type = "interactive.wait_progress",
363            task_name = %$target,
364            elapsed_secs = $elapsed_secs,
365        )
366    };
367}
368
369// System Events
370
371/// Emit a supervisor log event.
372#[macro_export]
373macro_rules! emit_supervisor_log {
374    ($tag:expr, $message:expr) => {
375        ::tracing::info!(
376            target: "cuenv::system",
377            event_type = "system.supervisor_log",
378            tag = %$tag,
379            message = %$message,
380        )
381    };
382}
383
384/// Emit a system shutdown event.
385#[macro_export]
386macro_rules! emit_shutdown {
387    () => {
388        ::tracing::info!(
389            target: "cuenv::system",
390            event_type = "system.shutdown",
391        )
392    };
393}
394
395// Output Events
396
397/// Emit a stdout output event.
398#[macro_export]
399macro_rules! emit_stdout {
400    ($content:expr) => {
401        ::tracing::info!(
402            target: "cuenv::output",
403            event_type = "output.stdout",
404            content = %$content,
405        )
406    };
407}
408
409/// Emit a stderr output event.
410#[macro_export]
411macro_rules! emit_stderr {
412    ($content:expr) => {
413        ::tracing::info!(
414            target: "cuenv::output",
415            event_type = "output.stderr",
416            content = %$content,
417        )
418    };
419}
420
421#[cfg(test)]
422mod tests {
423    use super::*;
424    use tokio::sync::mpsc;
425    use tracing_subscriber::layer::SubscriberExt;
426
427    #[tokio::test]
428    async fn test_emit_macros_compile() {
429        let (tx, _rx) = mpsc::unbounded_channel();
430        let layer = CuenvEventLayer::new(tx);
431        let subscriber = tracing_subscriber::registry().with(layer);
432
433        tracing::subscriber::with_default(subscriber, || {
434            emit_task_started!("build", "cargo build", true);
435            emit_task_cache_hit!("build", "abc123");
436            emit_task_cache_miss!("test");
437            emit_task_output!("build", "stdout", "output");
438            emit_task_completed!("build", true, Some(0), 1000_u64);
439            emit_task_group_started!("all", false, 3_usize);
440            emit_task_group_completed!("all", true, 5000_u64);
441
442            emit_ci_context!("github", "push", "main");
443            emit_ci_changed_files!(10_usize);
444            emit_ci_projects_discovered!(3_usize);
445            emit_ci_project_skipped!("/path", "no tasks");
446            emit_ci_task_executing!("/path", "build");
447            emit_ci_task_result!("/path", "build", true);
448            emit_ci_task_result!("/path", "test", false, "assertion failed");
449            emit_ci_report!("/path/report.json");
450
451            emit_command_started!("env");
452            emit_command_started!("task", vec!["build".to_string()]);
453            emit_command_progress!("env", 0.5_f32, "loading");
454            emit_command_completed!("env", true, 100_u64);
455
456            emit_prompt_requested!("p1", "Continue?", vec!["yes", "no"]);
457            emit_prompt_resolved!("p1", "yes");
458            emit_wait_progress!("hook", 5_u64);
459
460            emit_supervisor_log!("supervisor", "started");
461            emit_shutdown!();
462
463            emit_stdout!("hello");
464            emit_stderr!("error");
465        });
466    }
467}