kiam/lib.rs
1//! _("kiam" is "when" in Esperanto)_
2//!
3//! This crate introduces [`when!`] macro which provides better syntax for
4//! `if`/`else if`/`else` chains. The syntax is similar to `match`.
5//!
6//! (idea is borrowed from [kotlin][kt-when-expr])
7//!
8//! [kt-when-expr]: https://kotlinlang.org/docs/reference/control-flow.html#when-expression
9#![no_std]
10#![forbid(unsafe_code)]
11#![deny(missing_docs, broken_intra_doc_links)]
12
13/// Better syntax for `if`/`else if`/`else` similar to `match` syntax
14///
15/// ## Usage
16///
17/// Usage is similar to the usage of `match`, but instead of patterns, branches are guarded by boolean expression:
18///
19/// ```rust
20/// kiam::when! {
21/// false => (),
22/// true => (),
23/// // ...
24/// }
25/// ```
26///
27/// `_` can be used as a default branch (it's also required to use `when!` in expression position):
28///
29/// ```rust
30/// let x = kiam::when! {
31/// false => 0,
32/// _ => 1,
33/// };
34///
35/// assert_eq!(x, 1);
36/// ```
37///
38/// You can also use `let <pat> =` to match a pattern, but in difference with `match` you'll have to provide an expression for every pattern:
39///
40/// ```rust
41/// let a = None;
42/// let b = Some(17);
43/// let fallible = || Err(());
44///
45/// let x = kiam::when! {
46/// let Some(x) = a => x,
47/// let Ok(x) = fallible() => x,
48/// let Some(x) = b => (x as u32) + 1,
49/// _ => 1,
50/// };
51///
52/// assert_eq!(x, 18);
53/// ```
54///
55/// Last notes:
56/// - You can also compare structure literals without brackets (you can't do this with `if`/`else if`/`else` chain)
57/// - You can mixup boolean-branches with pattern matching
58/// - Only one branch is executed (not to be confused with `switch` in C-like languages)
59///
60/// ```rust
61/// let mut x = 0;
62///
63/// kiam::when! {
64/// let Ok(_) = Err::<(), _>(()) => x = 1,
65/// true => x = 2,
66/// true => x = 3,
67/// let Some(n) = Some(42) => x = n,
68/// };
69///
70/// assert_eq!(x, 2);
71/// ```
72///
73/// ```compile_fail
74/// #[derive(PartialEq)]
75/// struct Struct { a: i32 }
76///
77/// // This does not compile because of the ambiguity
78/// if Struct { a: 0 } == Struct { a: 0 } {
79/// // ...
80/// }
81/// ```
82///
83/// ```rust
84/// #[derive(PartialEq)]
85/// struct Struct { a: i32 }
86///
87/// kiam::when! {
88/// // This, on the other hand, compiles fine
89/// Struct { a: 0 } == Struct { a: 0 } => {
90/// // ...
91/// },
92/// }
93/// ```
94///
95/// ## Grammar
96///
97/// ```text
98/// grammar:
99/// ╭───────────────>────────────────╮ ╭────>────╮
100/// │ │ │ │
101/// │├──╭── line ──╮──╯── "," ── "_" ── "=>" ── expr ──╰──╯── "," ──╰──┤│
102/// │ │
103/// ╰── "," ───╯
104///
105/// line:
106/// ╭─────────────>─────────────╮
107/// │ │
108/// │├──╯── "let"/i ── pat ── "=" ──╰── expr ── "=>" ── expr ──┤│
109/// ```
110#[macro_export]
111macro_rules! when {
112 (
113 $(
114 $(let $pat:pat = )? $cond:expr => $branch:expr
115 ),+
116 $(, _ => $def_branch:expr)?
117 $(,)?
118 ) => {
119 $(
120 if $(let $pat = )? $cond {
121 $branch
122 } else
123 )+
124 {
125 $(
126 $def_branch
127 )?
128 }
129 };
130}
131
132#[cfg(test)]
133mod tests {
134 #[test]
135 fn it_works() {
136 let r = when! {
137 false => 0,
138 true => 1,
139 true => 2,
140 _ => 42,
141 };
142
143 assert_eq!(r, 1);
144 }
145
146 #[test]
147 fn pattern() {
148 let r = when! {
149 let Some(x) = None => x,
150 let Some(y) = Some(12) => y + 1,
151 _ => 0,
152 };
153
154 assert_eq!(r, 13);
155 }
156
157 #[test]
158 fn mixed() {
159 let r = when! {
160 false => 0,
161 let Some(_) = None::<bool> => 0,
162 let Some(y) = Some(12) => y + 1,
163 true => 1,
164 _ => 0,
165 };
166
167 assert_eq!(r, 13);
168 }
169
170 #[test]
171 fn r#struct() {
172 #[derive(PartialEq)]
173 struct Struct {
174 x: i32,
175 }
176
177 // won't work with if/elseif/else
178 #[allow(clippy::eq_op)]
179 let r = when! {
180 Struct { x: 0 } == Struct { x: 1 } => 0,
181 Struct { x: 22 } == Struct { x: 22 } => 1,
182 _ => 2,
183 };
184
185 assert_eq!(r, 1);
186 }
187
188 #[test]
189 fn no_def() {
190 let mut x = 0;
191
192 when! {
193 false => x = 18,
194 true => x += 1,
195 }
196
197 assert_eq!(x, 1);
198 }
199}