breez_sdk_spark/
events.rs1use core::fmt;
2use std::{
3 collections::BTreeMap,
4 sync::atomic::{AtomicU64, Ordering},
5};
6
7use serde::Serialize;
8use tokio::sync::RwLock;
9use uuid::Uuid;
10
11use crate::{DepositInfo, Payment};
12
13#[allow(clippy::large_enum_variant)]
15#[derive(Debug, Clone, Serialize)]
16#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
17pub enum SdkEvent {
18 Synced,
20 DataSynced {
22 did_pull_new_records: bool,
24 },
25 UnclaimedDeposits {
27 unclaimed_deposits: Vec<DepositInfo>,
28 },
29 ClaimedDeposits {
30 claimed_deposits: Vec<DepositInfo>,
31 },
32 PaymentSucceeded {
33 payment: Payment,
34 },
35 PaymentPending {
36 payment: Payment,
37 },
38 PaymentFailed {
39 payment: Payment,
40 },
41}
42
43impl fmt::Display for SdkEvent {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 match self {
46 SdkEvent::Synced => write!(f, "Synced"),
47 SdkEvent::DataSynced {
48 did_pull_new_records,
49 } => {
50 write!(
51 f,
52 "DataSynced: {} new records",
53 match did_pull_new_records {
54 true => "with",
55 false => "no",
56 }
57 )
58 }
59 SdkEvent::UnclaimedDeposits { unclaimed_deposits } => {
60 write!(f, "UnclaimedDeposits: {unclaimed_deposits:?}")
61 }
62 SdkEvent::ClaimedDeposits { claimed_deposits } => {
63 write!(f, "ClaimedDeposits: {claimed_deposits:?}")
64 }
65 SdkEvent::PaymentSucceeded { payment } => {
66 write!(f, "PaymentSucceeded: {payment:?}")
67 }
68 SdkEvent::PaymentPending { payment } => {
69 write!(f, "PaymentPending: {payment:?}")
70 }
71 SdkEvent::PaymentFailed { payment } => {
72 write!(f, "PaymentFailed: {payment:?}")
73 }
74 }
75 }
76}
77
78#[cfg_attr(feature = "uniffi", uniffi::export(callback_interface))]
80#[macros::async_trait]
81pub trait EventListener: Send + Sync {
82 async fn on_event(&self, event: SdkEvent);
84}
85
86pub struct EventEmitter {
88 listener_index: AtomicU64,
89 listeners: RwLock<BTreeMap<String, Box<dyn EventListener>>>,
90}
91
92impl EventEmitter {
93 pub fn new() -> Self {
95 Self {
96 listener_index: AtomicU64::new(0),
97 listeners: RwLock::new(BTreeMap::new()),
98 }
99 }
100
101 pub async fn add_listener(&self, listener: Box<dyn EventListener>) -> String {
111 let index = self.listener_index.fetch_add(1, Ordering::Relaxed);
112 let id = format!("listener_{}-{}", index, Uuid::new_v4());
113 let mut listeners = self.listeners.write().await;
114 listeners.insert(id.clone(), listener);
115 id
116 }
117
118 pub async fn remove_listener(&self, id: &str) -> bool {
128 let mut listeners = self.listeners.write().await;
129 listeners.remove(id).is_some()
130 }
131
132 pub async fn emit(&self, event: &SdkEvent) {
134 let listeners = self.listeners.read().await;
136
137 for listener in listeners.values() {
139 listener.on_event(event.clone()).await;
140 }
141 }
142}
143
144impl Default for EventEmitter {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use std::sync::Arc;
154 use std::sync::atomic::{AtomicBool, Ordering};
155
156 use macros::async_test_all;
157
158 #[cfg(feature = "browser-tests")]
159 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
160
161 struct TestListener {
162 received: Arc<AtomicBool>,
163 }
164
165 #[macros::async_trait]
166 impl EventListener for TestListener {
167 async fn on_event(&self, _event: SdkEvent) {
168 self.received.store(true, Ordering::Relaxed);
169 }
170 }
171
172 #[async_test_all]
173 async fn test_event_emission() {
174 let emitter = EventEmitter::new();
175 let received = Arc::new(AtomicBool::new(false));
176
177 let listener = Box::new(TestListener {
179 received: received.clone(),
180 });
181
182 let _ = emitter.add_listener(listener).await;
183
184 let event = SdkEvent::Synced {};
185
186 emitter.emit(&event).await;
187
188 assert!(received.load(Ordering::Relaxed));
190 }
191
192 #[async_test_all]
193 async fn test_remove_listener() {
194 let emitter = EventEmitter::new();
195
196 let received1 = Arc::new(AtomicBool::new(false));
198 let received2 = Arc::new(AtomicBool::new(false));
199
200 let listener1 = Box::new(TestListener {
202 received: received1.clone(),
203 });
204
205 let listener2 = Box::new(TestListener {
206 received: received2.clone(),
207 });
208
209 let id1 = emitter.add_listener(listener1).await;
210 let id2 = emitter.add_listener(listener2).await;
211
212 assert!(emitter.remove_listener(&id1).await);
214
215 let event = SdkEvent::Synced {};
217 emitter.emit(&event).await;
218
219 assert!(!received1.load(Ordering::Relaxed));
221
222 assert!(received2.load(Ordering::Relaxed));
224
225 assert!(emitter.remove_listener(&id2).await);
227
228 assert!(!emitter.remove_listener("non-existent-id").await);
230 }
231}