gglib_core/ports/event_emitter.rs
1//! Event emitter trait for cross-crate event broadcasting.
2//!
3//! This module defines the abstraction for emitting application events.
4//! Implementations handle transport details (channels, Tauri events, SSE, etc.).
5
6use crate::events::AppEvent;
7
8/// Trait for emitting application events.
9///
10/// This abstraction keeps event plumbing consistent across domains and prevents
11/// channel types from becoming part of the public API surface.
12///
13/// # Implementations
14///
15/// - `NoopEmitter` - For tests and CLI contexts that don't need events
16/// - Adapter-specific implementations (Tauri, Axum SSE, etc.)
17///
18/// # Example
19///
20/// ```ignore
21/// // In a service
22/// fn start_server(&self, emitter: Arc<dyn AppEventEmitter>) {
23/// // ... start server logic ...
24/// emitter.emit(AppEvent::server_started(model_id, model_name, port));
25/// }
26/// ```
27pub trait AppEventEmitter: Send + Sync {
28 /// Emit an application event.
29 ///
30 /// Implementations should handle the event asynchronously or buffer it.
31 /// This method should not block.
32 fn emit(&self, event: AppEvent);
33}
34
35/// A no-op event emitter for tests and CLI contexts.
36///
37/// This implementation discards all events, making it suitable for:
38/// - Unit tests that don't need to verify event emission
39/// - CLI applications that don't have an event listener
40/// - Contexts where event emission is optional
41#[derive(Debug, Clone, Default)]
42pub struct NoopEmitter;
43
44impl NoopEmitter {
45 /// Create a new no-op emitter.
46 pub const fn new() -> Self {
47 Self
48 }
49}
50
51impl AppEventEmitter for NoopEmitter {
52 fn emit(&self, _event: AppEvent) {
53 // Intentionally do nothing
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use std::sync::Arc;
61
62 #[test]
63 fn test_noop_emitter() {
64 let emitter = NoopEmitter::new();
65
66 // Should not panic
67 emitter.emit(AppEvent::model_removed(1));
68 }
69
70 #[test]
71 fn test_arc_emitter() {
72 let emitter: Arc<dyn AppEventEmitter> = Arc::new(NoopEmitter::new());
73 emitter.emit(AppEvent::model_removed(1));
74 }
75}