1#[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 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 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#[derive(Clone)]
291pub struct MergeVarBuilder<I: VarValue> {
292 inputs: Vec<AnyVar>,
293 _type: PhantomData<fn() -> I>,
294}
295impl<I: VarValue> MergeVarBuilder<I> {
296 pub fn new() -> Self {
298 Self {
299 inputs: vec![],
300 _type: PhantomData,
301 }
302 }
303
304 pub fn with_capacity(capacity: usize) -> Self {
306 Self {
307 inputs: Vec::with_capacity(capacity),
308 _type: PhantomData,
309 }
310 }
311
312 pub fn push(&mut self, input: impl MergeInput<I>) {
314 self.inputs.push(input.into_merge_input().into())
315 }
316
317 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
353pub struct VarMergeInputs<'a, I: VarValue> {
355 inputs: &'a [BoxAnyVarValue],
356 _type: PhantomData<&'a I>,
357}
358impl<I: VarValue> VarMergeInputs<'_, I> {
359 #[expect(clippy::len_without_is_empty)]
361 pub fn len(&self) -> usize {
362 self.inputs.len()
363 }
364
365 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}