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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#[macro_export]
macro_rules! specialize_call {
    (@single_mapping,
        ( $($acc_p:pat),* ),
        ( $($acc_t:ty),* ),
        $func:tt, $args:tt, $select:tt,
        [ ($p:pat => $($t:ty),+ ) $(, $p_t:tt)* $(,)* ],
        $mappings:tt
    ) => {
        specialize_call!(@specialize_call,
            ( $($acc_p,)* $p ),
            ( $($acc_t,)* $($t),+ ),

            $func, $args, $select,
            $mappings
        );

        specialize_call!(@single_mapping,
            ( $($acc_p),* ),
            ( $($acc_t),* ),
            $func, $args, $select,
            [ $($p_t),* ],
            $mappings
        );
    };
    (@single_mapping,
        $acc_p:tt, $acc_t: tt,
        $func:tt, $args:tt, $select:tt,
        [],
        $mappings:tt
    ) => {};

    (@specialize_call,
        $acc_p:tt,
        $acc_t:tt,
        $func:tt,
        $args:tt,
        $select:tt,

        ($head:tt $(, $tail:tt)*)
    ) => {
        specialize_call!(@single_mapping,
            $acc_p, $acc_t,
            $func, $args, $select,
            $head,
            ( $($tail),* )
        );
    };
    (@specialize_call,
        $acc_p:tt,
        $acc_t:tt,
        $func:tt,
        $args:tt,
        $select:tt,

        ()
    ) => {
        specialize_call!(@maybe_invoke,
            $acc_p,
            $acc_t,
            $select,
            $func,
            $args
        );
    };

    (@maybe_invoke,
        ( $($acc_p:pat),* ),
        ( $($acc_t:ty),* ),
        $select:expr,
        $func:ident,
        ($($arg:expr),*)
    ) => {
        #[allow(unused_parens)]
        if matches!($select, ( $($acc_p),* )) { break Some($func::<$($acc_t),*>( $($arg),* )); }
    };

    ($func:ident, ($($arg:expr),* $(,)*), $select:expr, $($mapping:tt),+ $(,)*) => {
        loop {
            specialize_call!(@specialize_call,
                (), (),
                $func,
                ($($arg),*),
                $select,
                ( $($mapping),* )
            );
            break None
        }
    };
}