amethystate_core/primitives/
pipeline.rs1use crate::primitives::signal::{Signal, SignalSubscription};
2use std::sync::{Arc, Mutex};
3use uuid::Uuid;
4
5pub trait Reactive<T>: Clone + Send + Sync + 'static
6where
7 T: Clone + Send + Sync + 'static,
8{
9 fn get(&self) -> T;
10
11 fn subscribe_with_source<F>(&self, callback: F) -> SignalSubscription
12 where
13 F: Fn(T, Option<Uuid>) + Send + Sync + 'static;
14
15 fn subscribe<F>(&self, callback: F) -> SignalSubscription
16 where
17 F: Fn(T) + Send + Sync + 'static,
18 {
19 self.subscribe_with_source(move |v, _src| callback(v))
20 }
21
22 fn keepalive(&self) -> Option<Arc<dyn Send + Sync>> {
23 None
24 }
25}
26
27pub trait IntoPipeline<T>
28where
29 T: Clone + Send + Sync + 'static,
30{
31 fn pipe(self) -> Pipeline<T>;
32}
33
34struct PipelineInner<T> {
35 signal: Arc<Signal<T>>,
36 _source_subs: Vec<SignalSubscription>,
37 _keepalive: Vec<Arc<dyn Send + Sync>>,
38}
39
40pub struct Pipeline<T> {
41 inner: Arc<PipelineInner<T>>,
42}
43
44impl<T> Clone for Pipeline<T> {
45 fn clone(&self) -> Self {
46 Self {
47 inner: Arc::clone(&self.inner),
48 }
49 }
50}
51
52impl<T> Pipeline<T>
53where
54 T: Clone + Send + Sync + 'static,
55{
56 pub fn from_signal(
57 signal: Arc<Signal<T>>,
58 source_subs: Vec<SignalSubscription>,
59 keepalive: Vec<Arc<dyn Send + Sync>>,
60 ) -> Self {
61 Self {
62 inner: Arc::new(PipelineInner {
63 signal,
64 _source_subs: source_subs,
65 _keepalive: keepalive,
66 }),
67 }
68 }
69
70 pub fn get(&self) -> T {
71 self.inner.signal.get()
72 }
73
74 pub fn subscribe_with_source<F>(&self, callback: F) -> SignalSubscription
75 where
76 F: Fn(T, Option<Uuid>) + Send + Sync + 'static,
77 {
78 self.inner
79 .signal
80 .subscribe_with_source(move |val, src| callback(val.clone(), src))
81 }
82
83 pub fn subscribe<F>(&self, callback: F) -> SignalSubscription
84 where
85 F: Fn(T) + Send + Sync + 'static,
86 {
87 self.subscribe_with_source(move |val, _src| callback(val))
88 }
89
90 pub fn map<U, F>(self, f: F) -> Pipeline<U>
91 where
92 U: Clone + Send + Sync + 'static,
93 F: Fn(T) -> U + Send + Sync + 'static,
94 {
95 let f = Arc::new(f);
96 let initial = f(self.get());
97 let signal = Arc::new(Signal::new(initial));
98 let target = Arc::clone(&signal);
99 let mapper = Arc::clone(&f);
100
101 let sub = self.subscribe_with_source(move |value, source| {
102 target.set(mapper(value), source);
103 });
104
105 Pipeline {
106 inner: Arc::new(PipelineInner {
107 signal,
108 _source_subs: vec![sub],
109 _keepalive: vec![self.inner],
110 }),
111 }
112 }
113
114 pub fn filter_map<U, F>(self, f: F) -> Pipeline<U>
115 where
116 U: Default + Clone + Send + Sync + 'static,
117 F: Fn(T) -> Option<U> + Send + Sync + 'static,
118 {
119 let f = Arc::new(f);
120 let initial = f(self.get()).unwrap_or_default();
121 let signal = Arc::new(Signal::new(initial));
122 let target = Arc::clone(&signal);
123 let mapper = Arc::clone(&f);
124
125 let sub = self.subscribe_with_source(move |value, source| {
126 if let Some(mapped) = mapper(value) {
127 target.set(mapped, source);
128 }
129 });
130
131 Pipeline {
132 inner: Arc::new(PipelineInner {
133 signal,
134 _source_subs: vec![sub],
135 _keepalive: vec![self.inner],
136 }),
137 }
138 }
139
140 pub fn inspect<F>(self, f: F) -> Pipeline<T>
141 where
142 F: Fn(&T) + Send + Sync + 'static,
143 {
144 let f = Arc::new(f);
145 let initial = self.get();
146 f(&initial);
147
148 let signal = Arc::new(Signal::new(initial));
149 let target = Arc::clone(&signal);
150 let inspector = Arc::clone(&f);
151
152 let sub = self.subscribe_with_source(move |value, source| {
153 inspector(&value);
154 target.set(value, source);
155 });
156
157 Pipeline {
158 inner: Arc::new(PipelineInner {
159 signal,
160 _source_subs: vec![sub],
161 _keepalive: vec![self.inner],
162 }),
163 }
164 }
165
166 pub fn dedupe(self) -> Pipeline<T>
167 where
168 T: PartialEq,
169 {
170 let initial = self.get();
171 let last = Arc::new(Mutex::new(initial.clone()));
172 let signal = Arc::new(Signal::new(initial));
173 let target = Arc::clone(&signal);
174 let last_seen = Arc::clone(&last);
175
176 let sub = self.subscribe_with_source(move |value, source| {
177 let mut last = last_seen.lock().unwrap();
178 if *last != value {
179 *last = value.clone();
180 target.set(value, source);
181 }
182 });
183
184 Pipeline {
185 inner: Arc::new(PipelineInner {
186 signal,
187 _source_subs: vec![sub],
188 _keepalive: vec![self.inner],
189 }),
190 }
191 }
192}
193
194impl<T> Reactive<T> for Pipeline<T>
195where
196 T: Clone + Send + Sync + 'static,
197{
198 fn get(&self) -> T {
199 self.get()
200 }
201
202 fn subscribe_with_source<F>(&self, callback: F) -> SignalSubscription
203 where
204 F: Fn(T, Option<Uuid>) + Send + Sync + 'static,
205 {
206 self.subscribe_with_source(callback)
207 }
208
209 fn keepalive(&self) -> Option<Arc<dyn Send + Sync>> {
210 Some(self.inner.clone())
211 }
212}
213
214#[derive(Default, Clone)]
215pub struct ReactiveScope {
216 subs: Vec<SignalSubscription>,
217}
218
219impl ReactiveScope {
220 pub fn new() -> Self {
221 Self::default()
222 }
223
224 pub fn watch(&mut self, sub: SignalSubscription) {
225 self.subs.push(sub);
226 }
227
228 pub fn watch_scope(&mut self, mut other: Self) {
229 self.subs.append(&mut other.subs);
230 }
231
232 pub fn clear(&mut self) {
233 self.subs.clear();
234 }
235
236 pub fn len(&self) -> usize {
237 self.subs.len()
238 }
239
240 pub fn is_empty(&self) -> bool {
241 self.subs.is_empty()
242 }
243}
244
245impl<R, T> IntoPipeline<T> for R
246where
247 R: Reactive<T>,
248 T: Clone + Send + Sync + 'static,
249{
250 fn pipe(self) -> Pipeline<T> {
251 let initial = self.get();
252 let signal = Arc::new(Signal::new(initial));
253 let target = Arc::clone(&signal);
254 let sub = self.subscribe_with_source(move |value, source| {
255 target.set(value, source);
256 });
257
258 Pipeline {
259 inner: Arc::new(PipelineInner {
260 signal,
261 _source_subs: vec![sub],
262 _keepalive: self.keepalive().into_iter().collect(),
263 }),
264 }
265 }
266}
267
268macro_rules! impl_tuple_pipeline {
269 ($(($source_ty:ident, $source:ident, $value_ty:ident, $value:ident)),+ $(,)?) => {
270 impl<$($source_ty, $value_ty),+> IntoPipeline<($($value_ty,)+)> for ($($source_ty,)+)
271 where
272 $(
273 $source_ty: Reactive<$value_ty>,
274 $value_ty: Clone + Send + Sync + 'static,
275 )+
276 {
277 #[allow(non_snake_case)]
278 fn pipe(self) -> Pipeline<($($value_ty,)+)> {
279 let ($($source,)+) = self;
280 let initial = ($($source.get(),)+);
281 let signal = Arc::new(Signal::new(initial));
282 let mut source_subs = Vec::new();
283 let mut keepalive = Vec::new();
284
285 let refresh: Arc<dyn Fn() -> ($($value_ty,)+) + Send + Sync> = {
286 $(let $value = $source.clone();)+
287 Arc::new(move || ($($value.get(),)+))
288 };
289
290 $(
291 if let Some(inner) = $source.keepalive() {
292 keepalive.push(inner);
293 }
294
295 let target = Arc::clone(&signal);
296 let refresh_cb = Arc::clone(&refresh);
297 source_subs.push($source.subscribe_with_source(move |_, src| {
298 target.set(refresh_cb(), src);
299 }));
300 )+
301
302 Pipeline::from_signal(signal, source_subs, keepalive)
303 }
304 }
305 };
306}
307
308impl_tuple_pipeline!((RA, ra, A, a), (RB, rb, B, b));
309impl_tuple_pipeline!((RA, ra, A, a), (RB, rb, B, b), (RC, rc, C, c));
310impl_tuple_pipeline!(
311 (RA, ra, A, a),
312 (RB, rb, B, b),
313 (RC, rc, C, c),
314 (RD, rd, D, d)
315);