Skip to main content

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}