Skip to main content

ferroui_base/reactive/
anonymous_observer.rs

1use super::anonymous_observer_non_generic_helper::AnonymousObserverNonGenericHelper;
2use super::{IObserver, ObservableError};
3
4/// An observer built from closures.
5///
6/// An observer without a closure for errors rethrows an error it is given
7/// (it panics with the message of the error); one without a closure for
8/// completion ignores it.
9pub struct AnonymousObserver<T> {
10    on_next: Box<dyn Fn(T)>,
11    on_error: Box<dyn Fn(ObservableError)>,
12    on_completed: Box<dyn Fn()>,
13}
14
15impl<T> AnonymousObserver<T> {
16    pub fn new(on_next: impl Fn(T) + 'static) -> Self {
17        Self {
18            on_next: Box::new(on_next),
19            on_error: Box::new(AnonymousObserverNonGenericHelper::throws_on_error),
20            on_completed: Box::new(AnonymousObserverNonGenericHelper::no_op_completed),
21        }
22    }
23
24    pub fn new_with_error(on_next: impl Fn(T) + 'static, on_error: impl Fn(ObservableError) + 'static) -> Self {
25        Self::new(on_next).with_error(on_error)
26    }
27
28    pub fn new_with_completed(on_next: impl Fn(T) + 'static, on_completed: impl Fn() + 'static) -> Self {
29        Self::new(on_next).with_completed(on_completed)
30    }
31
32    pub fn new_with_error_and_completed(
33        on_next: impl Fn(T) + 'static,
34        on_error: impl Fn(ObservableError) + 'static,
35        on_completed: impl Fn() + 'static,
36    ) -> Self {
37        Self::new(on_next).with_error(on_error).with_completed(on_completed)
38    }
39
40    pub fn with_error(mut self, on_error: impl Fn(ObservableError) + 'static) -> Self {
41        self.on_error = Box::new(on_error);
42        self
43    }
44
45    pub fn with_completed(mut self, on_completed: impl Fn() + 'static) -> Self {
46        self.on_completed = Box::new(on_completed);
47        self
48    }
49}
50
51impl<T> IObserver<T> for AnonymousObserver<T> {
52    fn on_next(&self, value: T) {
53        (self.on_next)(value)
54    }
55
56    fn on_error(&self, error: ObservableError) {
57        (self.on_error)(error)
58    }
59
60    fn on_completed(&self) {
61        (self.on_completed)()
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    // Not from upstream: the original has no tests of this type.
68    use super::*;
69    use std::cell::Cell;
70    use std::rc::Rc;
71
72    fn error() -> ObservableError {
73        Rc::new(std::fmt::Error)
74    }
75
76    #[test]
77    fn notifications_reach_the_closures() {
78        let next = Rc::new(Cell::new(0));
79        let errors = Rc::new(Cell::new(0));
80        let completed = Rc::new(Cell::new(0));
81        let (n, e, c) = (next.clone(), errors.clone(), completed.clone());
82        let observer = AnonymousObserver::new_with_error_and_completed(
83            move |value: i32| n.set(n.get() + value),
84            move |_| e.set(e.get() + 1),
85            move || c.set(c.get() + 1),
86        );
87
88        observer.on_next(2);
89        observer.on_next(3);
90        observer.on_error(error());
91        observer.on_completed();
92
93        assert_eq!((5, 1, 1), (next.get(), errors.get(), completed.get()));
94    }
95
96    #[test]
97    fn completion_is_ignored_without_a_closure() {
98        AnonymousObserver::new(|_: i32| {}).on_completed();
99        AnonymousObserver::new_with_error(|_: i32| {}, |_| {}).on_completed();
100    }
101
102    #[test]
103    #[should_panic]
104    fn an_error_is_rethrown_without_a_closure() {
105        AnonymousObserver::new_with_completed(|_: i32| {}, || {}).on_error(error());
106    }
107}