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}