1pub struct Signal<T: Clone> {
9 subs: Vec<Subscription<T>>,
10 new_id: usize,
11}
12
13struct Subscription<T> {
14 id: SubscriptionId,
15 callback: Box<dyn Fn(T)>,
16}
17
18#[derive(Clone, Copy, Eq, PartialEq)]
20pub struct SubscriptionId(usize);
21
22impl<T: Clone> Signal<T> {
23 pub fn new() -> Signal<T> {
25 Signal {
26 subs: Vec::with_capacity(0),
27 new_id: 0,
28 }
29 }
30
31 pub fn connect<F>(&mut self, callback: F) -> SubscriptionId
37 where F: (Fn(T)) + 'static
38 {
39 let id = SubscriptionId(self.new_id);
40 self.new_id = self.new_id.checked_add(1).expect("No overflow");
41
42 self.subs.push(Subscription {
43 id,
44 callback: Box::new(callback),
45 });
46 self.subs.shrink_to_fit();
47
48 id
49 }
50
51 pub fn raise(&self, value: T) {
53 for sub in self.subs.iter() {
54 (sub.callback)(value.clone())
55 }
56 }
57
58 pub fn disconnect(&mut self, id: SubscriptionId) {
60 self.subs.retain(|sub| sub.id != id);
61 self.subs.shrink_to_fit();
62 }
63}
64
65#[cfg(test)]
66mod test {
67 use crate::Signal;
68 use std::{cell::Cell, rc::Rc};
69
70 #[test]
71 fn signal() {
72 let mut sig = Signal::new();
73
74 let data: Rc<Cell<u32>> = Rc::new(Cell::new(0));
75 assert_eq!(data.get(), 0);
76
77 let dc = data.clone();
78 let subid = sig.connect(move |v| {
79 dc.set(dc.get() + v);
80 });
81 assert_eq!(data.get(), 0);
82
83 sig.raise(1);
84 assert_eq!(data.get(), 1);
85
86 sig.raise(2);
87 assert_eq!(data.get(), 3);
88
89 sig.disconnect(subid);
90
91 sig.raise(0);
92 assert_eq!(data.get(), 3);
93 }
94
95 #[test]
96 fn signal_multiple_subscriptions() {
97 let mut sig = Signal::new();
98
99 let data: Rc<Cell<u32>> = Rc::new(Cell::new(0));
100 assert_eq!(data.get(), 0);
101
102 let dc = data.clone();
103 let sub1 = sig.connect(move |_v| {
104 dc.set(dc.get() + 1);
105 });
106 let dc = data.clone();
107 let sub2 = sig.connect(move |_v| {
108 dc.set(dc.get() + 10);
109 });
110
111 sig.raise(0);
112 assert_eq!(data.get(), 11);
113
114 sig.disconnect(sub1);
115
116 sig.raise(0);
117 assert_eq!(data.get(), 21);
118
119 sig.disconnect(sub2);
120 sig.raise(0);
121
122 sig.raise(0);
123 assert_eq!(data.get(), 21);
124 }
125}