tf_observer/
lib.rs

1/*
2 * Copyright 2021 Julian Schmidhuber <github@schmiddi.anonaddy.com>
3 *
4 * This file is part of Tubefeeder-extractor.
5 *
6 * Tubefeeder-extractor is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Tubefeeder-extractor is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Tubefeeder-extractor.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20//! The traits [`Observable`] and [`Observer`] construct the typical observer-pattern.
21//!
22//! The messages parsed in between [`Observable`] and [`Observer`] is the generic variable `T`.
23//! Because keeping track of all [`Observer`]s when implementing [`Observable`] can be hard,
24//! this module also contains a [`ObserverList`] where [`Observer`]s can be
25//! [`attached`][ObserverList::attach], [`detached`][ObserverList::detach] and
26//! [`notified`][ObserverList::notify].
27
28use std::sync::Arc;
29use std::sync::Mutex;
30use std::sync::Weak;
31
32#[cfg(test)]
33use mockall::predicate::*;
34#[cfg(test)]
35use mockall::*;
36
37type WeakObserver<T> = Weak<Mutex<Box<dyn Observer<T> + Send>>>;
38
39/// A [Observer] observing a [Observable<T>].
40///
41/// `T` is the message being sent from the [Observable<T>] to the [Observer<T>].
42#[cfg_attr(test, automock)]
43pub trait Observer<T> {
44    /// The [Observable<T>] sending a message to the [Observer<T>].
45    fn notify(&mut self, message: T);
46}
47
48/// A [Observable] that can be observed by [Observer<T>].
49///
50/// `T` is the message being sent from the [Observable<T>] to the [Observer<T>].
51/// This should be implemented using the [ObserverList<T>].
52pub trait Observable<T> {
53    /// Attach a [Observer<T>] to the [Observable].
54    ///
55    /// Should be implemented using [ObserverList::attach].
56    fn attach(&mut self, observer: WeakObserver<T>);
57
58    /// Detach a [Observer<T>] to the [Observable].
59    ///
60    /// Should be implemented using [ObserverList::detach].
61    fn detach(&mut self, observer: WeakObserver<T>);
62}
63
64/// A list of [Observer<T>] using the message `T`.
65#[derive(Clone)]
66pub struct ObserverList<T> {
67    /// The [Observer<T>] list.
68    observers: Arc<Mutex<Vec<WeakObserver<T>>>>,
69}
70
71impl<T> ObserverList<T> {
72    /// Create a new [ObserverList] with no [Observer]s.
73    pub fn new() -> Self {
74        ObserverList {
75            observers: Arc::new(Mutex::new(vec![])),
76        }
77    }
78
79    /// Give the count of active (not dropped) [Observer]s in the [ObserverList].
80    pub fn count(&self) -> usize {
81        self.observers
82            .lock()
83            .unwrap()
84            .iter()
85            .filter(|r| r.upgrade().is_some())
86            .count()
87    }
88}
89
90impl<T> Observable<T> for ObserverList<T> {
91    /// Attach a [Observer<T>] to the [ObserverList].
92    ///
93    /// This will check for duplicate [Observer]s using `Weak::ptr_eq` and not add them.
94    fn attach(&mut self, observer: Weak<Mutex<Box<dyn Observer<T> + Send>>>) {
95        let mut observers = self.observers.lock().unwrap();
96        if !observers.iter().any(|o| o.ptr_eq(&observer)) {
97            observers.push(observer);
98        }
99    }
100
101    /// Detach a [Observer<T>] to the [ObserverList].
102    ///
103    /// This will also detach all dropped [Observer]s.
104    fn detach(&mut self, observer: Weak<Mutex<Box<dyn Observer<T> + Send>>>) {
105        self.observers
106            .lock()
107            .unwrap()
108            .retain(|o| o.upgrade().is_some() && !o.ptr_eq(&observer));
109    }
110}
111
112impl<T: Clone> ObserverList<T> {
113    /// Notify all [Observer<T>] in the list with the given message.
114    ///
115    /// Only a clone of the message and not the real object will be sent.
116    pub fn notify(&self, message: T) {
117        self.observers.lock().unwrap().iter().for_each(|o| {
118            if let Some(mutex) = o.upgrade() {
119                if let Ok(mut observer) = mutex.lock() {
120                    observer.notify(message.clone());
121                }
122            }
123        })
124    }
125}
126
127impl<T> Default for ObserverList<T> {
128    fn default() -> Self {
129        ObserverList::new()
130    }
131}
132
133#[cfg(test)]
134mod test {
135    use super::*;
136    use std::sync::Arc;
137
138    #[test]
139    fn observer_list_attach_detach() {
140        let mut observer_list = ObserverList::new();
141
142        let observer1 = MockObserver::new();
143
144        let observer1_ref = Arc::new(Mutex::new(
145            Box::new(observer1) as Box<dyn Observer<u64> + Send>
146        ));
147
148        observer_list.attach(Arc::downgrade(&observer1_ref));
149
150        assert_eq!(
151            1,
152            observer_list.count(),
153            "The observable does not have the correct amount of observers"
154        );
155
156        observer_list.detach(Arc::downgrade(&observer1_ref));
157
158        assert_eq!(
159            0,
160            observer_list.count(),
161            "The observable does not have the correct amount of observers"
162        );
163    }
164
165    #[test]
166    fn observer_list_notify() {
167        let mut observer_list = ObserverList::new();
168
169        let mut observer1 = MockObserver::new();
170        observer1
171            .expect_notify()
172            .with(predicate::eq(10u64))
173            .times(1)
174            .returning(|_| ());
175
176        let observer1_ref = Arc::new(Mutex::new(
177            Box::new(observer1) as Box<dyn Observer<u64> + Send>
178        ));
179
180        observer_list.attach(Arc::downgrade(&observer1_ref));
181        observer_list.notify(10);
182    }
183
184    #[test]
185    fn observable_multi_observer() {
186        let mut observer_list = ObserverList::new();
187
188        let mut observer1 = MockObserver::new();
189        observer1
190            .expect_notify()
191            .with(predicate::eq(10u64))
192            .times(1)
193            .returning(|_| ());
194        observer1
195            .expect_notify()
196            .with(predicate::eq(20u64))
197            .times(1)
198            .returning(|_| ());
199
200        let mut observer2 = MockObserver::new();
201        observer2
202            .expect_notify()
203            .with(predicate::eq(20u64))
204            .times(1)
205            .returning(|_| ());
206
207        let observer1_ref = Arc::new(Mutex::new(
208            Box::new(observer1) as Box<dyn Observer<u64> + Send>
209        ));
210        let observer2_ref = Arc::new(Mutex::new(
211            Box::new(observer2) as Box<dyn Observer<u64> + Send>
212        ));
213
214        observer_list.attach(Arc::downgrade(&observer1_ref));
215        observer_list.notify(10);
216
217        observer_list.attach(Arc::downgrade(&observer2_ref));
218        observer_list.notify(20);
219    }
220
221    #[test]
222    fn observer_list_test_drop_inactive() {
223        let mut observer_list = ObserverList::new();
224
225        let observer1 = MockObserver::new();
226
227        let observer1_ref = Arc::new(Mutex::new(
228            Box::new(observer1) as Box<dyn Observer<u64> + Send>
229        ));
230
231        observer_list.attach(Arc::downgrade(&observer1_ref));
232
233        assert_eq!(
234            1,
235            observer_list.observers.lock().unwrap().len(),
236            "The observable does not have the correct amount of observers"
237        );
238
239        observer_list.detach(Weak::new());
240
241        assert_eq!(
242            1,
243            observer_list.observers.lock().unwrap().len(),
244            "The observable does not have the correct amount of observers"
245        );
246
247        drop(observer1_ref);
248        observer_list.detach(Weak::new());
249
250        assert_eq!(
251            0,
252            observer_list.observers.lock().unwrap().len(),
253            "The observable does not have the correct amount of observers"
254        );
255    }
256
257    #[test]
258    fn observer_list_test_clone() {
259        let mut observer_list = ObserverList::new();
260
261        let mut observer1 = MockObserver::new();
262        observer1
263            .expect_notify()
264            .with(predicate::eq(10u64))
265            .times(1)
266            .returning(|_| ());
267
268        let observer1_ref = Arc::new(Mutex::new(
269            Box::new(observer1) as Box<dyn Observer<u64> + Send>
270        ));
271
272        observer_list.attach(Arc::downgrade(&observer1_ref));
273
274        assert_eq!(
275            1,
276            observer_list.count(),
277            "The observable does not have the correct amount of observers"
278        );
279
280        let observer_list2 = observer_list.clone();
281
282        assert_eq!(
283            1,
284            observer_list2.count(),
285            "The observable does not have the correct amount of observers"
286        );
287
288        observer_list2.notify(10);
289    }
290
291    #[test]
292    fn observer_list_test_clone_pre_attach() {
293        let mut observer_list = ObserverList::new();
294
295        let mut observer1 = MockObserver::new();
296        observer1
297            .expect_notify()
298            .with(predicate::eq(10u64))
299            .times(1)
300            .returning(|_| ());
301
302        let observer1_ref = Arc::new(Mutex::new(
303            Box::new(observer1) as Box<dyn Observer<u64> + Send>
304        ));
305
306        let observer_list2 = observer_list.clone();
307
308        observer_list.attach(Arc::downgrade(&observer1_ref));
309        drop(observer_list);
310
311        assert_eq!(
312            1,
313            observer_list2.count(),
314            "The observable does not have the correct amount of observers"
315        );
316
317        observer_list2.notify(10);
318    }
319}