check 1.0.0

Convenience assert!-like macros which return instead of panicking
Documentation
//! Convenience `assert!`-like macros which immediately returns `None` or `Err(...)` instead of
//! panicking.
//!
//! # Examples
//!
//! In a function returning an `Option<T>`, invoke the macro with just enough parameters to get a
//! condition to check.
//!
//! ```
//! # use check::{check_eq, check};
//! # fn a() -> Option<()> {
//! # let (a, b, n) = (1, 1, 10);
//! check!(a < n);
//! check_eq!(a, b);
//! # Some(())
//! # }
//! ```
//!
//! This will expand to:
//!
//! ```
//! # fn a() -> Option<()> {
//! # let (a, b, n) = (1, 1, 10);
//! if !(a < n) {
//!   return None;
//! }
//! if a != b {
//!   return None;
//! }
//! # Some(())
//! # }
//! ```
//!
//! In a function returning a `Result<T, E>`, invoke the macro with an extra argument, which is the
//! error to return if the check fails (and must have type `E`), just like you can add arguments to
//! choose a panic message with `assert!`.
//!
//! ```
//! # use check::{check_eq, check};
//! # enum MyError { TooBig, NotEqual }
//! # fn a() -> Result<(), MyError> {
//! # let (a, b, n) = (1, 1, 10);
//! check!(a < n, MyError::TooBig);
//! check_eq!(a, b, MyError::NotEqual);
//! # Ok(())
//! # }
//! ```
//!
//! This will expand to:
//!
//! ```
//! # enum MyError { TooBig, NotEqual }
//! # fn a() -> Result<(), MyError> {
//! # let (a, b, n) = (1, 1, 10);
//! if !(a < n) {
//!   return Err(MyError::TooBig);
//! }
//! if a != b {
//!   return Err(MyError::NotEqual);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Note
//!
//! Actually, the two following lines are quite equivalent:
//!
//! ```
//! # use check::check;
//! # fn a() -> Option<()> {
//! # let (a, b) = (1, 1);
//! check!(a <= b);
//! (a <= b).then(|| ())?;
//! # Some(())
//! # }
//! ```
//!

#![no_std]
#![warn(missing_docs)]

/// Exits the function prematurely if the condition is not met.
///
/// # Examples
///
/// ```
/// use check::check;
///
/// fn foo(a: i32, n: i32) -> Option<()> {
///     check!(a < n);
///     // If the code reaches this line, then you know that `a < n`.
///     // If it was not true, `None` was returned.
///     Some(())
/// }
/// ```
///
/// ```
/// use check::check;
///
/// enum MyError {
///     TooBig,
///     NotEqual,
/// }
///
/// fn foo(a: i32, n: i32) -> Result<(), MyError> {
///     check!(a < n, MyError::TooBig);
///     // If the code reaches this line, then you know that `a < n`.
///     // If it was not true, `Err(MyError::TooBig)` was returned.
///     Ok(())
/// }
/// ```
#[macro_export]
macro_rules! check {
    ($cond:expr $(,)?) => {
        if !($cond) {
            return None;
        }
    };
    ($cond:expr, $e: expr $(,)?) => {
        if !($cond) {
            return Err($e);
        }
    };
}

/// Exits the function prematurely if the two expressions do not evaluate the same.
///
/// # Examples
///
/// ```
/// use check::check_eq;
///
/// fn foo(a: i32, b: i32) -> Option<()> {
///     check_eq!(a, b);
///     // If the code reaches this line, then you know that `a == b`.
///     // If it was not true, `None` was returned.
///     Some(())
/// }
/// ```
///
/// ```
/// use check::check_eq;
///
/// enum MyError {
///     TooBig,
///     NotEqual,
/// }
///
/// fn foo(a: i32, b: i32) -> Result<(), MyError> {
///     check_eq!(a, b, MyError::NotEqual);
///     // If the code reaches this line, then you know that `a == b`.
///     // If it was not true, `Err(MyError::NotEqual)` was returned.
///     Ok(())
/// }
/// ```
#[macro_export]
macro_rules! check_eq {
    ($a:expr, $b:expr $(,)?) => {
        if $a != $b {
            return None;
        }
    };
    ($a:expr, $b:expr, $e: expr $(,)?) => {
        if $a != $b {
            return Err($e);
        }
    };
}

/// Exits the function prematurely if the two expressions evaluate the same.
///
/// # Examples
///
/// ```
/// use check::check_ne;
///
/// fn foo(a: i32, b: i32) -> Option<()> {
///     check_ne!(a, b);
///     // If the code reaches this line, then you know that `a != b`.
///     // If it was not true, `None` was returned.
///     Some(())
/// }
/// ```
///
/// ```
/// use check::check_ne;
///
/// enum MyError {
///     TooBig,
///     Equal,
/// }
///
/// fn foo(a: i32, b: i32) -> Result<(), MyError> {
///     check_ne!(a, b, MyError::Equal);
///     // If the code reaches this line, then you know that `a != b`.
///     // If it was not true, `Err(MyError::Equal)` was returned.
///     Ok(())
/// }
/// ```
#[macro_export]
macro_rules! check_ne {
    ($a:expr, $b:expr $(,)?) => {
        if $a == $b {
            return None;
        }
    };
    ($a:expr, $b:expr, $e: expr $(,)?) => {
        if $a == $b {
            return Err($e);
        }
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn check_works() {
        fn o(a: i32) -> Option<i32> {
            check!(a < 10);
            Some(a + 1)
        }
        fn r(a: i32) -> Result<i32, i32> {
            check!(a < 10, a);
            Ok(a + 1)
        }

        assert_eq!(o(9), Some(10));
        assert_eq!(o(10), None);
        assert_eq!(r(9), Ok(10));
        assert_eq!(r(10), Err(10));
    }

    #[test]
    fn check_eq_works() {
        fn o(a: i32) -> Option<i32> {
            check_eq!(a, 9);
            Some(a + 1)
        }
        fn r(a: i32) -> Result<i32, i32> {
            check_eq!(a, 9, a);
            Ok(a + 1)
        }

        assert_eq!(o(9), Some(10));
        assert_eq!(o(10), None);
        assert_eq!(r(9), Ok(10));
        assert_eq!(r(10), Err(10));
    }

    #[test]
    fn check_ne_works() {
        fn o(a: i32) -> Option<i32> {
            check_ne!(a, 9);
            Some(a + 1)
        }
        fn r(a: i32) -> Result<i32, i32> {
            check_ne!(a, 9, a);
            Ok(a + 1)
        }

        assert_eq!(o(9), None);
        assert_eq!(o(10), Some(11));
        assert_eq!(r(9), Err(9));
        assert_eq!(r(10), Ok(11));
    }
}