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}