zng_var/
merge.rs

1use std::{
2    marker::PhantomData,
3    ops,
4    sync::{Arc, Weak},
5};
6
7use super::{util::VarData, *};
8
9///<span data-del-macro-root></span> Initializes a new [`Var`](crate::Var) with value made
10/// by merging multiple other variables.
11///
12/// # Arguments
13///
14/// All arguments are separated by comma like a function call.
15///
16/// * `var0..N`: A list of [vars](crate::Var), minimal 2.
17/// * `merge`: A new closure that produces a new value from references to all variable values. `FnMut(&var0_T, ..) -> merge_T`
18///
19/// # Contextualized
20///
21/// The merge var is contextualized when needed, meaning if any input [`is_contextual`] at the moment the var is created it
22/// is also contextual. The full output type of this macro is a `BoxedVar<T>` that is either an `ArcMergeVar<T>` or
23/// a `ContextualizedVar<T, ArcMergeVar<T>>`.
24///
25/// [`is_contextual`]: AnyVar::is_contextual
26///
27/// # Examples
28///
29/// ```
30/// # use zng_var::*;
31/// # use zng_txt::*;
32/// # macro_rules! Text { ($($tt:tt)*) => { () } }
33/// let var0: ArcVar<Txt> = var_from("Hello");
34/// let var1: ArcVar<Txt> = var_from("World");
35///
36/// let greeting_text = Text!(merge_var!(var0, var1, |a, b| formatx!("{a} {b}!")));
37/// ```
38#[macro_export]
39macro_rules! merge_var {
40    ($($tt:tt)+) => {
41        $crate::types::__merge_var! {
42            $crate,
43            $($tt)+
44        }
45    };
46}
47
48use parking_lot::Mutex;
49#[doc(hidden)]
50pub use zng_var_proc_macros::merge_var as __merge_var;
51
52// used by the __merge_var! proc-macro.
53#[doc(hidden)]
54pub struct ArcMergeVarInput<T: VarValue, V: Var<T>>(PhantomData<(V, T)>);
55impl<T: VarValue, V: Var<T>> ArcMergeVarInput<T, V> {
56    pub fn new(_: &V) -> Self {
57        ArcMergeVarInput(PhantomData)
58    }
59
60    #[expect(clippy::borrowed_box)]
61    pub fn get<'v>(&self, value: &'v Box<dyn AnyVarValue>) -> &'v T {
62        (**value).as_any().downcast_ref::<T>().unwrap()
63    }
64}
65
66struct MergeData<T> {
67    _input_vars: Box<[BoxedAnyVar]>,
68    inputs: Box<[Box<dyn AnyVarValue>]>,
69    input_handles: Box<[VarHandle]>,
70    merge: Box<dyn FnMut(&[Box<dyn AnyVarValue>]) -> T + Send + Sync>,
71}
72
73struct Data<T: VarValue> {
74    m: Mutex<MergeData<T>>,
75    value: VarData,
76}
77
78/// See [`merge_var!`].
79pub struct ArcMergeVar<T: VarValue>(Arc<Data<T>>);
80
81/// Weak reference to [`ArcMergeVar<T>`].
82pub struct WeakMergeVar<T: VarValue>(Weak<Data<T>>);
83
84impl<T: VarValue> ArcMergeVar<T> {
85    #[doc(hidden)]
86    #[expect(clippy::new_ret_no_self)]
87    pub fn new(inputs: Box<[BoxedAnyVar]>, merge: impl FnMut(&[Box<dyn AnyVarValue>]) -> T + Send + 'static) -> BoxedVar<T> {
88        if inputs.iter().any(|v| v.is_contextual()) {
89            let merge = Arc::new(Mutex::new(merge));
90            types::ContextualizedVar::new(move || {
91                let merge = merge.clone();
92                ArcMergeVar::new_impl(Cow::Borrowed(&inputs), Box::new(move |values| merge.lock()(values)))
93            })
94            .boxed()
95        } else {
96            let merge = Mutex::new(merge);
97            ArcMergeVar::new_impl(Cow::Owned(inputs), Box::new(move |values| merge.lock()(values))).boxed()
98        }
99    }
100
101    fn new_impl(input_vars: Cow<Box<[BoxedAnyVar]>>, mut merge: Box<dyn FnMut(&[Box<dyn AnyVarValue>]) -> T + Send + Sync>) -> Self {
102        let inputs: Box<[_]> = input_vars.iter().map(|v| v.get_any()).collect();
103        let rc_merge = Arc::new(Data {
104            value: VarData::new(merge(&inputs)),
105            m: Mutex::new(MergeData {
106                _input_vars: Box::new([]),
107                inputs,
108                input_handles: Box::new([]),
109                merge,
110            }),
111        });
112        let wk_merge = Arc::downgrade(&rc_merge);
113
114        let input_handles: Box<[_]> = input_vars
115            .iter()
116            .enumerate()
117            .filter_map(|(i, var)| {
118                if var.capabilities().contains(VarCapability::NEW) {
119                    let wk_merge = wk_merge.clone();
120                    let handle = var.hook_any(Box::new(move |args| {
121                        if let Some(rc_merge) = wk_merge.upgrade() {
122                            let mut data = rc_merge.m.lock();
123                            let data_mut = &mut *data;
124                            if args.value_type() == data_mut.inputs[i].as_any().type_id() {
125                                data_mut.inputs[i] = args.value().clone_boxed();
126
127                                drop(data);
128                                VARS.schedule_update(ArcMergeVar::update_merge(rc_merge), std::any::type_name::<T>());
129                            }
130                            true
131                        } else {
132                            false
133                        }
134                    }));
135
136                    debug_assert!(!handle.is_dummy());
137
138                    Some(handle)
139                } else {
140                    None
141                }
142            })
143            .collect();
144
145        {
146            let mut l = rc_merge.m.lock();
147            l.input_handles = input_handles;
148            if let Cow::Owned(v) = input_vars {
149                l._input_vars = v;
150            } // else they are held by the ContextualizedVar
151        }
152
153        Self(rc_merge)
154    }
155
156    fn update_merge(rc_merge: Arc<Data<T>>) -> VarUpdateFn {
157        Box::new(move || {
158            let mut m = rc_merge.m.lock();
159            let m = &mut *m;
160            let new_value = (m.merge)(&m.inputs);
161            {
162                let modify = |v: &mut VarModify<T>| v.set(new_value);
163                #[cfg(feature = "dyn_closure")]
164                let modify = Box::new(modify);
165                rc_merge.value.apply_modify(modify)
166            };
167        })
168    }
169}
170
171impl<T: VarValue> Clone for ArcMergeVar<T> {
172    fn clone(&self) -> Self {
173        Self(self.0.clone())
174    }
175}
176
177impl<T: VarValue> Clone for WeakMergeVar<T> {
178    fn clone(&self) -> Self {
179        Self(self.0.clone())
180    }
181}
182
183impl<T: VarValue> crate::private::Sealed for ArcMergeVar<T> {}
184impl<T: VarValue> crate::private::Sealed for WeakMergeVar<T> {}
185
186impl<T: VarValue> AnyVar for ArcMergeVar<T> {
187    fn clone_any(&self) -> BoxedAnyVar {
188        Box::new(self.clone())
189    }
190
191    fn as_any(&self) -> &dyn Any {
192        self
193    }
194
195    fn as_unboxed_any(&self) -> &dyn Any {
196        self
197    }
198
199    fn double_boxed_any(self: Box<Self>) -> Box<dyn Any> {
200        let me: BoxedVar<T> = self;
201        Box::new(me)
202    }
203
204    fn var_type_id(&self) -> TypeId {
205        TypeId::of::<T>()
206    }
207
208    fn get_any(&self) -> Box<dyn AnyVarValue> {
209        Box::new(self.get())
210    }
211
212    fn with_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) {
213        self.with(|v| read(v))
214    }
215
216    fn with_new_any(&self, read: &mut dyn FnMut(&dyn AnyVarValue)) -> bool {
217        self.with_new(|v| read(v)).is_some()
218    }
219
220    fn set_any(&self, _: Box<dyn AnyVarValue>) -> Result<(), VarIsReadOnlyError> {
221        Err(VarIsReadOnlyError {
222            capabilities: self.capabilities(),
223        })
224    }
225
226    fn last_update(&self) -> VarUpdateId {
227        self.0.value.last_update()
228    }
229
230    fn is_contextual(&self) -> bool {
231        false // if inputs are contextual Self::new uses a ContextualizedVar wrapper.
232    }
233
234    fn capabilities(&self) -> VarCapability {
235        if self.0.m.lock().inputs.is_empty() {
236            VarCapability::empty()
237        } else {
238            VarCapability::NEW
239        }
240    }
241
242    fn hook_any(&self, pos_modify_action: Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync>) -> VarHandle {
243        self.0.value.push_hook(pos_modify_action)
244    }
245
246    fn hook_animation_stop(&self, handler: Box<dyn FnOnce() + Send>) -> Result<(), Box<dyn FnOnce() + Send>> {
247        self.0.value.push_animation_hook(handler)
248    }
249
250    fn strong_count(&self) -> usize {
251        Arc::strong_count(&self.0)
252    }
253
254    fn weak_count(&self) -> usize {
255        Arc::weak_count(&self.0)
256    }
257
258    fn actual_var_any(&self) -> BoxedAnyVar {
259        Box::new(self.clone())
260    }
261
262    fn downgrade_any(&self) -> BoxedAnyWeakVar {
263        Box::new(WeakMergeVar(Arc::downgrade(&self.0)))
264    }
265
266    fn is_animating(&self) -> bool {
267        self.0.value.is_animating()
268    }
269
270    fn modify_importance(&self) -> usize {
271        self.0.value.modify_importance()
272    }
273
274    fn var_ptr(&self) -> VarPtr {
275        VarPtr::new_arc(&self.0)
276    }
277
278    fn get_debug(&self) -> Txt {
279        self.with(var_debug)
280    }
281
282    fn update(&self) -> Result<(), VarIsReadOnlyError> {
283        Var::modify(self, var_update)
284    }
285
286    fn map_debug(&self) -> BoxedVar<Txt> {
287        Var::map(self, var_debug).boxed()
288    }
289}
290
291impl<T: VarValue> AnyWeakVar for WeakMergeVar<T> {
292    fn clone_any(&self) -> BoxedAnyWeakVar {
293        Box::new(self.clone())
294    }
295
296    fn strong_count(&self) -> usize {
297        self.0.strong_count()
298    }
299
300    fn weak_count(&self) -> usize {
301        self.0.weak_count()
302    }
303
304    fn upgrade_any(&self) -> Option<BoxedAnyVar> {
305        self.0.upgrade().map(|rc| Box::new(ArcMergeVar(rc)) as _)
306    }
307
308    fn as_any(&self) -> &dyn Any {
309        self
310    }
311}
312
313impl<T: VarValue> IntoVar<T> for ArcMergeVar<T> {
314    type Var = Self;
315
316    fn into_var(self) -> Self::Var {
317        self
318    }
319}
320
321impl<T: VarValue> Var<T> for ArcMergeVar<T> {
322    type ReadOnly = types::ReadOnlyVar<T, Self>;
323
324    type ActualVar = Self;
325
326    type Downgrade = WeakMergeVar<T>;
327
328    type Map<O: VarValue> = ReadOnlyArcVar<O>;
329    type MapBidi<O: VarValue> = ArcVar<O>;
330
331    type FlatMap<O: VarValue, V: Var<O>> = types::ArcFlatMapVar<O, V>;
332
333    type FilterMap<O: VarValue> = ReadOnlyArcVar<O>;
334    type FilterMapBidi<O: VarValue> = ArcVar<O>;
335
336    type MapRef<O: VarValue> = types::MapRef<T, O, Self>;
337    type MapRefBidi<O: VarValue> = types::MapRefBidi<T, O, Self>;
338
339    type Easing = ReadOnlyArcVar<T>;
340
341    fn with<R, F>(&self, read: F) -> R
342    where
343        F: FnOnce(&T) -> R,
344    {
345        self.0.value.with(read)
346    }
347
348    fn modify<F>(&self, _: F) -> Result<(), VarIsReadOnlyError>
349    where
350        F: FnOnce(&mut VarModify<T>) + 'static,
351    {
352        Err(VarIsReadOnlyError {
353            capabilities: self.capabilities(),
354        })
355    }
356
357    fn actual_var(self) -> Self {
358        self
359    }
360
361    fn downgrade(&self) -> WeakMergeVar<T> {
362        WeakMergeVar(Arc::downgrade(&self.0))
363    }
364
365    fn into_value(self) -> T {
366        match Arc::try_unwrap(self.0) {
367            Ok(data) => data.value.into_value(),
368            Err(rc) => Self(rc).get(),
369        }
370    }
371
372    fn read_only(&self) -> Self::ReadOnly {
373        types::ReadOnlyVar::new(self.clone())
374    }
375
376    fn map<O, M>(&self, map: M) -> Self::Map<O>
377    where
378        O: VarValue,
379        M: FnMut(&T) -> O + Send + 'static,
380    {
381        var_map(self, map)
382    }
383
384    fn map_bidi<O, M, B>(&self, map: M, map_back: B) -> Self::MapBidi<O>
385    where
386        O: VarValue,
387        M: FnMut(&T) -> O + Send + 'static,
388        B: FnMut(&O) -> T + Send + 'static,
389    {
390        var_map_bidi(self, map, map_back)
391    }
392
393    fn flat_map<O, V, M>(&self, map: M) -> Self::FlatMap<O, V>
394    where
395        O: VarValue,
396        V: Var<O>,
397        M: FnMut(&T) -> V + Send + 'static,
398    {
399        var_flat_map(self, map)
400    }
401
402    fn filter_map<O, M, I>(&self, map: M, fallback: I) -> Self::FilterMap<O>
403    where
404        O: VarValue,
405        M: FnMut(&T) -> Option<O> + Send + 'static,
406        I: Fn() -> O + Send + Sync + 'static,
407    {
408        var_filter_map(self, map, fallback)
409    }
410
411    fn filter_map_bidi<O, M, B, I>(&self, map: M, map_back: B, fallback: I) -> Self::FilterMapBidi<O>
412    where
413        O: VarValue,
414        M: FnMut(&T) -> Option<O> + Send + 'static,
415        B: FnMut(&O) -> Option<T> + Send + 'static,
416        I: Fn() -> O + Send + Sync + 'static,
417    {
418        var_filter_map_bidi(self, map, map_back, fallback)
419    }
420
421    fn map_ref<O, M>(&self, map: M) -> Self::MapRef<O>
422    where
423        O: VarValue,
424        M: Fn(&T) -> &O + Send + Sync + 'static,
425    {
426        var_map_ref(self, map)
427    }
428
429    fn map_ref_bidi<O, M, B>(&self, map: M, map_mut: B) -> Self::MapRefBidi<O>
430    where
431        O: VarValue,
432        M: Fn(&T) -> &O + Send + Sync + 'static,
433        B: Fn(&mut T) -> &mut O + Send + Sync + 'static,
434    {
435        var_map_ref_bidi(self, map, map_mut)
436    }
437
438    fn easing<F>(&self, duration: Duration, easing: F) -> Self::Easing
439    where
440        T: Transitionable,
441        F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
442    {
443        var_easing(self, duration, easing)
444    }
445
446    fn easing_with<F, S>(&self, duration: Duration, easing: F, sampler: S) -> Self::Easing
447    where
448        T: Transitionable,
449        F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
450        S: Fn(&animation::Transition<T>, EasingStep) -> T + Send + Sync + 'static,
451    {
452        var_easing_with(self, duration, easing, sampler)
453    }
454}
455
456impl<T: VarValue> WeakVar<T> for WeakMergeVar<T> {
457    type Upgrade = ArcMergeVar<T>;
458
459    fn upgrade(&self) -> Option<ArcMergeVar<T>> {
460        self.0.upgrade().map(|rc| ArcMergeVar(rc))
461    }
462}
463
464/// Build a merge-var from any number of input vars of the same type `I`.
465pub struct MergeVarBuilder<I: VarValue> {
466    inputs: Vec<Box<dyn AnyVar>>,
467    _type: PhantomData<fn() -> I>,
468}
469impl<I: VarValue> MergeVarBuilder<I> {
470    /// New empty.
471    pub fn new() -> Self {
472        Self {
473            inputs: vec![],
474            _type: PhantomData,
475        }
476    }
477
478    /// New with pre-allocated inputs.
479    pub fn with_capacity(capacity: usize) -> Self {
480        Self {
481            inputs: Vec::with_capacity(capacity),
482            _type: PhantomData,
483        }
484    }
485
486    /// Push an input.
487    pub fn push(&mut self, input: impl Var<I>) {
488        self.inputs.push(input.boxed_any())
489    }
490
491    /// Build the merge var.
492    pub fn build<O: VarValue>(self, mut merge: impl FnMut(MergeVarInputs<I>) -> O + Send + 'static) -> BoxedVar<O> {
493        ArcMergeVar::new(self.inputs.into_boxed_slice(), move |inputs| {
494            merge(MergeVarInputs {
495                inputs,
496                _type: PhantomData,
497            })
498        })
499    }
500}
501impl<I: VarValue> Default for MergeVarBuilder<I> {
502    fn default() -> Self {
503        Self::new()
504    }
505}
506
507/// Input arguments for the merge closure of [`MergeVarBuilder`] merge vars.
508pub struct MergeVarInputs<'a, I: VarValue> {
509    inputs: &'a [Box<dyn AnyVarValue>],
510    _type: PhantomData<&'a I>,
511}
512impl<I: VarValue> MergeVarInputs<'_, I> {
513    /// Number of inputs.
514    #[expect(clippy::len_without_is_empty)]
515    pub fn len(&self) -> usize {
516        self.inputs.len()
517    }
518
519    /// Iterate over the values.
520    pub fn iter(&self) -> impl ExactSizeIterator<Item = &I> + '_ {
521        (0..self.len()).map(move |i| &self[i])
522    }
523}
524impl<I: VarValue> ops::Index<usize> for MergeVarInputs<'_, I> {
525    type Output = I;
526
527    fn index(&self, index: usize) -> &Self::Output {
528        self.inputs[index].as_any().downcast_ref().unwrap()
529    }
530}