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 fmt::Write as _,
48 marker::PhantomData,
49 ops,
50 sync::{Arc, Weak},
51};
52
53use parking_lot::Mutex;
54use smallbox::{SmallBox, smallbox};
55use zng_clone_move::clmv;
56use zng_txt::Txt;
57#[doc(hidden)]
58pub use zng_var_proc_macros::merge_var as __merge_var;
59
60use crate::{
61 AnyVar, AnyVarValue, BoxAnyVarValue, ContextVar, DynAnyVar, DynWeakAnyVar, Response, ResponseVar, Var, VarHandle, VarImpl,
62 VarInstanceTag, VarValue, WeakVarImpl, any_contextual_var, any_var, contextual_var,
63};
64
65use super::VarCapability;
66
67#[doc(hidden)]
68pub fn merge_var_input<I: VarValue>(input: impl MergeInput<I>) -> AnyVar {
69 input.into_merge_input().into()
70}
71
72#[doc(hidden)]
73pub fn merge_var_with(var: &AnyVar, visitor: &mut dyn FnMut(&dyn AnyVarValue)) {
74 var.0.with(visitor);
75}
76
77#[doc(hidden)]
78pub fn merge_var_output<O: VarValue>(output: O) -> BoxAnyVarValue {
79 BoxAnyVarValue::new(output)
80}
81
82#[doc(hidden)]
83pub fn merge_var<O: VarValue>(inputs: Box<[AnyVar]>, merge: impl FnMut(&[AnyVar]) -> BoxAnyVarValue + Send + 'static) -> Var<O> {
84 Var::new_any(var_merge_impl(inputs, smallbox!(merge), TypeId::of::<O>()))
85}
86
87#[doc(hidden)]
88#[diagnostic::on_unimplemented(note = "merge_var! and expr_var! inputs can be: Var<T>, ContextVar<T> or ResponseVar<T>")]
89pub trait MergeInput<T: VarValue> {
90 fn into_merge_input(self) -> Var<T>;
91}
92impl<T: VarValue> MergeInput<T> for Var<T> {
93 fn into_merge_input(self) -> Var<T> {
94 self
95 }
96}
97impl<T: VarValue> MergeInput<T> for ContextVar<T> {
98 fn into_merge_input(self) -> Var<T> {
99 self.into()
100 }
101}
102impl<T: VarValue> MergeInput<Response<T>> for ResponseVar<T> {
103 fn into_merge_input(self) -> Var<Response<T>> {
104 self.into()
105 }
106}
107
108fn var_merge_impl(inputs: Box<[AnyVar]>, merge: MergeFn, value_type: TypeId) -> AnyVar {
109 if inputs.iter().any(|i| i.capabilities().is_contextual()) {
110 let merge = Arc::new(Mutex::new(merge));
111 return any_contextual_var(
112 move || {
113 let mut inputs = inputs.clone();
114 for v in inputs.iter_mut() {
115 if v.capabilities().is_contextual() {
116 *v = v.current_context();
117 }
118 }
119 var_merge_tail(inputs, smallbox!(clmv!(merge, |inputs: &[AnyVar]| { merge.lock()(inputs) })))
120 },
121 value_type,
122 );
123 }
124 var_merge_tail(inputs, merge)
125}
126fn var_merge_tail(inputs: Box<[AnyVar]>, mut merge: MergeFn) -> AnyVar {
127 let output = any_var(merge(&inputs));
128 let data = Arc::new(MergeVarData {
129 inputs,
130 merge: Mutex::new((merge, 0)),
131 output,
132 });
133
134 for input in &data.inputs {
135 let weak = Arc::downgrade(&data);
136 input
137 .hook(move |_| {
138 if let Some(data) = weak.upgrade() {
139 let modify_id = data.merge.lock().1;
144 data.output.modify(clmv!(weak, |output| {
145 if let Some(data) = weak.upgrade() {
146 let mut m = data.merge.lock();
147 if m.1 != modify_id {
148 return;
150 }
151
152 let new_value = m.0(&data.inputs);
153 output.set(new_value);
154 }
155 }));
156 true
157 } else {
158 false
159 }
160 })
161 .perm();
162 }
163
164 AnyVar(DynAnyVar::Merge(MergeVar(data)))
165}
166
167type MergeFn = SmallBox<dyn FnMut(&[AnyVar]) -> BoxAnyVarValue + Send + 'static, smallbox::space::S4>;
168
169struct MergeVarData {
170 inputs: Box<[AnyVar]>,
171 merge: Mutex<(MergeFn, usize)>,
172 output: AnyVar,
173}
174
175pub(crate) struct MergeVar(Arc<MergeVarData>);
176impl fmt::Debug for MergeVar {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 let mut b = f.debug_struct("MergeVar");
179 b.field("var_instance_tag()", &self.var_instance_tag());
180 b.field("inputs", &self.0.inputs);
181 b.field("output", &self.0.output);
182 b.finish()
183 }
184}
185impl VarImpl for MergeVar {
186 fn clone_dyn(&self) -> DynAnyVar {
187 DynAnyVar::Merge(MergeVar(self.0.clone()))
188 }
189
190 fn value_type(&self) -> std::any::TypeId {
191 self.0.output.0.value_type()
192 }
193
194 #[cfg(feature = "type_names")]
195 fn value_type_name(&self) -> &'static str {
196 self.0.output.0.value_type_name()
197 }
198
199 fn strong_count(&self) -> usize {
200 Arc::strong_count(&self.0)
201 }
202
203 fn var_eq(&self, other: &DynAnyVar) -> bool {
204 match other {
205 DynAnyVar::Merge(o) => Arc::ptr_eq(&self.0, &o.0),
206 _ => false,
207 }
208 }
209
210 fn var_instance_tag(&self) -> VarInstanceTag {
211 VarInstanceTag(Arc::as_ptr(&self.0) as _)
212 }
213
214 fn downgrade(&self) -> DynWeakAnyVar {
215 DynWeakAnyVar::Merge(WeakMergeVar(Arc::downgrade(&self.0)))
216 }
217
218 fn capabilities(&self) -> VarCapability {
219 self.0.output.0.capabilities().as_always_read_only()
220 }
221
222 fn with(&self, visitor: &mut dyn FnMut(&dyn AnyVarValue)) {
223 self.0.output.0.with(visitor);
224 }
225
226 fn get(&self) -> BoxAnyVarValue {
227 self.0.output.0.get()
228 }
229
230 fn set(&self, _: BoxAnyVarValue) -> bool {
231 false
232 }
233
234 fn update(&self) -> bool {
235 false
236 }
237
238 fn modify(&self, _: SmallBox<dyn FnMut(&mut super::AnyVarModify) + Send + 'static, smallbox::space::S4>) -> bool {
239 false
240 }
241
242 fn hook(&self, on_new: SmallBox<dyn FnMut(&crate::AnyVarHookArgs) -> bool + Send + 'static, smallbox::space::S4>) -> super::VarHandle {
243 self.0.output.0.hook(on_new)
244 }
245
246 fn last_update(&self) -> crate::VarUpdateId {
247 self.0.output.0.last_update()
248 }
249
250 fn modify_info(&self) -> crate::animation::ModifyInfo {
251 self.0.output.0.modify_info()
252 }
253
254 fn modify_importance(&self) -> usize {
255 self.0.output.0.modify_importance()
256 }
257
258 fn is_animating(&self) -> bool {
259 self.0.output.0.is_animating()
260 }
261
262 fn hook_animation_stop(&self, handler: crate::animation::AnimationStopFn) -> VarHandle {
263 self.0.output.0.hook_animation_stop(handler)
264 }
265
266 fn current_context(&self) -> DynAnyVar {
267 self.clone_dyn()
268 }
269}
270
271pub(crate) struct WeakMergeVar(Weak<MergeVarData>);
272impl fmt::Debug for WeakMergeVar {
273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274 f.debug_tuple("WeakMergeVar").field(&self.0.as_ptr()).finish()
275 }
276}
277impl WeakVarImpl for WeakMergeVar {
278 fn clone_dyn(&self) -> DynWeakAnyVar {
279 DynWeakAnyVar::Merge(WeakMergeVar(self.0.clone()))
280 }
281
282 fn strong_count(&self) -> usize {
283 self.0.strong_count()
284 }
285
286 fn upgrade(&self) -> Option<DynAnyVar> {
287 Some(DynAnyVar::Merge(MergeVar(self.0.upgrade()?)))
288 }
289
290 fn var_eq(&self, other: &DynWeakAnyVar) -> bool {
291 match other {
292 DynWeakAnyVar::Merge(o) => self.0.ptr_eq(&o.0),
293 _ => false,
294 }
295 }
296}
297
298#[derive(Clone)]
300pub struct MergeVarBuilder<I: VarValue> {
301 inputs: Vec<AnyVar>,
302 _type: PhantomData<fn() -> I>,
303}
304impl<I: VarValue> MergeVarBuilder<I> {
305 pub fn new() -> Self {
307 Self {
308 inputs: vec![],
309 _type: PhantomData,
310 }
311 }
312
313 pub fn with_capacity(capacity: usize) -> Self {
315 Self {
316 inputs: Vec::with_capacity(capacity),
317 _type: PhantomData,
318 }
319 }
320
321 pub fn push(&mut self, input: impl MergeInput<I>) {
323 self.inputs.push(input.into_merge_input().into())
324 }
325
326 pub fn build<O: VarValue>(self, merge: impl FnMut(VarMergeInputs<I>) -> O + Send + 'static) -> Var<O> {
328 if self.inputs.iter().any(|i| i.capabilities().is_contextual()) {
329 let merge = Arc::new(Mutex::new(merge));
330 return contextual_var(move || {
331 let builder = MergeVarBuilder {
332 inputs: self.inputs.iter().map(|v| v.current_context()).collect(),
333 _type: PhantomData,
334 };
335 builder.build_tail(clmv!(merge, |inputs| merge.lock()(inputs)))
336 });
337 }
338 self.build_tail(merge)
339 }
340 fn build_tail<O: VarValue>(self, mut merge: impl FnMut(VarMergeInputs<I>) -> O + Send + 'static) -> Var<O> {
341 let any = var_merge_impl(
342 self.inputs.into_boxed_slice(),
343 smallbox!(move |vars: &[AnyVar]| {
344 let values: Box<[BoxAnyVarValue]> = vars.iter().map(|v| v.get()).collect();
345 let out = merge(VarMergeInputs {
346 inputs: &values[..],
347 _type: PhantomData,
348 });
349 BoxAnyVarValue::new(out)
350 }),
351 TypeId::of::<O>(),
352 );
353 Var::new_any(any)
354 }
355}
356impl<I: VarValue> Default for MergeVarBuilder<I> {
357 fn default() -> Self {
358 Self::new()
359 }
360}
361impl<T: VarValue + AsRef<str>> MergeVarBuilder<T> {
362 pub fn join_txt(self, separator: impl Into<Txt>) -> Var<Txt> {
364 self.join_txt_impl(separator.into())
365 }
366 fn join_txt_impl(self, separator: Txt) -> Var<Txt> {
367 self.build(move |t| {
368 let mut s = String::new();
369 let mut sep = "";
370 for t in t.iter() {
371 write!(&mut s, "{sep}{}", t.as_ref()).unwrap();
372 sep = &separator;
373 }
374 s.into()
375 })
376 }
377}
378
379pub struct VarMergeInputs<'a, I: VarValue> {
381 inputs: &'a [BoxAnyVarValue],
382 _type: PhantomData<&'a I>,
383}
384impl<I: VarValue> VarMergeInputs<'_, I> {
385 #[expect(clippy::len_without_is_empty)]
387 pub fn len(&self) -> usize {
388 self.inputs.len()
389 }
390
391 pub fn iter(&self) -> impl ExactSizeIterator<Item = &I> + '_ {
393 (0..self.len()).map(move |i| &self[i])
394 }
395}
396impl<I: VarValue> ops::Index<usize> for VarMergeInputs<'_, I> {
397 type Output = I;
398
399 fn index(&self, index: usize) -> &Self::Output {
400 self.inputs[index].downcast_ref().unwrap()
401 }
402}