match-eq 0.2.4

This crate is no longer maintained.
Documentation
#![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
)]
//! A [`match`](https://doc.rust-lang.org/nightly/reference/expressions/match-expr.html)-like
//! macro that uses [`PartialEq`] to ["match"](`PartialEq::eq`) each "patten".
//!
//! ## Crate feature
//! - `no-core`: Make this crate [`#![no_core]`](https://github.com/rust-lang/rust/issues/29639).
//!   <details>
//!   <summary><code>#![no_core]</code> example</summary>
//!
#![cfg_attr(feature = "no-core", doc = "  ```")]
#![cfg_attr(not(feature = "no-core"), doc = "  ```compile_fail")]
//!   #![feature(no_core, lang_items, start)]
//!   #![no_core]
//!
//!   #[cfg_attr(not(windows), link(name = "c"))]
//!   #[cfg_attr(windows, link(name = "msvcrt"))]
//!   extern "C" {}
//!
//!   use match_eq::prelude::*;
//!
//!   #[lang = "sized"]
//!   trait Sized {}
//!
//!   #[lang = "copy"]
//!   trait Copy {}
//!
//!   impl Copy for i32 {}
//!
//!   #[lang = "receiver"]
//!   trait Receiver {}
//!
//!   impl<T: ?Sized> Receiver for &T {}
//!
//!   #[lang = "eh_personality"]
//!   fn eh_personality() -> ! {
//!       loop {}
//!   }
//!
//!   #[lang = "eq"]
//!   trait CustomPartialEq<T> {
//!       fn eq(&self, _: &T) -> bool;
//!   }
//!
//!   impl CustomPartialEq<i32> for i32 {
//!       fn eq(&self, _: &i32) -> bool {
//!           loop {}
//!       }
//!   }
//!
//!   #[start]
//!   fn main(_: isize, _: *const *const u8) -> isize {
//!       match_eq!(1 => {
//!           1, 2 => if matches_eq!(3, 3, 4) {
//!               if matches_eq!(5, 6, 7) { 1 } else { 0 }
//!           } else {
//!               1
//!           },
//!           _ => 1,
//!       })
//!   }
//!   ```
//!   </details>
//!
//! ## Example
//! ```
//! # use match_eq::prelude::*;
//! use std::ffi::{OsStr, OsString};
//!
//! assert_eq!(
//!     match_eq!(OsString::from("🎉") => {
//!         "🎃" => 0,
//!         "🚀", "🎉" => -1,
//!         s = OsStr::new("🎉") => {
//!             drop::<OsString>(s);
//!             -2
//!         }
//!         ref s = "🎉", OsStr::new("🚀") => {
//!             let _: &OsString = s;
//!             -3
//!         }
//!         _ = "🎉" => unreachable!(),
//!         _ => -5,
//!     }),
//!     -1
//! );
//! ```

/// The main macro of this crate.
///
/// ## Syntax
/// ```text
/// MatchExpression => {
///     (
///         (MatchArmAttribute)*
///         (BindingPattern =)? (ComparingExpression,)* =>
///             ((BlockAttribute)* Block)|(Expression,)
///     )*
///     (MatchArmAttribute)*
///     MatchArmPattern => Expression,?
/// }
/// ```
/// Note that the latest arm has a different syntax: it's identical to the latest arm in normal
/// [`match`](https://doc.rust-lang.org/nightly/reference/expressions/match-expr.html) expressions.
///
/// ## Limitations
/// 1. This macro rejects trailing comma after a block:
///    ```compile_fail
///    # use match_eq::prelude::*;
///    match_eq!(-1 => {
///        -2 => {},
///        _ => (),
///    });
///    ```
///    Unless it's at the end:
///    ```
///    # use match_eq::prelude::*;
///    match_eq!(-1 => {
///        -2 => unreachable!(),
///        x => { assert_eq!(x, -1) },
///    });
///    ```
/// 2. This macro requires a `,` to be present even if your expression is ended with `}`:
///    ```compile_fail
///    # use match_eq::prelude::*;
///    match_eq!(-2 => {
///        -2 => if false { unreachable!() }
///        _ => unreachable!(),
///    });
///    ```
///    Unlike the built-in
///    [`match`](https://doc.rust-lang.org/nightly/reference/expressions/match-expr.html):
///    ```
///    match -2 {
///        -2 => if false { unreachable!() }
///         _ => unreachable!(),
///    }
///    ```
///    You need to add a comma to make it compile:
///    ```
///    # use match_eq::prelude::*;
///    match_eq!(-2 => {
///        -2 => if false { unreachable!() },
///        _ => unreachable!(),
///    })
///    ```
///
/// ## See also
/// The [example in the crate-level documentation](crate#example).
#[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)+ })
    );
}

/// [`match_eq`] version of [`matches`].
///
/// ## Examples
/// ```
/// # use match_eq::prelude::*;
/// assert!(matches_eq!(1 + 1, 2));
/// assert!(matches_eq!(1 + 2, 3, 4));
/// assert!(matches_eq!(2 + 2, -4, 4));
/// assert!(!matches_eq!(1 + 1, 3));
/// assert!(!matches_eq!(1 + 1, 3, 4));
/// assert!(matches_eq!(2 + 3, 5,)); // trailing comma is
/// assert!(matches_eq!(3 + 3, 6, 7,)); // always allowed
/// ```
#[macro_export]
macro_rules! matches_eq {
    ($actual:expr, $($expected:expr),+ $(,)?) => (match $actual {
        actual => $(actual == $expected) || +
    });
}

/// The prelude.
///
/// ```
/// use match_eq::prelude::*;
/// # #[allow(unused_imports)]
/// use {match_eq, matches_eq};
/// ```
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
        );
    }
}