1use crate::{Callback, ChangeToken, Registration, SharedChangeToken, SingleChangeToken};
2use std::{
3 any::Any,
4 sync::{Arc, Weak},
5};
6
7struct Mediator {
8 parent: SharedChangeToken<SingleChangeToken>,
9 children: Vec<Box<dyn ChangeToken>>,
10 _registrations: Vec<Registration>,
11}
12
13impl Mediator {
14 fn new(
15 parent: SharedChangeToken<SingleChangeToken>,
16 tokens: Vec<Box<dyn ChangeToken>>,
17 ) -> Arc<Self> {
18 Arc::new_cyclic(|me| {
19 let registrations = Self::register(me, &tokens);
20 Self {
21 parent,
22 children: tokens,
23 _registrations: registrations,
24 }
25 })
26 }
27
28 fn register(me: &Weak<Self>, tokens: &[Box<dyn ChangeToken>]) -> Vec<Registration> {
29 let mut registrations = Vec::with_capacity(tokens.len());
30
31 for token in tokens {
32 if token.must_poll() {
33 continue;
34 }
35
36 let registration = token.register(
37 Box::new(|state| {
38 let weak = state.unwrap();
39 if let Some(this) = weak.downcast_ref::<Weak<Self>>().unwrap().upgrade() {
40 this.parent.notify();
41 }
42 }),
43 Some(Arc::new(me.clone())),
44 );
45
46 registrations.push(registration);
47 }
48
49 registrations
50 }
51}
52
53pub struct CompositeChangeToken {
55 inner: SharedChangeToken<SingleChangeToken>,
56 mediator: Arc<Mediator>,
57}
58
59impl CompositeChangeToken {
60 pub fn new(tokens: impl Iterator<Item = Box<dyn ChangeToken>>) -> Self {
66 let inner = SharedChangeToken::<SingleChangeToken>::default();
67 let shared: SharedChangeToken<SingleChangeToken> = inner.clone();
68 Self {
69 inner,
70 mediator: Mediator::new(shared, tokens.collect()),
71 }
72 }
73
74 pub fn notify(&self) {
76 self.inner.notify()
77 }
78}
79
80impl ChangeToken for CompositeChangeToken {
81 fn changed(&self) -> bool {
82 self.inner.changed() || self.mediator.children.iter().any(|t| t.changed())
83 }
84
85 fn must_poll(&self) -> bool {
86 self.mediator.children.iter().all(|t| t.must_poll())
87 }
88
89 fn register(&self, callback: Callback, state: Option<Arc<dyn Any>>) -> Registration {
90 self.inner.register(callback, state)
91 }
92}
93
94#[cfg(test)]
95mod tests {
96
97 use super::*;
98 use crate::*;
99 use std::iter::empty;
100 use std::sync::{
101 atomic::{AtomicU8, Ordering},
102 Arc,
103 };
104
105 #[test]
106 fn changed_should_be_false_when_no_changes_have_occurred() {
107 let token = CompositeChangeToken::new(empty());
109
110 let changed = token.changed();
112
113 assert_eq!(changed, false);
115 }
116
117 #[test]
118 fn changed_should_be_true_if_child_has_changed() {
119 let child = SingleChangeToken::new();
121
122 child.notify();
123
124 let child: Box<dyn ChangeToken> = Box::new(child);
125 let tokens = vec![child];
126 let token = CompositeChangeToken::new(tokens.into_iter());
127
128 let changed = token.changed();
130
131 assert_eq!(changed, true);
133 }
134
135 #[test]
136 fn changed_should_be_true_if_ever_notified() {
137 let child: Box<dyn ChangeToken> = Box::new(SingleChangeToken::new());
139 let tokens = vec![child];
140 let token = CompositeChangeToken::new(tokens.into_iter());
141
142 token.notify();
143
144 let changed = token.changed();
146
147 assert_eq!(changed, true);
149 }
150
151 #[test]
152 fn must_poll_should_be_true_all_tokens_do_not_support_callbacks() {
153 let child: Box<dyn ChangeToken> = Box::new(NeverChangeToken::new());
155 let tokens = vec![child];
156 let token = CompositeChangeToken::new(tokens.into_iter());
157
158 let poll_required = token.must_poll();
160
161 assert_eq!(poll_required, true);
163 }
164
165 #[test]
166 fn must_poll_should_be_false_if_at_least_one_token_supports_callbacks() {
167 let tokens: Vec<Box<dyn ChangeToken>> = vec![
169 Box::new(NeverChangeToken::new()),
170 Box::new(SingleChangeToken::new()),
171 ];
172 let token = CompositeChangeToken::new(tokens.into_iter());
173
174 let poll_required = token.must_poll();
176
177 assert_eq!(poll_required, false);
179 }
180
181 #[test]
182 fn child_should_trigger_parent_callbacks() {
183 let child = SharedChangeToken::<DefaultChangeToken>::default();
185 let tokens: Vec<Box<dyn ChangeToken>> = vec![Box::new(child.clone())];
186 let token = CompositeChangeToken::new(tokens.into_iter());
187 let counter = Arc::new(AtomicU8::default());
188 let _registration = token.register(
189 Box::new(|state| {
190 state
191 .unwrap()
192 .downcast_ref::<AtomicU8>()
193 .unwrap()
194 .fetch_add(1, Ordering::SeqCst);
195 }),
196 Some(counter.clone()),
197 );
198
199 child.notify();
201
202 assert_eq!(counter.load(Ordering::SeqCst), 1);
204 }
205
206 #[test]
207 fn child_should_not_trigger_callbacks_multiple_times() {
208 let child = SharedChangeToken::<DefaultChangeToken>::default();
210 let tokens: Vec<Box<dyn ChangeToken>> = vec![Box::new(child.clone())];
211 let token = CompositeChangeToken::new(tokens.into_iter());
212 let counter = Arc::new(AtomicU8::default());
213 let _registration = token.register(
214 Box::new(|state| {
215 state
216 .unwrap()
217 .downcast_ref::<AtomicU8>()
218 .unwrap()
219 .fetch_add(1, Ordering::SeqCst);
220 }),
221 Some(counter.clone()),
222 );
223
224 child.notify();
225
226 child.notify();
228
229 assert_eq!(counter.load(Ordering::SeqCst), 1);
231 }
232
233 #[test]
234 fn notify_should_not_trigger_callbacks_multiple_times() {
235 let child: Box<dyn ChangeToken> = Box::new(NeverChangeToken::new());
237 let tokens = vec![child];
238 let token = CompositeChangeToken::new(tokens.into_iter());
239 let counter = Arc::new(AtomicU8::default());
240 let _registration = token.register(
241 Box::new(|state| {
242 state
243 .unwrap()
244 .downcast_ref::<AtomicU8>()
245 .unwrap()
246 .fetch_add(1, Ordering::SeqCst);
247 }),
248 Some(counter.clone()),
249 );
250
251 token.notify();
252
253 token.notify();
255
256 assert_eq!(counter.load(Ordering::SeqCst), 1);
258 }
259}