maycoon_core/signal/
map.rs

1use crate::signal::{BoxedSignal, Ref, Signal};
2use std::rc::Rc;
3
4/// A signal wrapping another signal and applying a mapping function, when the inner value is requested.
5/// The mapping function will be called every time the inner value is requested via [Signal::get].
6/// This signal cannot be directly mutated. Use [MapSignal::signal] to get the inner signal.
7///
8/// Calling [Signal::set], [Signal::set_value], [Signal::listen] has no effect.
9pub struct MapSignal<T: 'static, U: 'static> {
10    signal: BoxedSignal<T>,
11    map: Rc<dyn Fn(Ref<T>) -> Ref<U>>,
12}
13
14impl<T: 'static, U: 'static> MapSignal<T, U> {
15    /// Create a new map signal using the given inner signal and mapping function.
16    #[inline(always)]
17    pub fn new(signal: BoxedSignal<T>, map: impl Fn(Ref<T>) -> Ref<U> + 'static) -> Self {
18        Self {
19            signal,
20            map: Rc::new(map),
21        }
22    }
23
24    /// Get the inner signal.
25    ///
26    /// Can be used to mutate the inner value.
27    #[inline(always)]
28    pub fn signal(&self) -> BoxedSignal<T> {
29        self.signal.dyn_clone()
30    }
31
32    /// Get the inner signal's value, without applying the mapping function.
33    #[inline(always)]
34    pub fn get_unmapped(&self) -> Ref<'_, T> {
35        self.signal.get()
36    }
37}
38
39impl<T: 'static, U: 'static> Signal<U> for MapSignal<T, U> {
40    #[inline(always)]
41    fn get(&self) -> Ref<'_, U> {
42        (self.map)(self.get_unmapped())
43    }
44
45    #[inline(always)]
46    fn set_value(&self, _: U) {}
47
48    #[inline(always)]
49    fn listen(self, _: Box<dyn Fn(Ref<'_, U>)>) -> Self
50    where
51        Self: Sized,
52    {
53        self
54    }
55
56    #[inline(always)]
57    fn notify(&self) {
58        self.signal.notify();
59    }
60
61    #[inline(always)]
62    fn dyn_clone(&self) -> Box<dyn Signal<U>> {
63        Box::new(self.clone())
64    }
65}
66
67impl<T: 'static, U: 'static> Clone for MapSignal<T, U> {
68    #[inline(always)]
69    fn clone(&self) -> Self {
70        Self {
71            signal: self.signal.dyn_clone(),
72            map: self.map.clone(),
73        }
74    }
75}
76
77#[cfg(all(test, feature = "test"))]
78mod tests {
79    use crate::reference::Ref;
80    use crate::signal::Signal;
81    use crate::signal::fixed::FixedSignal;
82
83    /// Tests the [MapSignal] implementation of [Signal].
84    #[test]
85    fn test_map_signal() {
86        let signal = FixedSignal::new(0)
87            .map(|i| Ref::Owned(i.to_string()))
88            .map(|s| Ref::Owned(s.len()))
89            .map(|len| Ref::Owned(len.to_string()));
90
91        assert_eq!(*signal.get(), "1".to_string());
92    }
93}