use std::sync::{Arc, Mutex};
type ChangeListener = Box<dyn Fn() + Send + Sync>;
pub struct ChangeNotifier {
listeners: Arc<Mutex<Vec<ChangeListener>>>,
}
impl ChangeNotifier {
pub fn new() -> Self {
Self {
listeners: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn add_listener<F>(&self, listener: F)
where
F: Fn() + Send + Sync + 'static,
{
let mut listeners = self.listeners.lock().unwrap();
listeners.push(Box::new(listener));
}
pub fn notify(&self) {
let listeners = self.listeners.lock().unwrap();
for listener in listeners.iter() {
listener();
}
}
pub fn clear_listeners(&self) {
let mut listeners = self.listeners.lock().unwrap();
listeners.clear();
}
pub fn listener_count(&self) -> usize {
self.listeners.lock().unwrap().len()
}
}
impl Default for ChangeNotifier {
fn default() -> Self {
Self::new()
}
}
impl Clone for ChangeNotifier {
fn clone(&self) -> Self {
Self {
listeners: Arc::clone(&self.listeners),
}
}
}
impl std::fmt::Debug for ChangeNotifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChangeNotifier")
.field("listener_count", &self.listener_count())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicI32, Ordering};
struct TestCounter {
notifier: ChangeNotifier,
count: i32,
}
impl TestCounter {
fn new() -> Self {
Self {
notifier: ChangeNotifier::new(),
count: 0,
}
}
fn increment(&mut self) {
self.count += 1;
self.notifier.notify();
}
fn get_count(&self) -> i32 {
self.count
}
fn add_listener<F>(&self, listener: F)
where
F: Fn() + Send + Sync + 'static,
{
self.notifier.add_listener(listener);
}
}
#[test]
fn test_change_notifier_basic() {
let notifier = ChangeNotifier::new();
let counter = Arc::new(AtomicI32::new(0));
let c = Arc::clone(&counter);
notifier.add_listener(move || {
c.fetch_add(1, Ordering::SeqCst);
});
notifier.notify();
assert_eq!(counter.load(Ordering::SeqCst), 1);
notifier.notify();
assert_eq!(counter.load(Ordering::SeqCst), 2);
}
#[test]
fn test_change_notifier_multiple_listeners() {
let notifier = ChangeNotifier::new();
let counter1 = Arc::new(AtomicI32::new(0));
let counter2 = Arc::new(AtomicI32::new(0));
let c1 = Arc::clone(&counter1);
notifier.add_listener(move || {
c1.fetch_add(1, Ordering::SeqCst);
});
let c2 = Arc::clone(&counter2);
notifier.add_listener(move || {
c2.fetch_add(10, Ordering::SeqCst);
});
notifier.notify();
assert_eq!(counter1.load(Ordering::SeqCst), 1);
assert_eq!(counter2.load(Ordering::SeqCst), 10);
}
#[test]
fn test_change_notifier_clear() {
let notifier = ChangeNotifier::new();
let counter = Arc::new(AtomicI32::new(0));
let c = Arc::clone(&counter);
notifier.add_listener(move || {
c.fetch_add(1, Ordering::SeqCst);
});
notifier.notify();
assert_eq!(counter.load(Ordering::SeqCst), 1);
notifier.clear_listeners();
notifier.notify();
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_change_notifier_listener_count() {
let notifier = ChangeNotifier::new();
assert_eq!(notifier.listener_count(), 0);
notifier.add_listener(|| {});
assert_eq!(notifier.listener_count(), 1);
notifier.add_listener(|| {});
assert_eq!(notifier.listener_count(), 2);
notifier.clear_listeners();
assert_eq!(notifier.listener_count(), 0);
}
#[test]
fn test_change_notifier_with_struct() {
let mut counter = TestCounter::new();
let call_count = Arc::new(AtomicI32::new(0));
let c = Arc::clone(&call_count);
counter.add_listener(move || {
c.fetch_add(1, Ordering::SeqCst);
});
assert_eq!(counter.get_count(), 0);
assert_eq!(call_count.load(Ordering::SeqCst), 0);
counter.increment();
assert_eq!(counter.get_count(), 1);
assert_eq!(call_count.load(Ordering::SeqCst), 1);
counter.increment();
assert_eq!(counter.get_count(), 2);
assert_eq!(call_count.load(Ordering::SeqCst), 2);
}
#[test]
fn test_change_notifier_clone() {
let notifier1 = ChangeNotifier::new();
let notifier2 = notifier1.clone();
let counter = Arc::new(AtomicI32::new(0));
let c = Arc::clone(&counter);
notifier1.add_listener(move || {
c.fetch_add(1, Ordering::SeqCst);
});
notifier2.notify();
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
}