floem_reactive/
derived.rs

1use std::marker::PhantomData;
2
3use crate::{
4    read::SignalTrack, signal::NotThreadSafe, RwSignal, SignalGet, SignalUpdate, SignalWith,
5};
6
7/// A signal that is derived from an [RwSignal](super::RwSignal) but lets you specify getters and setters for the signal.
8///
9/// This is useful when you want a single state variable and don't want to use effects to synchronize multiple signals.
10///
11/// This is also useful when you want a derived signal that implements the [SignalGet], [SignalWith], etc. traits.
12pub struct DerivedRwSignal<
13    T: 'static,
14    O,
15    GF: Fn(&T) -> O + Clone + 'static,
16    UF: Fn(&O) -> T + 'static,
17> {
18    signal: RwSignal<T>,
19    getter: RwSignal<Box<GF>>,
20    setter: RwSignal<Box<UF>>,
21    ty: PhantomData<T>,
22    ts: PhantomData<NotThreadSafe>,
23}
24
25impl<T, O, GF: Fn(&T) -> O + Copy, UF: Fn(&O) -> T + Copy> Clone for DerivedRwSignal<T, O, GF, UF> {
26    fn clone(&self) -> Self {
27        *self
28    }
29}
30
31impl<T, O, GF: Fn(&T) -> O + Copy, UF: Fn(&O) -> T + Copy> Copy for DerivedRwSignal<T, O, GF, UF> {}
32
33impl<T: Clone + 'static, O: Clone, GF: Fn(&T) -> O + Copy, UF: Fn(&O) -> T + Copy> SignalGet<O>
34    for DerivedRwSignal<T, O, GF, UF>
35{
36    fn id(&self) -> crate::id::Id {
37        self.signal.id
38    }
39
40    fn try_get(&self) -> Option<O>
41    where
42        O: 'static,
43    {
44        let sig = self.getter;
45        SignalGet::id(self).signal().map(|signal| {
46            let func = sig.get_untracked();
47            func(&signal.get()).clone()
48        })
49    }
50
51    fn try_get_untracked(&self) -> Option<O>
52    where
53        O: 'static,
54    {
55        let sig = self.getter;
56        SignalGet::id(self).signal().map(|signal| {
57            let func = sig.get_untracked();
58            func(&signal.get_untracked()).clone()
59        })
60    }
61}
62
63impl<T: Clone + 'static, O: Clone, GF: Fn(&T) -> O + Copy, UF: Fn(&O) -> T + Copy> SignalWith<O>
64    for DerivedRwSignal<T, O, GF, UF>
65{
66    fn id(&self) -> crate::id::Id {
67        self.signal.id
68    }
69
70    fn with<O2>(&self, f: impl FnOnce(&O) -> O2) -> O2
71    where
72        T: 'static,
73    {
74        let func = self.getter.get_untracked();
75        SignalWith::id(self).signal().unwrap().with(|t| f(&func(t)))
76    }
77
78    fn with_untracked<O2>(&self, f: impl FnOnce(&O) -> O2) -> O2
79    where
80        T: 'static,
81    {
82        let func = self.getter.get_untracked();
83        SignalWith::id(self)
84            .signal()
85            .unwrap()
86            .with_untracked(|t| f(&func(t)))
87    }
88
89    fn try_with<O2>(&self, f: impl FnOnce(Option<&O>) -> O2) -> O2
90    where
91        T: 'static,
92    {
93        if let Some(signal) = SignalWith::id(self).signal() {
94            let func = self.getter.get_untracked();
95            signal.with(|v| f(Some(&func(v))))
96        } else {
97            f(None)
98        }
99    }
100
101    fn try_with_untracked<O2>(&self, f: impl FnOnce(Option<&O>) -> O2) -> O2
102    where
103        T: 'static,
104    {
105        if let Some(signal) = SignalWith::id(self).signal() {
106            let func = self.getter.get_untracked();
107            signal.with_untracked(|v| f(Some(&func(v))))
108        } else {
109            f(None)
110        }
111    }
112}
113
114impl<T: Clone + 'static, O: Clone, GF: Fn(&T) -> O + Copy, UF: Fn(&O) -> T + Copy> SignalTrack<O>
115    for DerivedRwSignal<T, O, GF, UF>
116{
117    fn id(&self) -> crate::id::Id {
118        self.signal.id
119    }
120    fn track(&self) {
121        SignalWith::id(self).signal().unwrap().subscribe();
122    }
123
124    fn try_track(&self) {
125        if let Some(signal) = SignalWith::id(self).signal() {
126            signal.subscribe();
127        }
128    }
129}
130
131impl<T: 'static, O, GF: Fn(&T) -> O + Copy, UF: Fn(&O) -> T + Copy> SignalUpdate<O>
132    for DerivedRwSignal<T, O, GF, UF>
133{
134    fn id(&self) -> crate::id::Id {
135        self.signal.id
136    }
137
138    fn set(&self, new_value: O)
139    where
140        O: 'static,
141    {
142        if let Some(signal) = self.id().signal() {
143            let func = self.setter.get_untracked();
144            signal.update_value::<_, T>(|v| {
145                let new = func(&new_value);
146                *v = new;
147            });
148        }
149    }
150
151    fn update(&self, f: impl FnOnce(&mut O))
152    where
153        O: 'static,
154    {
155        if let Some(signal) = self.id().signal() {
156            let get_func = self.getter.get_untracked();
157            let set_func = self.setter.get_untracked();
158            signal.update_value::<_, T>(|cv| {
159                let mut new = get_func(cv);
160                f(&mut new);
161                let new = set_func(&new);
162                *cv = new;
163            });
164        }
165    }
166
167    fn try_update<O2>(&self, f: impl FnOnce(&mut O) -> O2) -> Option<O2>
168    where
169        O: 'static,
170    {
171        self.id().signal().map(|signal| {
172            let get_func = self.getter.get_untracked();
173            let set_func = self.setter.get_untracked();
174            signal.update_value::<_, T>(|cv| {
175                let mut new = get_func(cv);
176                let ret = f(&mut new);
177                let new = set_func(&new);
178                *cv = new;
179                ret
180            })
181        })
182    }
183}
184impl<T, O, GF, UF> DerivedRwSignal<T, O, GF, UF>
185where
186    GF: Fn(&T) -> O + Clone + 'static,
187    UF: Fn(&O) -> T + 'static,
188{
189    pub fn new(signal: RwSignal<T>, getter: GF, setter: UF) -> Self {
190        let getter = RwSignal::new(Box::new(getter));
191        let setter = RwSignal::new(Box::new(setter));
192        DerivedRwSignal {
193            signal,
194            getter,
195            setter,
196            ty: PhantomData,
197            ts: PhantomData,
198        }
199    }
200}
201
202pub fn create_derived_rw_signal<T, O, GF, UF>(
203    signal: RwSignal<T>,
204    getter: GF,
205    setter: UF,
206) -> DerivedRwSignal<T, O, GF, UF>
207where
208    GF: Fn(&T) -> O + Clone + 'static,
209    UF: Fn(&O) -> T + 'static,
210{
211    let getter = RwSignal::new(Box::new(getter));
212    let setter = RwSignal::new(Box::new(setter));
213    DerivedRwSignal {
214        signal,
215        getter,
216        setter,
217        ty: PhantomData,
218        ts: PhantomData,
219    }
220}