static_cond/
lib.rs

1#![no_std]
2
3/// Evaluates a conditional during macro expansion as expression.
4///
5/// Currently limited to equality comparison. Can compare any two token trees. Can be nested.
6///
7/// This macro only works in expression-position, see [static_cond_item] for
8/// a macro that works in item-position.
9///
10/// Examples
11/// =======
12///
13/// ```
14/// # #[macro_use] extern crate static_cond;
15/// # fn main() {
16/// let x = static_cond!(if (+ 1 [2 3]) == (+ 1 [2 3]) {
17///     static_cond!(if black != white {
18///         "ok"
19///     } else {
20///         the compiler will never even try to interpret this
21///     })
22/// } else {
23///     blah blah blah blah blah unreachable
24/// });
25/// assert_eq!(x, "ok");
26/// # }
27/// ```
28///
29/// The actual conditional and the code provided for the branches not followed is eliminated after
30/// macro expansion (check `rustc --pretty=expanded`).
31#[macro_export]
32macro_rules! static_cond {
33    // private rule to define and call the local macro
34    // we evaluate a conditional by generating a new macro (in an inner scope, so name shadowing is
35    // not a big concern) and calling it
36    (@expr $lhs:tt $rhs:tt $arm1:tt $arm2:tt) => {{
37        // note that the inner macro has no captures (it can't, because there's no way to escape `$`)
38        macro_rules! __static_cond {
39            ($lhs $lhs) => $arm1;
40            ($lhs $rhs) => $arm2;
41        }
42
43        __static_cond!($lhs $rhs)
44    }};
45    (@item $lhs:tt $rhs:tt $arm1:tt $arm2:tt) => {
46        // note that the inner macro has no captures (it can't, because there's no way to escape `$`)
47        macro_rules! __static_cond {
48            ($lhs $lhs) => $arm1;
49            ($lhs $rhs) => $arm2;
50        }
51
52        __static_cond! { $lhs $rhs }
53    };
54
55    // no else condition provided: fall through with empty else
56    (if $lhs:tt == $rhs:tt $then:tt) => {
57        $crate::static_cond!(if $lhs == $rhs $then else { })
58    };
59    (if $lhs:tt != $rhs:tt $then:tt) => {
60        $crate::static_cond!(if $lhs != $rhs $then else { })
61    };
62
63    // main entry point with then and else arms
64    (if $lhs:tt == $rhs:tt $then:tt else $els:tt) => {
65        $crate::static_cond!(@expr $lhs $rhs $then $els)
66	};
67    (if $lhs:tt != $rhs:tt $then:tt else $els:tt) => {
68        $crate::static_cond!(@expr $lhs $rhs $els $then)
69    };
70}
71
72/// Evaluates a conditional during macro expansion as item.
73///
74/// Currently limited to equality comparison. Can compare any two token trees. Can be nested.
75///
76/// This macro only works in item-position, see [static_cond] for
77/// a macro that works in expression-position.
78///
79/// Examples
80/// =======
81///
82/// ```
83/// # #[macro_use] extern crate static_cond;
84/// # fn main() {
85/// static_cond_item!{
86///     if (+ 1 [2 3]) == (+ 1 [2 3]) {
87///         static_cond_item!{if black != white {
88///             fn foo() -> &'static str {"ok"}
89///         } else {
90///             the compiler will never even try to interpret this
91///         }}
92///     } else {
93///         blah blah blah blah blah unreachable
94///     }
95/// }
96/// assert_eq!(foo(), "ok");
97/// # }
98/// ```
99///
100/// The actual conditional and the code provided for the branches not followed is eliminated after
101/// macro expansion (check `rustc --pretty=expanded`).
102#[macro_export]
103macro_rules! static_cond_item {
104    // no else condition provided: fall through with empty else
105    (if $lhs:tt == $rhs:tt $then:tt) => {
106        $crate::static_cond_item! { if $lhs == $rhs $then else { } }
107    };
108    (if $lhs:tt != $rhs:tt $then:tt) => {
109        $crate::static_cond_item! { if $lhs != $rhs $then else { } }
110    };
111
112    // main entry point with then and else arms
113    (if $lhs:tt == $rhs:tt $then:tt else $els:tt) => {
114        $crate::static_cond! { @item $lhs $rhs $then $els }
115    };
116    (if $lhs:tt != $rhs:tt $then:tt else $els:tt) => {
117        $crate::static_cond! { @item $lhs $rhs $els $then }
118    };
119}