zng_var_proc_macros/
merge_var.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use syn::{
    parse::{Parse, ParseStream},
    parse_macro_input,
    punctuated::Punctuated,
    Expr, Path, Token,
};

pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let Input { vars_mod, mut inputs, .. } = parse_macro_input!(input as Input);

    if inputs.len() < 3 {
        abort_call_site!("expected at least two input vars and one merge closure")
    }

    let merge = inputs.pop().unwrap();

    let type_idents: Vec<_> = (0..inputs.len()).map(|i| ident!("T{i}")).collect();
    let var_idents: Vec<_> = (0..inputs.len()).map(|i| ident!("V{i}")).collect();
    let input_idents: Vec<_> = (0..inputs.len()).map(|i| ident!("var{i}")).collect();
    let idx: Vec<_> = (0..inputs.len())
        .map(|i| syn::Index {
            index: i as _,
            span: proc_macro2::Span::call_site(),
        })
        .collect();

    let out = quote! {
        {
            fn merge_var__<
                #(#type_idents: #vars_mod::VarValue,)*
                #(#var_idents: #vars_mod::Var<#type_idents>,)*
                O: #vars_mod::VarValue,
                F: FnMut(
                    #(&#type_idents,)*
                ) -> O + Send + 'static
            >(
                #(#input_idents: #var_idents,)*
                mut merge: F
            ) -> #vars_mod::BoxedVar<O> {
                let input_types = (
                    #(#vars_mod::types::ArcMergeVarInput::new(&#input_idents)),*
                );
                #vars_mod::types::ArcMergeVar::new(
                    Box::new([
                        #(Box::new(#input_idents),)*
                    ]),
                    move |inputs| {
                        merge(
                            #(input_types.#idx.get(&inputs[#idx])),*
                        )
                    }
                )
            }
            merge_var__(#inputs #merge)
        }
    };

    out.into()
}

struct Input {
    vars_mod: Path,
    _comma: Token![,],
    inputs: Punctuated<Expr, Token![,]>,
}
impl Parse for Input {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(Input {
            vars_mod: input.parse()?,
            _comma: input.parse()?,
            inputs: Punctuated::parse_terminated(input)?,
        })
    }
}