zng_var/var_impl/
merge_var.rs

1//! Variable that merges multiple others.
2
3///<span data-del-macro-root></span> Initializes a new [`Var<T>`](crate::Var) with value made
4/// by merging multiple other variables.
5///
6/// # Arguments
7///
8/// All arguments are separated by comma like a function call.
9///
10/// * `var0..N`: A list of *vars*, minimal 2.
11/// * `merge`: A new closure that produces a new value from references to all variable values. `FnMut(&var0_T, ..) -> merge_T`
12///
13/// Note that the *vars* can be of any of `Var<T>`, `ContextVar<T>` or `ResponseVar<T>`, that is, already constructed
14/// var types, not all types that convert into var.
15///
16/// # Contextualized
17///
18/// The merge var is contextualized when needed, meaning if any input is [contextual] at the moment the var is created it
19/// is also contextual. The full output type of this macro is a `Var<O>`, the `O` type is defined by the output of the merge closure.
20///
21/// [contextual]: crate::VarCapability::CONTEXT
22///
23/// # Examples
24///
25/// ```
26/// # use zng_var::*;
27/// # use zng_txt::*;
28/// # macro_rules! Text { ($($tt:tt)*) => { () } }
29/// let var0: Var<Txt> = var_from("Hello");
30/// let var1: Var<Txt> = var_from("World");
31///
32/// let greeting_text = Text!(merge_var!(var0, var1, |a, b| formatx!("{a} {b}!")));
33/// ```
34#[macro_export]
35macro_rules! merge_var {
36    ($($tt:tt)+) => {
37        $crate::__merge_var! {
38            $crate,
39            $($tt)+
40        }
41    };
42}
43
44use core::fmt;
45use std::{
46    any::TypeId,
47    marker::PhantomData,
48    ops,
49    sync::{Arc, Weak},
50};
51
52use parking_lot::Mutex;
53use smallbox::{SmallBox, smallbox};
54use zng_clone_move::clmv;
55#[doc(hidden)]
56pub use zng_var_proc_macros::merge_var as __merge_var;
57
58use crate::{
59    AnyVar, AnyVarValue, BoxAnyVarValue, ContextVar, DynAnyVar, DynWeakAnyVar, Response, ResponseVar, Var, VarHandle, VarImpl,
60    VarInstanceTag, VarValue, WeakVarImpl, any_contextual_var, any_var, contextual_var,
61};
62
63use super::VarCapability;
64
65#[doc(hidden)]
66pub fn merge_var_input<I: VarValue>(input: impl MergeInput<I>) -> AnyVar {
67    input.into_merge_input().into()
68}
69
70#[doc(hidden)]
71pub fn merge_var_with(var: &AnyVar, visitor: &mut dyn FnMut(&dyn AnyVarValue)) {
72    var.0.with(visitor);
73}
74
75#[doc(hidden)]
76pub fn merge_var_output<O: VarValue>(output: O) -> BoxAnyVarValue {
77    BoxAnyVarValue::new(output)
78}
79
80#[doc(hidden)]
81pub fn merge_var<O: VarValue>(inputs: Box<[AnyVar]>, merge: impl FnMut(&[AnyVar]) -> BoxAnyVarValue + Send + 'static) -> Var<O> {
82    Var::new_any(var_merge_impl(inputs, smallbox!(merge), TypeId::of::<O>()))
83}
84
85#[doc(hidden)]
86#[diagnostic::on_unimplemented(note = "merge_var! and expr_var! inputs can be: Var<T>, ContextVar<T> or ResponseVar<T>")]
87pub trait MergeInput<T: VarValue> {
88    fn into_merge_input(self) -> Var<T>;
89}
90impl<T: VarValue> MergeInput<T> for Var<T> {
91    fn into_merge_input(self) -> Var<T> {
92        self
93    }
94}
95impl<T: VarValue> MergeInput<T> for ContextVar<T> {
96    fn into_merge_input(self) -> Var<T> {
97        self.into()
98    }
99}
100impl<T: VarValue> MergeInput<Response<T>> for ResponseVar<T> {
101    fn into_merge_input(self) -> Var<Response<T>> {
102        self.into()
103    }
104}
105
106fn var_merge_impl(inputs: Box<[AnyVar]>, merge: MergeFn, value_type: TypeId) -> AnyVar {
107    if inputs.iter().any(|i| i.capabilities().is_contextual()) {
108        let merge = Arc::new(Mutex::new(merge));
109        return any_contextual_var(
110            move || {
111                let mut inputs = inputs.clone();
112                for v in inputs.iter_mut() {
113                    if v.capabilities().is_contextual() {
114                        *v = v.current_context();
115                    }
116                }
117                var_merge_tail(inputs, smallbox!(clmv!(merge, |inputs: &[AnyVar]| { merge.lock()(inputs) })))
118            },
119            value_type,
120        );
121    }
122    var_merge_tail(inputs, merge)
123}
124fn var_merge_tail(inputs: Box<[AnyVar]>, mut merge: MergeFn) -> AnyVar {
125    let output = any_var(merge(&inputs));
126    let data = Arc::new(MergeVarData {
127        inputs,
128        merge: Mutex::new((merge, 0)),
129        output,
130    });
131
132    for input in &data.inputs {
133        let weak = Arc::downgrade(&data);
134        input
135            .hook(move |_| {
136                if let Some(data) = weak.upgrade() {
137                    // Multiple inputs can update on the same cycle,
138                    // to avoid running merge multiple times schedule an output modify
139                    // so it runs after the current burst on the same cycle, and use
140                    // this counter to skip subsequent modify requests on the same cycle
141                    let modify_id = data.merge.lock().1;
142                    data.output.modify(clmv!(weak, |output| {
143                        if let Some(data) = weak.upgrade() {
144                            let mut m = data.merge.lock();
145                            if m.1 != modify_id {
146                                // already applied
147                                return;
148                            }
149
150                            let new_value = m.0(&data.inputs);
151                            output.set(new_value);
152                        }
153                    }));
154                    true
155                } else {
156                    false
157                }
158            })
159            .perm();
160    }
161
162    AnyVar(DynAnyVar::Merge(MergeVar(data)))
163}
164
165type MergeFn = SmallBox<dyn FnMut(&[AnyVar]) -> BoxAnyVarValue + Send + 'static, smallbox::space::S4>;
166
167struct MergeVarData {
168    inputs: Box<[AnyVar]>,
169    merge: Mutex<(MergeFn, usize)>,
170    output: AnyVar,
171}
172
173pub(crate) struct MergeVar(Arc<MergeVarData>);
174impl fmt::Debug for MergeVar {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        let mut b = f.debug_struct("MergeVar");
177        b.field("var_instance_tag()", &self.var_instance_tag());
178        b.field("inputs", &self.0.inputs);
179        b.field("output", &self.0.output);
180        b.finish()
181    }
182}
183impl VarImpl for MergeVar {
184    fn clone_dyn(&self) -> DynAnyVar {
185        DynAnyVar::Merge(MergeVar(self.0.clone()))
186    }
187
188    fn value_type(&self) -> std::any::TypeId {
189        self.0.output.0.value_type()
190    }
191
192    #[cfg(feature = "type_names")]
193    fn value_type_name(&self) -> &'static str {
194        self.0.output.0.value_type_name()
195    }
196
197    fn strong_count(&self) -> usize {
198        Arc::strong_count(&self.0)
199    }
200
201    fn var_eq(&self, other: &DynAnyVar) -> bool {
202        match other {
203            DynAnyVar::Merge(o) => Arc::ptr_eq(&self.0, &o.0),
204            _ => false,
205        }
206    }
207
208    fn var_instance_tag(&self) -> VarInstanceTag {
209        VarInstanceTag(Arc::as_ptr(&self.0) as _)
210    }
211
212    fn downgrade(&self) -> DynWeakAnyVar {
213        DynWeakAnyVar::Merge(WeakMergeVar(Arc::downgrade(&self.0)))
214    }
215
216    fn capabilities(&self) -> VarCapability {
217        self.0.output.0.capabilities().as_always_read_only()
218    }
219
220    fn with(&self, visitor: &mut dyn FnMut(&dyn AnyVarValue)) {
221        self.0.output.0.with(visitor);
222    }
223
224    fn get(&self) -> BoxAnyVarValue {
225        self.0.output.0.get()
226    }
227
228    fn set(&self, _: BoxAnyVarValue) -> bool {
229        false
230    }
231
232    fn update(&self) -> bool {
233        false
234    }
235
236    fn modify(&self, _: SmallBox<dyn FnMut(&mut super::AnyVarModify) + Send + 'static, smallbox::space::S4>) -> bool {
237        false
238    }
239
240    fn hook(&self, on_new: SmallBox<dyn FnMut(&crate::AnyVarHookArgs) -> bool + Send + 'static, smallbox::space::S4>) -> super::VarHandle {
241        self.0.output.0.hook(on_new)
242    }
243
244    fn last_update(&self) -> crate::VarUpdateId {
245        self.0.output.0.last_update()
246    }
247
248    fn modify_info(&self) -> crate::animation::ModifyInfo {
249        self.0.output.0.modify_info()
250    }
251
252    fn modify_importance(&self) -> usize {
253        self.0.output.0.modify_importance()
254    }
255
256    fn is_animating(&self) -> bool {
257        self.0.output.0.is_animating()
258    }
259
260    fn hook_animation_stop(&self, handler: crate::animation::AnimationStopFn) -> VarHandle {
261        self.0.output.0.hook_animation_stop(handler)
262    }
263
264    fn current_context(&self) -> DynAnyVar {
265        self.clone_dyn()
266    }
267}
268
269pub(crate) struct WeakMergeVar(Weak<MergeVarData>);
270impl fmt::Debug for WeakMergeVar {
271    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272        f.debug_tuple("WeakMergeVar").field(&self.0.as_ptr()).finish()
273    }
274}
275impl WeakVarImpl for WeakMergeVar {
276    fn clone_dyn(&self) -> DynWeakAnyVar {
277        DynWeakAnyVar::Merge(WeakMergeVar(self.0.clone()))
278    }
279
280    fn strong_count(&self) -> usize {
281        self.0.strong_count()
282    }
283
284    fn upgrade(&self) -> Option<DynAnyVar> {
285        Some(DynAnyVar::Merge(MergeVar(self.0.upgrade()?)))
286    }
287}
288
289/// Build a [`merge_var!`] from any number of input vars of the same type `I`.
290#[derive(Clone)]
291pub struct MergeVarBuilder<I: VarValue> {
292    inputs: Vec<AnyVar>,
293    _type: PhantomData<fn() -> I>,
294}
295impl<I: VarValue> MergeVarBuilder<I> {
296    /// New empty.
297    pub fn new() -> Self {
298        Self {
299            inputs: vec![],
300            _type: PhantomData,
301        }
302    }
303
304    /// New with pre-allocated inputs.
305    pub fn with_capacity(capacity: usize) -> Self {
306        Self {
307            inputs: Vec::with_capacity(capacity),
308            _type: PhantomData,
309        }
310    }
311
312    /// Push an input.
313    pub fn push(&mut self, input: impl MergeInput<I>) {
314        self.inputs.push(input.into_merge_input().into())
315    }
316
317    /// Build the merge var.
318    pub fn build<O: VarValue>(self, merge: impl FnMut(VarMergeInputs<I>) -> O + Send + 'static) -> Var<O> {
319        if self.inputs.iter().any(|i| i.capabilities().is_contextual()) {
320            let merge = Arc::new(Mutex::new(merge));
321            return contextual_var(move || {
322                let builder = MergeVarBuilder {
323                    inputs: self.inputs.iter().map(|v| v.current_context()).collect(),
324                    _type: PhantomData,
325                };
326                builder.build_tail(clmv!(merge, |inputs| merge.lock()(inputs)))
327            });
328        }
329        self.build_tail(merge)
330    }
331    fn build_tail<O: VarValue>(self, mut merge: impl FnMut(VarMergeInputs<I>) -> O + Send + 'static) -> Var<O> {
332        let any = var_merge_impl(
333            self.inputs.into_boxed_slice(),
334            smallbox!(move |vars: &[AnyVar]| {
335                let values: Box<[BoxAnyVarValue]> = vars.iter().map(|v| v.get()).collect();
336                let out = merge(VarMergeInputs {
337                    inputs: &values[..],
338                    _type: PhantomData,
339                });
340                BoxAnyVarValue::new(out)
341            }),
342            TypeId::of::<O>(),
343        );
344        Var::new_any(any)
345    }
346}
347impl<I: VarValue> Default for MergeVarBuilder<I> {
348    fn default() -> Self {
349        Self::new()
350    }
351}
352
353/// Input arguments for the merge closure of [`MergeVarBuilder`] merge vars.
354pub struct VarMergeInputs<'a, I: VarValue> {
355    inputs: &'a [BoxAnyVarValue],
356    _type: PhantomData<&'a I>,
357}
358impl<I: VarValue> VarMergeInputs<'_, I> {
359    /// Number of inputs.
360    #[expect(clippy::len_without_is_empty)]
361    pub fn len(&self) -> usize {
362        self.inputs.len()
363    }
364
365    /// Iterate over the values.
366    pub fn iter(&self) -> impl ExactSizeIterator<Item = &I> + '_ {
367        (0..self.len()).map(move |i| &self[i])
368    }
369}
370impl<I: VarValue> ops::Index<usize> for VarMergeInputs<'_, I> {
371    type Output = I;
372
373    fn index(&self, index: usize) -> &Self::Output {
374        self.inputs[index].downcast_ref().unwrap()
375    }
376}