use crate::apps::AppConfig;
use std::sync::{Arc, Mutex, OnceLock};
pub type SignalReceiver = Box<dyn Fn(&AppConfig) + Send + Sync>;
type InternalReceiver = Arc<dyn Fn(&AppConfig) + Send + Sync>;
#[derive(Default)]
pub struct Signal {
receivers: Arc<Mutex<Vec<InternalReceiver>>>,
}
impl Signal {
pub fn new() -> Self {
Self {
receivers: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn connect(&self, receiver: SignalReceiver) {
self.receivers
.lock()
.unwrap_or_else(|e| e.into_inner())
.push(Arc::from(receiver));
}
pub fn send(&self, config: &AppConfig) {
let snapshot: Vec<InternalReceiver> = {
let guard = self.receivers.lock().unwrap_or_else(|e| e.into_inner());
guard.iter().map(Arc::clone).collect()
};
for receiver in &snapshot {
receiver(config);
}
}
pub fn disconnect_all(&self) {
self.receivers
.lock()
.unwrap_or_else(|e| e.into_inner())
.clear();
}
pub fn receiver_count(&self) -> usize {
self.receivers
.lock()
.unwrap_or_else(|e| e.into_inner())
.len()
}
}
impl Clone for Signal {
fn clone(&self) -> Self {
Self {
receivers: Arc::clone(&self.receivers),
}
}
}
static APP_READY_SIGNAL: OnceLock<Signal> = OnceLock::new();
static APP_SHUTDOWN_SIGNAL: OnceLock<Signal> = OnceLock::new();
pub fn app_ready() -> &'static Signal {
APP_READY_SIGNAL.get_or_init(Signal::new)
}
pub fn app_shutdown() -> &'static Signal {
APP_SHUTDOWN_SIGNAL.get_or_init(Signal::new)
}
pub fn clear_all_signals() {
if let Some(signal) = APP_READY_SIGNAL.get() {
signal.disconnect_all();
}
if let Some(signal) = APP_SHUTDOWN_SIGNAL.get() {
signal.disconnect_all();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn test_signal_creation() {
let signal = Signal::new();
assert_eq!(signal.receiver_count(), 0);
}
#[test]
fn test_signal_connect() {
let signal = Signal::new();
signal.connect(Box::new(|_| {}));
assert_eq!(signal.receiver_count(), 1);
}
#[test]
fn test_signal_send() {
let signal = Signal::new();
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = Arc::clone(&counter);
signal.connect(Box::new(move |_| {
counter_clone.fetch_add(1, Ordering::SeqCst);
}));
let config = AppConfig::new("test", "test");
signal.send(&config);
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_signal_multiple_receivers() {
let signal = Signal::new();
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone1 = Arc::clone(&counter);
signal.connect(Box::new(move |_| {
counter_clone1.fetch_add(1, Ordering::SeqCst);
}));
let counter_clone2 = Arc::clone(&counter);
signal.connect(Box::new(move |_| {
counter_clone2.fetch_add(2, Ordering::SeqCst);
}));
let config = AppConfig::new("test", "test");
signal.send(&config);
assert_eq!(counter.load(Ordering::SeqCst), 3); }
#[test]
fn test_signal_disconnect_all() {
let signal = Signal::new();
signal.connect(Box::new(|_| {}));
signal.connect(Box::new(|_| {}));
assert_eq!(signal.receiver_count(), 2);
signal.disconnect_all();
assert_eq!(signal.receiver_count(), 0);
}
#[test]
fn test_signal_receiver_with_config() {
let signal = Signal::new();
let received_label = Arc::new(Mutex::new(String::new()));
let label_clone = Arc::clone(&received_label);
signal.connect(Box::new(move |config| {
*label_clone.lock().unwrap() = config.label.clone();
}));
let config = AppConfig::new("testapp", "testapp");
signal.send(&config);
assert_eq!(*received_label.lock().unwrap(), "testapp");
}
#[test]
fn test_app_ready_signal() {
clear_all_signals();
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = Arc::clone(&counter);
app_ready().connect(Box::new(move |_| {
counter_clone.fetch_add(1, Ordering::SeqCst);
}));
let config = AppConfig::new("test", "test");
app_ready().send(&config);
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_app_shutdown_signal() {
clear_all_signals();
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = Arc::clone(&counter);
app_shutdown().connect(Box::new(move |_| {
counter_clone.fetch_add(1, Ordering::SeqCst);
}));
let config = AppConfig::new("test", "test");
app_shutdown().send(&config);
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[test]
fn test_signal_clone() {
let signal1 = Signal::new();
signal1.connect(Box::new(|_| {}));
let signal2 = signal1.clone();
assert_eq!(signal1.receiver_count(), 1);
assert_eq!(signal2.receiver_count(), 1);
signal2.connect(Box::new(|_| {}));
assert_eq!(signal1.receiver_count(), 2);
assert_eq!(signal2.receiver_count(), 2);
}
#[test]
fn test_clear_all_signals() {
clear_all_signals();
app_ready().connect(Box::new(|_| {}));
app_shutdown().connect(Box::new(|_| {}));
assert_eq!(app_ready().receiver_count(), 1);
assert_eq!(app_shutdown().receiver_count(), 1);
clear_all_signals();
assert_eq!(app_ready().receiver_count(), 0);
assert_eq!(app_shutdown().receiver_count(), 0);
}
#[test]
fn test_signal_send_multiple_times() {
let signal = Signal::new();
let counter = Arc::new(AtomicUsize::new(0));
let counter_clone = Arc::clone(&counter);
signal.connect(Box::new(move |_| {
counter_clone.fetch_add(1, Ordering::SeqCst);
}));
let config = AppConfig::new("test", "test");
signal.send(&config);
signal.send(&config);
signal.send(&config);
assert_eq!(counter.load(Ordering::SeqCst), 3);
}
#[test]
fn test_signal_receiver_error_handling() {
let signal = Signal::new();
let success_counter = Arc::new(AtomicUsize::new(0));
signal.connect(Box::new(|_| {
}));
let counter_clone = Arc::clone(&success_counter);
signal.connect(Box::new(move |_| {
counter_clone.fetch_add(1, Ordering::SeqCst);
}));
let config = AppConfig::new("test", "test");
signal.send(&config);
assert_eq!(success_counter.load(Ordering::SeqCst), 1);
}
}