pub mod types;
use std::{
collections::HashMap,
fmt::{Debug, Formatter, Result},
};
use self::types::{Event, WalletEvent, WalletEventType};
type Handler<T> = Box<dyn Fn(&T) + Send + Sync + 'static>;
pub struct EventEmitter {
handlers: HashMap<WalletEventType, Vec<Handler<Event>>>,
}
impl EventEmitter {
pub fn new() -> Self {
Self {
handlers: HashMap::new(),
}
}
pub fn on<F>(&mut self, events: Vec<WalletEventType>, handler: F)
where
F: Fn(&Event) + 'static + Clone + Send + Sync,
{
if events.is_empty() {
for event_type in &[
WalletEventType::NewOutput,
WalletEventType::SpentOutput,
WalletEventType::TransactionInclusion,
WalletEventType::TransactionProgress,
WalletEventType::ConsolidationRequired,
#[cfg(feature = "ledger_nano")]
WalletEventType::LedgerAddressGeneration,
] {
let event_handlers = self.handlers.entry(*event_type).or_insert_with(Vec::new);
event_handlers.push(Box::new(handler.clone()));
}
}
for event in events.into_iter() {
let event_handlers = self.handlers.entry(event).or_insert_with(Vec::new);
event_handlers.push(Box::new(handler.clone()));
}
}
pub fn clear(&mut self, events: Vec<WalletEventType>) {
if events.is_empty() {
self.handlers.clear();
}
for event in events {
self.handlers.remove(&event);
}
}
pub fn emit(&self, account_index: u32, event: WalletEvent) {
let event_type = match &event {
WalletEvent::NewOutput(_) => WalletEventType::NewOutput,
WalletEvent::SpentOutput(_) => WalletEventType::SpentOutput,
WalletEvent::TransactionInclusion(_) => WalletEventType::TransactionInclusion,
WalletEvent::TransactionProgress(_) => WalletEventType::TransactionProgress,
WalletEvent::ConsolidationRequired => WalletEventType::ConsolidationRequired,
#[cfg(feature = "ledger_nano")]
WalletEvent::LedgerAddressGeneration(_) => WalletEventType::LedgerAddressGeneration,
};
let event = Event { account_index, event };
if let Some(handlers) = self.handlers.get(&event_type) {
for handler in handlers {
handler(&event);
}
}
}
}
impl Default for EventEmitter {
fn default() -> Self {
Self::new()
}
}
impl Debug for EventEmitter {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(
f,
"event_types_with_handlers: {:?}",
self.handlers.keys().collect::<Vec<&WalletEventType>>()
)
}
}
#[cfg(test)]
mod tests {
use std::{
str::FromStr,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use super::{
types::{TransactionInclusionEvent, TransactionProgressEvent, WalletEvent, WalletEventType},
EventEmitter,
};
use crate::{types::block::payload::transaction::TransactionId, wallet::account::types::InclusionState};
#[test]
fn events() {
let mut emitter = EventEmitter::new();
let event_counter = Arc::new(AtomicUsize::new(0));
emitter.on(vec![WalletEventType::ConsolidationRequired], |_name| {
});
emitter.on(
vec![
WalletEventType::TransactionProgress,
WalletEventType::ConsolidationRequired,
],
move |_name| {
},
);
let event_counter_clone = Arc::clone(&event_counter);
emitter.on(vec![], move |_name| {
event_counter_clone.fetch_add(1, Ordering::SeqCst);
});
emitter.emit(0, WalletEvent::ConsolidationRequired);
emitter.emit(
0,
WalletEvent::TransactionProgress(TransactionProgressEvent::SelectingInputs),
);
emitter.emit(
0,
WalletEvent::TransactionInclusion(TransactionInclusionEvent {
transaction_id: TransactionId::from_str(
"0x2289d9981fb23cc5f4f6c2742685eeb480f8476089888aa886a18232bad81989",
)
.expect("invalid tx id"),
inclusion_state: InclusionState::Confirmed,
}),
);
assert_eq!(3, event_counter.load(Ordering::SeqCst));
emitter.clear(vec![WalletEventType::ConsolidationRequired]);
emitter.emit(0, WalletEvent::ConsolidationRequired);
assert_eq!(3, event_counter.load(Ordering::SeqCst));
emitter.clear(vec![]);
emitter.emit(
0,
WalletEvent::TransactionProgress(TransactionProgressEvent::SelectingInputs),
);
emitter.emit(
0,
WalletEvent::TransactionInclusion(TransactionInclusionEvent {
transaction_id: TransactionId::from_str(
"0x2289d9981fb23cc5f4f6c2742685eeb480f8476089888aa886a18232bad81989",
)
.expect("invalid tx id"),
inclusion_state: InclusionState::Confirmed,
}),
);
assert_eq!(3, event_counter.load(Ordering::SeqCst));
let event_counter_clone = Arc::clone(&event_counter);
emitter.on(vec![WalletEventType::ConsolidationRequired], move |_name| {
event_counter_clone.fetch_add(1, Ordering::SeqCst);
});
for _ in 0..1_000_000 {
emitter.emit(0, WalletEvent::ConsolidationRequired);
}
assert_eq!(1_000_003, event_counter.load(Ordering::SeqCst));
}
}