#![deny(
warnings,
missing_docs,
rustdoc::all,
clippy::pedantic,
clippy::dbg_macro,
clippy::semicolon_if_nothing_returned
)]
#![forbid(unsafe_code)]
#![allow(clippy::non_ascii_literal)]
#![doc(test(attr(deny(warnings), forbid(unsafe_code))))]
#![cfg_attr(any(not(feature = "no-core"), doc, test), no_std)]
#![cfg_attr(
all(feature = "no-core", not(any(doc, test))),
feature(no_core),
no_core
)]
#![cfg_attr(feature = "no-core", doc = " ```")]
#![cfg_attr(not(feature = "no-core"), doc = " ```compile_fail")]
#[macro_export]
macro_rules! match_eq {
($input:expr => {
$(#[$arm_attr:meta])*
$binding:pat = $($expected:expr),+ => $(#[$block_attr:meta])* $arm:block
$($remaining:tt)+
}) => (match $input {
$(#[$arm_attr])*
input if $(input == $expected) || + => {
let $binding = input;
$(#[$block_attr])*
$arm
}
input => $crate::match_eq!(input => { $($remaining)+ }),
});
($input:expr => {
$(#[$attr:meta])*
$binding:pat = $($expected:expr),+ => $arm:expr,
$($remaining:tt)+
}) => ($crate::match_eq!($input => {
$(#[$attr])?
$binding = $($expected),+ => { $arm }
$($remaining)+
}));
($input:expr => {
$(#[$attr:meta])*
$($binding:pat)|+ => $arm:expr $(,)?
}) => (match $input {
$(#[$attr])*
$($binding)|+ => $arm,
});
($input:expr => { $($expected:expr),+ => $($remaining:tt)+ }) => (
$crate::match_eq!($input => { _ = $($expected),+ => $($remaining)+ })
);
}
#[macro_export]
macro_rules! matches_eq {
($actual:expr, $($expected:expr),+ $(,)?) => (match $actual {
actual => $(actual == $expected) || +
});
}
pub mod prelude {
pub use super::{match_eq, matches_eq};
}
#[cfg(test)]
mod tests {
use core::{cell::Cell, mem};
#[test]
fn live_long_enough() {
struct ShortLived<'a> {
assign_me: &'a mut (),
flag: &'a mut bool,
compared: &'a Cell<bool>,
}
impl PartialEq<()> for ShortLived<'_> {
fn eq(&self, (): &()) -> bool {
assert!(!self.compared.replace(true));
false
}
}
let mut flag = false;
let compared = false.into();
match_eq!(ShortLived { assign_me: &mut (), flag: &mut flag, compared: &compared } => {
s = () => {
*s.assign_me = ();
unreachable!();
}
#[allow(dead_code)]
s => {
struct Dead;
*s.assign_me = ();
assert!(!mem::replace(s.flag, true));
}
});
assert!(compared.replace(false));
assert!(mem::replace(&mut flag, false));
match_eq!(ShortLived { assign_me: &mut (), flag: &mut flag, compared: &compared } => {
s => {
*s.assign_me = ();
assert!(!mem::replace(s.flag, true));
}
});
assert!(!compared.get());
assert!(flag);
}
#[test]
fn single_arm() {
let mut flag = false;
assert!(!match_eq!(0 => {
#[allow(dead_code)]
#[allow(unused_variables)]
unused => {
struct Dead;
mem::replace(&mut flag, true)
}
}));
assert!(mem::replace(&mut flag, false));
assert!(!match_eq!(-1 => {
x => {
assert_eq!(x, -1);
mem::replace(&mut flag, true)
}
}));
assert!(mem::replace(&mut flag, false));
assert!(!match_eq!(-1 => { _ => mem::replace(&mut flag, true) }));
assert!(flag);
}
struct Wrapper(&'static str);
impl PartialEq<&str> for Wrapper {
fn eq(&self, other: &&str) -> bool {
self.0 == *other
}
}
impl PartialEq for Wrapper {
fn eq(&self, _: &Self) -> bool {
false
}
}
#[test]
fn single() {
assert_eq!(
match_eq!(Wrapper("🎨") => {
"🚀" => 0,
Wrapper("🎨") => -1,
Wrapper("🎨"), "🎯" => -2,
"🎨" => -3,
"🐟", Wrapper("🥀") => #[allow(unused_variables)] #[allow(clippy::let_unit_value)] {
let unused: ();
-4
}
"🎨" => -5,
_ => -6,
}),
-3
);
let mut flag = false;
match_eq!(Wrapper("🐌") => {
wrapper = "🐌" => #[allow(dead_code)] {
struct Dead;
assert_eq!(wrapper.0, "🐌");
assert!(!mem::replace(&mut flag, true));
}
Wrapper("🐌") => unreachable!(),
_ => unreachable!(),
});
assert!(flag);
assert_eq!(match_eq!(-1 => { 0 => unreachable!(), x => x - 1 }), -2);
}
#[test]
fn multiple() {
assert_eq!(
match_eq!(Wrapper("💣") => {
"🔮", Wrapper("💣"), Wrapper("💰") => -1,
"📍" => -2,
Wrapper("💣"), "🌃" => -3,
"📻", "💣" => #[allow(dead_code)] {
struct Dead;
-4
}
_ => -5,
}),
-4
);
let mut flag = false;
match_eq!(Wrapper("🎁") => {
wrapper = "🎁", Wrapper("📻"), Wrapper("🎁"), "🎁" =>
#[allow(unused_variables)]
#[allow(clippy::let_unit_value)]
{
let unused: ();
assert_eq!(wrapper.0, "🎁");
assert!(!mem::replace(&mut flag, true));
}
"🥝" => unreachable!(),
"🦄", "🔇" => unreachable!(),
_ => unreachable!(),
});
assert!(flag);
}
#[test]
fn binding() {
let mut flag = false;
match_eq!((flag,) => {
#[allow(unconditional_panic)]
(mut x,) = (true,) => {
x &= false;
let _ = i8::from(x) / 0;
}
#[allow(unused_variables, dead_code)]
#[allow(unconditional_panic)]
(unused,) = {
struct Dead;
(true,)
} => {
let _ = 1 / 0;
}
(false,) => assert!(!mem::replace(&mut flag, true)),
(..) => unreachable!(),
});
assert!(mem::replace(&mut flag, false));
}
#[cfg(feature = "no-core")]
#[test]
fn or_patterns() {
let mut flag = false;
match_eq!(-1 => {
0 => unreachable!(),
(x | x) = -1 => #[allow(dead_code)] {
struct Dead;
assert_eq!(x, -1);
assert!(!mem::replace(&mut flag, true));
}
(_ | _) = -2 => unreachable!(),
_ => unreachable!(),
});
assert!(mem::replace(&mut flag, false));
match_eq!(-2 => {
#[allow(unused_variables)]
#[allow(unconditional_panic)]
(x | x) = -1 => {
let _ = -3 / 0;
}
y => {
assert_eq!(y, -2);
assert!(!mem::replace(&mut flag, true));
}
});
assert!(mem::replace(&mut flag, false));
match_eq!(-3 => {
(x | x) = -3 => {
assert_eq!(x, -3);
assert!(!mem::replace(&mut flag, true));
}
_ => unreachable!(),
});
assert!(flag);
}
#[test]
fn fallback() {
let mut flag = false;
assert!(!match_eq!(Wrapper("💥") => {
"⭐" => unreachable!(),
Wrapper("💥") => unreachable!(),
#[allow(dead_code)]
#[allow(unreachable_patterns)]
_ | _ => {
struct Dead;
mem::replace(&mut flag, true)
}
}));
assert!(mem::replace(&mut flag, false));
assert!(!match_eq!(Wrapper("❌") => {
"🎆", Wrapper("❌") => unreachable!(),
#[allow(unreachable_patterns)]
wrapper @ Wrapper(..) | wrapper => {
assert_eq!(wrapper.0, "❌");
mem::replace(&mut flag, true)
}
}));
assert!(mem::replace(&mut flag, false));
assert!(!match_eq!(Wrapper("🍩") => {
Wrapper("🍩") => unreachable!(),
_ => mem::replace(&mut flag, true),
}));
assert!(flag);
}
#[test]
fn type_deduction() {
assert_eq!(
match_eq!(-1_i128 => {
1 => -1,
(-1).into() => -2,
_ => -3,
}),
-2
);
assert_eq!(
match_eq!(1.into() => {
2 => -1,
-1_i64 | _ => -2,
}),
-2
);
}
}