gun/events.rs
1//! Event system for reactive updates
2//!
3//! This module implements Gun's event emitter pattern, allowing components to subscribe
4//! to and emit events. Events are used throughout Gun for:
5//! - Notifying listeners when nodes are updated
6//! - Triggering network synchronization
7//! - Reacting to graph changes
8//!
9//! Based on Gun.js `onto.js` and event system. The event emitter is thread-safe and
10//! supports multiple listeners per event type.
11
12use parking_lot::Mutex;
13use std::collections::HashMap;
14use std::sync::{Arc, RwLock};
15
16/// Event callback function type
17///
18/// All event listeners must implement this type. The callback receives the event
19/// that was emitted and can access its type and data.
20///
21/// # Thread Safety
22///
23/// Callbacks must be `Send + Sync` to be used across threads.
24pub type EventCallback = Box<dyn Fn(&Event) + Send + Sync>;
25
26/// An event in the Gun event system
27///
28/// Events consist of:
29/// - `event_type`: A string identifier for the event (e.g., "node_update:user_123")
30/// - `data`: Arbitrary JSON data associated with the event
31///
32/// # Example
33///
34/// ```rust,no_run
35/// use gun::events::Event;
36/// use serde_json::json;
37///
38/// let event = Event {
39/// event_type: "node_update:user_123".to_string(),
40/// data: json!({"name": "Alice", "age": 30}),
41/// };
42/// ```
43#[derive(Clone, Debug)]
44pub struct Event {
45 pub event_type: String,
46 pub data: serde_json::Value,
47}
48
49struct ListenerEntry {
50 id: u64,
51 callback: Arc<EventCallback>,
52}
53
54/// Event emitter for subscribing to and emitting events
55///
56/// The event emitter maintains a map of event types to their listeners.
57/// Multiple listeners can subscribe to the same event type, and all will be
58/// called when the event is emitted.
59///
60/// # Thread Safety
61///
62/// `EventEmitter` is thread-safe and can be shared across threads using `Arc<EventEmitter>`.
63///
64/// # Example
65///
66/// ```rust,no_run
67/// use gun::events::{EventEmitter, Event};
68/// use serde_json::json;
69/// use std::sync::Arc;
70///
71/// let emitter = Arc::new(EventEmitter::new());
72///
73/// // Subscribe to events
74/// let emitter_clone = emitter.clone();
75/// emitter.on("test_event", Box::new(move |event: &Event| {
76/// println!("Received event: {:?}", event);
77/// }));
78///
79/// // Emit an event
80/// emitter.emit(&Event {
81/// event_type: "test_event".to_string(),
82/// data: json!({"message": "hello"}),
83/// });
84/// ```
85pub struct EventEmitter {
86 listeners: Arc<RwLock<HashMap<String, Vec<ListenerEntry>>>>,
87 id_counter: Arc<Mutex<u64>>,
88}
89
90impl EventEmitter {
91 /// Create a new event emitter
92 pub fn new() -> Self {
93 Self {
94 listeners: Arc::new(RwLock::new(HashMap::new())),
95 id_counter: Arc::new(Mutex::new(0)),
96 }
97 }
98
99 /// Register an event listener
100 /// Returns the listener ID for later removal
101 ///
102 /// # Panics
103 /// This function will panic if the lock is poisoned, which should never happen
104 /// in practice since we don't panic while holding the lock.
105 pub fn on(&self, event_type: &str, callback: EventCallback) -> u64 {
106 let mut listeners = self.listeners.write().expect("EventEmitter lock poisoned");
107 let callbacks = listeners.entry(event_type.to_string()).or_default();
108
109 let id = {
110 let mut counter = self.id_counter.lock();
111 *counter += 1;
112 *counter
113 };
114
115 callbacks.push(ListenerEntry {
116 id,
117 callback: Arc::new(callback),
118 });
119 id
120 }
121
122 /// Remove an event listener by ID
123 ///
124 /// # Panics
125 /// This function will panic if the lock is poisoned, which should never happen
126 /// in practice since we don't panic while holding the lock.
127 pub fn off(&self, event_type: &str, id: u64) {
128 let mut listeners = self.listeners.write().expect("EventEmitter lock poisoned");
129 if let Some(callbacks) = listeners.get_mut(event_type) {
130 callbacks.retain(|entry| entry.id != id);
131 // Remove empty event types
132 if callbacks.is_empty() {
133 listeners.remove(event_type);
134 }
135 }
136 }
137
138 /// Remove all listeners for an event type
139 ///
140 /// # Panics
141 /// This function will panic if the lock is poisoned, which should never happen
142 /// in practice since we don't panic while holding the lock.
143 pub fn off_all(&self, event_type: &str) {
144 let mut listeners = self.listeners.write().expect("EventEmitter lock poisoned");
145 listeners.remove(event_type);
146 }
147
148 /// Emit an event
149 ///
150 /// # Panics
151 /// This function will panic if the lock is poisoned, which should never happen
152 /// in practice since we don't panic while holding the lock.
153 pub fn emit(&self, event: &Event) {
154 let listeners = self.listeners.read().expect("EventEmitter lock poisoned");
155 if let Some(callbacks) = listeners.get(&event.event_type) {
156 // Clone callbacks to avoid holding lock during execution
157 let callbacks: Vec<Arc<EventCallback>> = callbacks
158 .iter()
159 .map(|entry| entry.callback.clone())
160 .collect();
161 drop(listeners); // Release lock before calling callbacks
162
163 for callback in callbacks.iter() {
164 callback(event);
165 }
166 }
167 }
168
169 /// Remove all listeners for an event type
170 pub fn remove_all_listeners(&self, event_type: &str) {
171 self.off_all(event_type);
172 }
173
174 /// Get count of listeners for an event type
175 ///
176 /// # Panics
177 /// This function will panic if the lock is poisoned, which should never happen
178 /// in practice since we don't panic while holding the lock.
179 pub fn listener_count(&self, event_type: &str) -> usize {
180 let listeners = self.listeners.read().expect("EventEmitter lock poisoned");
181 listeners.get(event_type).map(|v| v.len()).unwrap_or(0)
182 }
183}
184
185impl Default for EventEmitter {
186 fn default() -> Self {
187 Self::new()
188 }
189}