#![cfg_attr(not(test), no_std)]
#[macro_export(local_inner_macros)]
macro_rules! if_chain {
($($tt:tt)*) => {
__if_chain! { @init () $($tt)* }
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! __if_chain {
(@init ($($tt:tt)*) then { $($then:tt)* } else { $($other:tt)* }) => {
__if_chain! { @expand { $($other)* } $($tt)* then { $($then)* } }
};
(@init ($($tt:tt)*) then { $($then:tt)* }) => {
__if_chain! { @expand {} $($tt)* then { $($then)* } }
};
(@init ($($tt:tt)*) $head:tt $($tail:tt)*) => {
__if_chain! { @init ($($tt)* $head) $($tail)* }
};
(@expand { $($other:tt)* } let $pat:pat = $expr:expr; $($tt:tt)+) => {
{
let $pat = $expr;
__if_chain! { @expand { $($other)* } $($tt)+ }
}
};
(@expand { $($other:tt)* } let $ident:ident: $ty:ty = $expr:expr; $($tt:tt)+) => {
{
let $ident: $ty = $expr;
__if_chain! { @expand { $($other)* } $($tt)+ }
}
};
(@expand { $($other:tt)* } let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => {
match $expr {
$pat1 | $($pat)|+ => __if_chain! { @expand { $($other)* } $($tt)+ }
}
};
(@expand {} if let $pat:pat = $expr:expr; $($tt:tt)+) => {
if let $pat = $expr {
__if_chain! { @expand {} $($tt)+ }
}
};
(@expand { $($other:tt)+ } if let $pat:pat = $expr:expr; $($tt:tt)+) => {
if let $pat = $expr {
__if_chain! { @expand { $($other)+ } $($tt)+ }
} else {
$($other)+
}
};
(@expand { $($other:tt)* } if let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => {
match $expr {
$pat1 | $($pat)|+ => { __if_chain! { @expand { $($other)* } $($tt)+ } },
_ => { $($other)* }
}
};
(@expand {} if $expr:expr; $($tt:tt)+) => {
if $expr {
__if_chain! { @expand {} $($tt)+ }
}
};
(@expand { $($other:tt)+ } if $expr:expr; $($tt:tt)+) => {
if $expr {
__if_chain! { @expand { $($other)+ } $($tt)+ }
} else {
$($other)+
}
};
(@expand { $($other:tt)* } then { $($then:tt)* }) => {
$($then)*
};
}
#[cfg(test)]
mod tests {
#[test]
fn simple() {
let x: Option<Result<Option<String>, (u32, u32)>> = Some(Err((41, 42)));
let mut success = false;
if_chain! {
if let Some(y) = x;
if let Err(z) = y;
let (_, b) = z;
if b == 42;
then { success = true; }
}
assert!(success);
}
#[test]
fn empty() {
let success;
if_chain! {
then { success = true; }
}
assert!(success);
}
#[test]
fn empty_with_else() {
let success;
if_chain! {
then { success = true; }
else { unreachable!(); }
}
assert!(success);
}
#[test]
fn if_let_multiple_patterns() {
#[derive(Copy, Clone)]
enum Robot { Nano, Biscuit1, Biscuit2 }
for &(robot, expected) in &[
(Robot::Nano, false),
(Robot::Biscuit1, true),
(Robot::Biscuit2, true),
] {
let is_biscuit = if_chain! {
if let Robot::Biscuit1 | Robot::Biscuit2 = robot;
then { true } else { false }
};
assert_eq!(is_biscuit, expected);
}
}
#[test]
fn let_multiple_patterns() {
let x: Result<u32, u32> = Ok(42);
if_chain! {
let Ok(x) | Err(x) = x;
then { assert_eq!(x, 42); }
else { panic!(); }
}
}
#[test]
fn let_type_annotation_patterns() {
let mut x = 1;
if_chain! {
if x > 0;
let y: u32 = 2;
then { x += y; }
else { x += 1; }
};
assert_eq!(x, 3);
}
}