devela/code/util/
is.rs

1// devela::code::util::is
2//
3//! inline if macro.
4//
5
6/// Conditional evaluation.
7///
8/// Combines:
9/// 1. `if`/`else` conditions
10/// 2. `if let` pattern matching
11/// 3. Temporary value binding
12///
13/// # Examples
14///
15/// 1. Replacing `if`:
16/// ```
17/// # use devela::is;
18/// is![true; print!("true")];
19///
20/// // This
21/// let s = is![1 > 0; true; false];
22///
23/// // Would be equivalent to
24/// let s = if 1 > 0 {
25///     true
26/// } else {
27///     false
28/// };
29/// ```
30///
31/// 2. Replacing `if let`:
32/// ```
33/// # use devela::is;
34/// let num = Some(123);
35///
36/// // This
37/// is![let Some(n) = num ; println!("num:{n}") ; { dbg![num]; }];
38///
39/// // Would be equivalent to
40/// if let Some(n) = num {
41///     println!("num:{n}")
42/// } else {
43///     dbg![num];
44/// }
45/// ```
46///
47/// Nested:
48/// ```
49/// # use devela::is;
50/// let mut s = String::new();
51/// let is_premium = Some(true);
52///
53/// // This
54/// is![let Some(b) = is_premium; is![b; s += " [premium]"]];
55///
56/// // Would be equivalent to
57/// if let Some(b) = is_premium {
58///     if b {
59///         s += " [premium]";
60///     }
61/// }
62/// ```
63///
64/// 3. Temporary value binding:
65/// ```
66/// # use devela::{format_args, is, FmtWrite};
67/// let mut s = String::new();
68/// let (a, b) = (1, 2);
69///
70/// // This
71/// is![
72///     tmp A = format_args!("A{a}");
73///     tmp B = format_args!("B{b}");
74///     write!(s, "{A}+{B},");
75/// ];
76/// assert_eq![&s, "A1+B2,"];
77///
78/// // Would be equivalent to
79/// match format_args!("A{a}") {
80///     A => match format_args!("B{b}") {
81///         B => { write!(s, "{A}+{B},"); }
82///     }
83/// }
84/// ```
85///
86/// Otherwise it fails with `E0716: temporary value dropped while borrowed` (in stable):
87// WAIT:1.89 [Allow storing format_args! in variable](https://github.com/rust-lang/rust/pull/140748)
88// ```compile_fail,E0716
89/// ```ignore
90/// # use devela::{format_args, FmtWrite};
91/// let mut s = String::new();
92/// let (a, b) = (1, 2);
93///
94/// let A = format_args!("A{a}"); // ← freed here
95/// let B = format_args!("B{b}"); // ← freed here
96/// write!(s, "{A}+{B},");
97/// ```
98#[macro_export]
99#[cfg_attr(cargo_primary_package, doc(hidden))]
100macro_rules! is {
101    ($cond:expr ; $then:expr) => {
102        if $cond {
103            $then
104        }
105    };
106    ($cond:expr ; $then:expr ; $($else:expr)?) => {
107        // WAIT: [stmt_expr_attributes](https://github.com/rust-lang/rust/issues/15701)
108        // #[allow(clippy::redundant_else)]
109        if $cond {
110            $then
111        } else {
112            $( $else )?
113        }
114    };
115
116    (let $pat:pat = $cond:expr ; $then:expr) => {
117        #[allow(clippy::question_mark)]
118        if let $pat = $cond {
119            $then
120        }
121    };
122    (let $pat:pat = $cond:expr ; $then:expr ; $($else:expr)? ) => {
123        if let $pat = $cond {
124            $then
125        } else {
126            $( $else )?
127        }
128    };
129
130    // Temporary value binding helper that:
131    // 1. Binds short-lived expressions to names
132    // 3. Enables expression chaining while maintaining temporary lifetimes
133    //
134    // source: https://github.com/rust-lang/rust/issues/92698#issuecomment-1680155957
135    (tmp $name:pat = $val:expr; $($rest:tt)+) => {
136        match $val { $name => $crate::is!($($rest)*) }
137    };
138    ($($rest:tt)+) => {{ $($rest)+ }}
139}
140#[doc(inline)]
141pub use is;
142
143/// Renamed to [`is`].
144#[macro_export]
145#[cfg_attr(cargo_primary_package, doc(hidden))]
146#[deprecated(since = "0.23.0", note = "Use the 'is!' macro instead.")]
147macro_rules! iif { ($($tt:tt)*) => { $crate::is![$($tt)*] }; }
148#[allow(deprecated, reason = "re-exported")]
149#[doc(inline)]
150pub use iif;
151
152#[cfg(test)]
153mod test_is {
154    use crate::is;
155
156    #[test]
157    fn is_if() {
158        assert_eq!('a', is!(true ; 'a' ; 'b'));
159        assert_eq!('b', is!(false ; 'a' ; 'b'));
160    }
161
162    #[test]
163    fn is_let() {
164        let somea = Some('a');
165        let noa: Option<char> = None;
166
167        assert_eq!('a', is!(let Some(a) = somea; a ; 'b'));
168        assert_eq!('b', is!(let Some(a) = noa; a ; 'b'));
169    }
170}