1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Recoverable assertions, inspired by [the use of `assert()` in
//! SQLite](https://www.sqlite.org/assert.html).
//!
//! `never!` and `always!` return the actual value of the condition if
//! `debug_assertions` are disabled.
//!
//! Use them when terminating on assertion failure is worse than continuing.
//!
//! One example would be a critical application like a database:
//!
//! ```ignore
//! use always_assert::never;
//!
//! fn apply_transaction(&mut self, tx: Transaction) -> Result<(), TransactionAborted> {
//!     let delta = self.compute_delta(&tx);
//!
//!     if never!(!self.check_internal_invariant(&delta)) {
//!         // Ok, something in this transaction messed up our internal state.
//!         // This really shouldn't be happening, and this signifies a bug.
//!         // Luckily, we can recover by just rejecting the transaction.
//!         return abort_transaction(tx);
//!     }
//!     self.commit(delta);
//!     Ok(())
//! }
//! ```
//!
//! Another example is assertions about non-critical functionality in usual apps
//!
//! ```ignore
//! use always_assert::never;
//!
//! let english_message = "super app installed!"
//! let mut local_message = localize(english_message);
//! if never!(local_message.is_empty(), "missing localization for {}", english_message) {
//!     // We localized all the messages but this one slipper through the cracks?
//!     // Better to show the english one then than to fail outright;
//!     local_message = english_message;
//! }
//! println!("{}", local_message);
//! ```

/// Asserts that the condition is always true and returns its actual value.
///
/// If the condition is true does nothing and and evaluates to true.
///
/// If the condition is false:
/// * panics if `force` feature or `debug_assertions` are enabled,
/// * logs an error if `log` feature is enabled,
/// * evaluates to false.
///
/// Accepts `format!` style arguments.
#[macro_export]
macro_rules! always {
    ($cond:expr) => {
        $crate::always!($cond, "assertion failed: {}", stringify!($cond))
    };

    ($cond:expr, $fmt:literal $($arg:tt)*) => {{
        let cond = $cond;
        if cfg!(debug_assertions) || $crate::__FORCE {
            assert!(cond, $fmt $($arg)*);
        }
        if !cond {
            $crate::__log_error!($fmt $($arg)*);
        }
        cond
    }};
}

/// Asserts that the condition is never true and returns its actual value.
///
/// If the condition is false does nothing and and evaluates to false.
///
/// If the condition is true:
/// * panics if `force` feature or `debug_assertions` are enabled,
/// * logs an error if `log` feature is enabled,
/// * evaluates to true.
///
/// Accepts `format!` style arguments.
///
/// Empty condition is equivalent to false:
///
/// ```ignore
/// never!("oups") ~= unreachable!("oups")
/// ```
#[macro_export]
macro_rules! never {
    (true $($tt:tt)*) => { $crate::never!((true) $($tt)*) };
    (false $($tt:tt)*) => { $crate::never!((false) $($tt)*) };
    () => { $crate::never!("assertion failed: entered unreachable code") };
    ($fmt:literal $(, $($arg:tt)*)?) => {{
        if cfg!(debug_assertions) || $crate::__FORCE {
            unreachable!($fmt $(, $($arg)*)?);
        }
        $crate::__log_error!($fmt $(, $($arg)*)?);
    }};

    ($cond:expr) => {{
        let cond = !$crate::always!(!$cond);
        cond
    }};

    ($cond:expr, $fmt:literal $($arg:tt)*) => {{
        let cond = !$crate::always!(!$cond, $fmt $($arg)*);
        cond
    }};
}

#[cfg(feature = "log")]
#[doc(hidden)]
pub use log::error as __log_error;

#[cfg(not(feature = "log"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __log_error {
    ($($tt:tt)*) => {};
}

#[doc(hidden)]
pub const __FORCE: bool = cfg!(feature = "force");