enum_macro 0.3.1

Useful macro for enum
Documentation
//! Use `use enum_macro::em;` or `#[macro_use(em)]`
//! to import `em!` macro,.
//!
//! Notice: DO NOT import all macro,
//! there's are some helper macro in the crate.
//!
//! For more information about usage, see [em]

#[doc(hidden)]
#[macro_export]
macro_rules! em_if {
    ( $i:expr, $p:pat, $t:expr, $f:expr ) => {
        if let $p = $i {
            $t
        } else {
            $f
        }
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! get {
    ( $i:expr, $p:path, $pp:tt, $r:expr ) => {
        $crate::em_if!($i, $p$pp, Some($r), None)
    };
    ( $i:expr, $p:path ) => {
        $crate::em_if!($i, $p, Some(()), None)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! is {
    ( $i:expr, $p:path, $pp:tt, $r:expr ) => {
        $crate::em_if!($i, $p$pp, true, false)
    };
    ( $i:expr, $p:path ) => {
        $crate::em_if!($i, $p, true, false)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! map {
    ( $i:ident, $p:path, $pp:tt, $r:expr, $f:expr ) => {
        $crate::em_if!($i, $p$pp, $p($f($r)), $i)
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! and_then {
    ( $i:ident, $p:path, $pp:tt, ($($r:ident,)+), $f:expr ) => {
        $crate::em_if!($i, $p$pp, $f($($r,)+), $i)
    };
    ( $i:ident, $p:path, $pp:tt, $r:ident, $f:expr ) => {
        $crate::em_if!($i, $p$pp, $f($r), $i)
    };
}

/// enum helper macro
///
/// ```
/// use enum_macro::em;
///
/// #[derive(Clone, Debug, PartialEq)]
/// enum Enum {
///     A(i32),
///     B(i32, i32),
///     C { x: i32, y: i32 },
///     D, // Note: Unit variant must follow by '|'
/// }
///
/// let a = Enum::A(1);
/// let b = Enum::B(2, 3);
/// let c = Enum::C { x: 4, y: 5 };
/// let d = Enum::D;
///
/// // Get Inner Value
///
/// assert_eq!(em!(a.get Enum::A), Some(1));
/// assert_eq!(em!(Enum::A(1) => get Enum::A), Some(1));
/// assert_eq!(em!(b.get Enum::B[i, j]), Some((2, 3)));
/// assert_eq!(em!(c.get Enum::C{x, y}), Some((4, 5)));
/// assert_eq!(em!(c.get Enum::C{y, x}), Some((5, 4)));
/// assert_eq!(em!(c.get Enum::C{x, ..}), Some((4,)));
/// assert_eq!(em!(d.get Enum::D|), Some(())); // Unit variant must follow by '|'
/// assert_eq!(em!(Enum::D => get Enum::D|), Some(()));
///
/// // Variants Detect
///
/// assert_eq!(em!(a.is Enum::A), true);
/// assert_eq!(em!(Enum::A(1) => is Enum::A), true);
///
/// assert_eq!(em!(b.is Enum::C{x, y}), false);
/// // or
/// assert_eq!(em!(b.is Enum::C{..}), false);
/// // or
/// assert_eq!(em!(b.is Enum::C{}), false);
///
/// assert_eq!(em!(c.is Enum::B[i, j]), false);
/// // or
/// assert_eq!(em!(c.is Enum::B[..]), false);
/// // or
/// assert_eq!(em!(c.is Enum::B[]), false);
///
/// assert_eq!(em!(d.is Enum::D|), true); // Unit variant must follow by '|'
/// // or
/// assert_eq!(em!(Enum::D => is Enum::D|), true); // Unit variant must follow by '|'
///
/// // Map
///
/// let a2 = a.clone();
/// let b2 = b.clone();
/// assert_eq!(em!(a2.map Enum::A > |x| x + 1), Enum::A(2));
/// assert_eq!(em!(b2.map Enum::A > |x| x + 1), Enum::B(2, 3));
///
/// // And Then
///
/// let a2 = a.clone();
/// assert_eq!(em!(a2.and_then Enum::A > |x| Enum::B(x + 1, 0)), Enum::B(2, 0));
///
/// let b2 = b.clone();
/// assert_eq!(em!(b2.and_then Enum::B[x, y] > |x, y| Enum::C {x, y}), Enum::C { x: 2, y: 3 });
///
/// let b2 = b.clone();
/// // only care order
/// assert_eq!(em!(b2.and_then Enum::B[x, y] > |i, j| Enum::C {x: i, y: j}), Enum::C { x: 2, y: 3 });
/// ```
#[macro_export]
macro_rules! em {
    ( $i:ident.$m:ident $p:path $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, (_x), _x $(, $($a),+)?)
    };
    ( $i:ident.$m:ident $p:path[$(..)?] $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, (..), () $(, $($a),+)?)
    };
    ( $i:ident.$m:ident $p:path[$($x:ident),+] $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, ($($x,)+), ($($x,)+) $(, $($a),+)?)
    };
    ( $i:ident.$m:ident $p:path{$($($x:ident),+$(,)?)? $(..)?} $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, { $($($x,)+)? .. }, ($($($x,)+)?) $(, $($a),+)?)
    };
    ( $i:expr => $m:ident $p:path $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, (_x), _x $(, $($a),+)?)
    };
    ( $i:expr => $m:ident $p:path[$(..)?] $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, (..), () $(, $($a),+)?)
    };
    ( $i:expr => $m:ident $p:path[$($x:ident),+] $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, ($($x,)+), ($($x,)+) $(, $($a),+)?)
    };
    ( $i:expr => $m:ident $p:path{$($($x:ident),+$(,)?)? $(..)?} $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p, { $($($x,)+)? .. }, ($($($x,)+)?) $(, $($a),+)?)
    };
    ( $i:ident.$m:ident $p:path| $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p $(, $($a),+)?)
    };
    ( $i:expr => $m:ident $p:path| $(> $($a:expr),+)? ) => {
        $crate::$m!($i, $p $(, $($a),+)?)
    };
}

#[cfg(test)]
mod tests {
    #[derive(Debug, PartialEq)]
    enum Enum {
        A(i32),
        B(i32, i32),
        C { x: i32, y: i32 },
        D,
    }

    fn init() -> (Enum, Enum, Enum) {
        let a = Enum::A(1);
        let b = Enum::B(2, 3);
        let c = Enum::C { x: 4, y: 5 };
        (a, b, c)
    }

    #[test]
    fn get() {
        let (a, b, c) = init();
        let d = Enum::D;
        assert_eq!(em!(a.get Enum::A), Some(1));
        assert_eq!(em!(Enum::A(1) => get Enum::A), Some(1));
        assert_eq!(em!(b.get Enum::B[i, j]), Some((2, 3)));
        assert_eq!(em!(c.get Enum::C{x, y}), Some((4, 5)));
        assert_eq!(em!(c.get Enum::C{x, ..}), Some((4,)));
        assert_eq!(em!(d.get Enum::D|), Some(()));
        assert_eq!(em!(Enum::D => get Enum::D|), Some(()));
    }

    #[test]
    fn not_get() {
        let (a, b, c) = init();
        assert_eq!(em!(a.get Enum::C{x, y}), None);
        assert_eq!(em!(b.get Enum::A), None);
        assert_eq!(em!(c.get Enum::B[i, j]), None);
        assert_eq!(em!(Enum::A(1) => get Enum::D|), None);
    }

    #[test]
    fn is() {
        let (a, b, c) = init();
        let d = Enum::D;
        assert_eq!(em!(a.is Enum::A), true);
        assert_eq!(em!(b.is Enum::B[]), true);
        assert_eq!(em!(c.is Enum::C{}), true);
        assert_eq!(em!(d.is Enum::D|), true);
    }

    #[test]
    fn isnt() {
        let (a, b, c) = init();
        let d = Enum::D;
        assert_eq!(em!(a.is Enum::D|), false);
        assert_eq!(em!(b.is Enum::A), false);
        assert_eq!(em!(c.is Enum::B[]), false);
        assert_eq!(em!(d.is Enum::C{}), false);
    }

    #[test]
    fn map() {
        let (a, b, _c) = init();
        assert_eq!(em!(a.map Enum::A > |x| x + 1), Enum::A(2));
        assert_eq!(em!(b.map Enum::A > |x| x + 1), Enum::B(2, 3));
    }

    #[test]
    fn and_then() {
        let (a, b, c) = init();
        assert_eq!(
            em!(a.and_then Enum::A > |x| Enum::B(x + 1, 0)),
            Enum::B(2, 0)
        );
        assert_eq!(
            em!(b.and_then Enum::B[x, y] > |x, y| Enum::C {x, y}),
            Enum::C { x: 2, y: 3 }
        );
        assert_eq!(
            em!(c.and_then Enum::A > |x| Enum::B(x - 1, 0)),
            Enum::C { x: 4, y: 5 }
        );
    }
}