hive_btle/observer.rs
1//! Observer pattern for HIVE mesh events
2//!
3//! This module provides the event types and observer trait for receiving
4//! notifications about mesh state changes. Platform implementations register
5//! observers to receive callbacks when peers are discovered, connected,
6//! disconnected, or when documents are synced.
7//!
8//! ## Usage
9//!
10//! ```ignore
11//! use hive_btle::observer::{HiveEvent, HiveObserver};
12//!
13//! struct MyObserver;
14//!
15//! impl HiveObserver for MyObserver {
16//! fn on_event(&self, event: HiveEvent) {
17//! match event {
18//! HiveEvent::PeerDiscovered { peer } => {
19//! println!("Discovered: {}", peer.display_name());
20//! }
21//! HiveEvent::EmergencyReceived { from_node } => {
22//! println!("EMERGENCY from {:08X}", from_node.as_u32());
23//! }
24//! _ => {}
25//! }
26//! }
27//! }
28//! ```
29
30#[cfg(not(feature = "std"))]
31use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
32#[cfg(feature = "std")]
33use std::sync::Arc;
34
35// Re-import Vec for HiveEvent variants
36#[cfg(feature = "std")]
37use std::string::String;
38#[cfg(feature = "std")]
39use std::vec::Vec;
40
41use crate::peer::HivePeer;
42use crate::sync::crdt::EventType;
43use crate::NodeId;
44
45/// Events emitted by the HIVE mesh
46///
47/// These events notify observers about changes in mesh state, peer lifecycle,
48/// and document synchronization.
49#[derive(Debug, Clone)]
50pub enum HiveEvent {
51 // ==================== Peer Lifecycle Events ====================
52 /// A new peer was discovered via BLE scanning
53 PeerDiscovered {
54 /// The discovered peer
55 peer: HivePeer,
56 },
57
58 /// A peer connected to us (either direction)
59 PeerConnected {
60 /// Node ID of the connected peer
61 node_id: NodeId,
62 },
63
64 /// A peer disconnected
65 PeerDisconnected {
66 /// Node ID of the disconnected peer
67 node_id: NodeId,
68 /// Reason for disconnection
69 reason: DisconnectReason,
70 },
71
72 /// A peer was removed due to timeout (stale)
73 PeerLost {
74 /// Node ID of the lost peer
75 node_id: NodeId,
76 },
77
78 // ==================== Mesh Events ====================
79 /// An emergency event was received from a peer
80 EmergencyReceived {
81 /// Node ID that sent the emergency
82 from_node: NodeId,
83 },
84
85 /// An ACK event was received from a peer
86 AckReceived {
87 /// Node ID that sent the ACK
88 from_node: NodeId,
89 },
90
91 /// A generic event was received from a peer
92 EventReceived {
93 /// Node ID that sent the event
94 from_node: NodeId,
95 /// Type of event
96 event_type: EventType,
97 },
98
99 /// A document was synced with a peer
100 DocumentSynced {
101 /// Node ID that we synced with
102 from_node: NodeId,
103 /// Updated total counter value
104 total_count: u64,
105 },
106
107 // ==================== Mesh State Events ====================
108 /// Mesh state changed (peer count, connected count)
109 MeshStateChanged {
110 /// Total number of known peers
111 peer_count: usize,
112 /// Number of connected peers
113 connected_count: usize,
114 },
115
116 /// All peers have acknowledged an emergency
117 AllPeersAcked {
118 /// Number of peers that acknowledged
119 ack_count: usize,
120 },
121
122 // ==================== Per-Peer E2EE Events ====================
123 /// E2EE session established with a peer
124 PeerE2eeEstablished {
125 /// Node ID of the peer we established E2EE with
126 peer_node_id: NodeId,
127 },
128
129 /// E2EE session closed with a peer
130 PeerE2eeClosed {
131 /// Node ID of the peer whose E2EE session closed
132 peer_node_id: NodeId,
133 },
134
135 /// Received an E2EE encrypted message from a peer
136 PeerE2eeMessageReceived {
137 /// Node ID of the sender
138 from_node: NodeId,
139 /// Decrypted message data
140 data: Vec<u8>,
141 },
142
143 /// E2EE session failed to establish
144 PeerE2eeFailed {
145 /// Node ID of the peer
146 peer_node_id: NodeId,
147 /// Error description
148 error: String,
149 },
150
151 // ==================== Security Events ====================
152 /// A security violation was detected
153 SecurityViolation {
154 /// Type of violation
155 kind: SecurityViolationKind,
156 /// Optional source identifier (node_id, BLE identifier, etc.)
157 source: Option<String>,
158 },
159}
160
161impl HiveEvent {
162 /// Create a peer discovered event
163 pub fn peer_discovered(peer: HivePeer) -> Self {
164 Self::PeerDiscovered { peer }
165 }
166
167 /// Create a peer connected event
168 pub fn peer_connected(node_id: NodeId) -> Self {
169 Self::PeerConnected { node_id }
170 }
171
172 /// Create a peer disconnected event
173 pub fn peer_disconnected(node_id: NodeId, reason: DisconnectReason) -> Self {
174 Self::PeerDisconnected { node_id, reason }
175 }
176
177 /// Create a peer lost event (timeout)
178 pub fn peer_lost(node_id: NodeId) -> Self {
179 Self::PeerLost { node_id }
180 }
181
182 /// Create an emergency received event
183 pub fn emergency_received(from_node: NodeId) -> Self {
184 Self::EmergencyReceived { from_node }
185 }
186
187 /// Create an ACK received event
188 pub fn ack_received(from_node: NodeId) -> Self {
189 Self::AckReceived { from_node }
190 }
191
192 /// Create a generic event received
193 pub fn event_received(from_node: NodeId, event_type: EventType) -> Self {
194 Self::EventReceived {
195 from_node,
196 event_type,
197 }
198 }
199
200 /// Create a document synced event
201 pub fn document_synced(from_node: NodeId, total_count: u64) -> Self {
202 Self::DocumentSynced {
203 from_node,
204 total_count,
205 }
206 }
207
208 /// Create a peer E2EE established event
209 pub fn peer_e2ee_established(peer_node_id: NodeId) -> Self {
210 Self::PeerE2eeEstablished { peer_node_id }
211 }
212
213 /// Create a peer E2EE closed event
214 pub fn peer_e2ee_closed(peer_node_id: NodeId) -> Self {
215 Self::PeerE2eeClosed { peer_node_id }
216 }
217
218 /// Create a peer E2EE message received event
219 pub fn peer_e2ee_message_received(from_node: NodeId, data: Vec<u8>) -> Self {
220 Self::PeerE2eeMessageReceived { from_node, data }
221 }
222
223 /// Create a peer E2EE failed event
224 pub fn peer_e2ee_failed(peer_node_id: NodeId, error: String) -> Self {
225 Self::PeerE2eeFailed {
226 peer_node_id,
227 error,
228 }
229 }
230
231 /// Create a security violation event
232 pub fn security_violation(kind: SecurityViolationKind, source: Option<String>) -> Self {
233 Self::SecurityViolation { kind, source }
234 }
235}
236
237/// Reason for peer disconnection
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
239pub enum DisconnectReason {
240 /// Local initiated disconnect
241 LocalRequest,
242 /// Remote peer initiated disconnect
243 RemoteRequest,
244 /// Connection timed out
245 Timeout,
246 /// BLE link lost
247 LinkLoss,
248 /// Connection failed
249 ConnectionFailed,
250 /// Unknown reason
251 #[default]
252 Unknown,
253}
254
255/// Types of security violations that can be detected
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
257pub enum SecurityViolationKind {
258 /// Received unencrypted document when strict encryption mode is enabled
259 UnencryptedInStrictMode,
260 /// Decryption failed (wrong key or corrupted data)
261 DecryptionFailed,
262 /// Replay attack detected (duplicate message counter)
263 ReplayDetected,
264 /// Message from unknown/unauthorized node
265 UnauthorizedNode,
266}
267
268/// Observer trait for receiving HIVE mesh events
269///
270/// Implement this trait to receive callbacks when mesh events occur.
271/// Observers must be thread-safe (Send + Sync) as they may be called
272/// from any thread.
273///
274/// ## Platform Notes
275///
276/// - **iOS/macOS**: Wrap in a Swift class that conforms to this protocol via UniFFI
277/// - **Android**: Implement via JNI callback interface
278/// - **ESP32**: Use direct Rust implementation with static callbacks
279pub trait HiveObserver: Send + Sync {
280 /// Called when a mesh event occurs
281 ///
282 /// This method should return quickly to avoid blocking the mesh.
283 /// If heavy processing is needed, dispatch to another thread.
284 fn on_event(&self, event: HiveEvent);
285}
286
287/// A simple observer that collects events into a vector (useful for testing)
288#[cfg(feature = "std")]
289#[derive(Debug, Default)]
290pub struct CollectingObserver {
291 events: std::sync::Mutex<Vec<HiveEvent>>,
292}
293
294#[cfg(feature = "std")]
295impl CollectingObserver {
296 /// Create a new collecting observer
297 pub fn new() -> Self {
298 Self {
299 events: std::sync::Mutex::new(Vec::new()),
300 }
301 }
302
303 /// Get all collected events
304 pub fn events(&self) -> Vec<HiveEvent> {
305 self.events.lock().unwrap().clone()
306 }
307
308 /// Clear collected events
309 pub fn clear(&self) {
310 self.events.lock().unwrap().clear();
311 }
312
313 /// Get count of collected events
314 pub fn count(&self) -> usize {
315 self.events.lock().unwrap().len()
316 }
317}
318
319#[cfg(feature = "std")]
320impl HiveObserver for CollectingObserver {
321 fn on_event(&self, event: HiveEvent) {
322 self.events.lock().unwrap().push(event);
323 }
324}
325
326/// Helper to manage multiple observers
327#[cfg(feature = "std")]
328pub struct ObserverManager {
329 observers: std::sync::RwLock<Vec<Arc<dyn HiveObserver>>>,
330}
331
332#[cfg(feature = "std")]
333impl Default for ObserverManager {
334 fn default() -> Self {
335 Self::new()
336 }
337}
338
339#[cfg(feature = "std")]
340impl ObserverManager {
341 /// Create a new observer manager
342 pub fn new() -> Self {
343 Self {
344 observers: std::sync::RwLock::new(Vec::new()),
345 }
346 }
347
348 /// Add an observer
349 pub fn add(&self, observer: Arc<dyn HiveObserver>) {
350 self.observers.write().unwrap().push(observer);
351 }
352
353 /// Remove an observer (by Arc pointer equality)
354 pub fn remove(&self, observer: &Arc<dyn HiveObserver>) {
355 self.observers
356 .write()
357 .unwrap()
358 .retain(|o| !Arc::ptr_eq(o, observer));
359 }
360
361 /// Notify all observers of an event
362 pub fn notify(&self, event: HiveEvent) {
363 // Use try_read to avoid panicking on poisoned locks
364 if let Ok(observers) = self.observers.try_read() {
365 for observer in observers.iter() {
366 observer.on_event(event.clone());
367 }
368 }
369 }
370
371 /// Get the number of registered observers
372 pub fn count(&self) -> usize {
373 self.observers.read().unwrap().len()
374 }
375}
376
377#[cfg(all(test, feature = "std"))]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn test_collecting_observer() {
383 let observer = CollectingObserver::new();
384
385 observer.on_event(HiveEvent::peer_connected(NodeId::new(0x12345678)));
386 observer.on_event(HiveEvent::emergency_received(NodeId::new(0x87654321)));
387
388 assert_eq!(observer.count(), 2);
389
390 let events = observer.events();
391 assert!(matches!(events[0], HiveEvent::PeerConnected { .. }));
392 assert!(matches!(events[1], HiveEvent::EmergencyReceived { .. }));
393
394 observer.clear();
395 assert_eq!(observer.count(), 0);
396 }
397
398 #[test]
399 fn test_observer_manager() {
400 let manager = ObserverManager::new();
401
402 // Keep concrete references for count checks
403 let obs1_concrete = Arc::new(CollectingObserver::new());
404 let obs2_concrete = Arc::new(CollectingObserver::new());
405 let observer1: Arc<dyn HiveObserver> = obs1_concrete.clone();
406 let observer2: Arc<dyn HiveObserver> = obs2_concrete.clone();
407
408 manager.add(observer1.clone());
409 manager.add(observer2.clone());
410
411 assert_eq!(manager.count(), 2);
412
413 manager.notify(HiveEvent::peer_connected(NodeId::new(0x12345678)));
414
415 assert_eq!(obs1_concrete.count(), 1);
416 assert_eq!(obs2_concrete.count(), 1);
417
418 manager.remove(&observer1);
419 assert_eq!(manager.count(), 1);
420
421 manager.notify(HiveEvent::peer_lost(NodeId::new(0x12345678)));
422
423 assert_eq!(obs1_concrete.count(), 1); // Not notified
424 assert_eq!(obs2_concrete.count(), 2); // Got both events
425 }
426}