#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
#![allow(clippy::multiple_crate_versions)]
pub use colored::Colorize;
pub use moosicbox_env_utils;
#[macro_export]
macro_rules! assert {
($evaluate:expr $(,)?) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !($evaluate)
{
eprintln!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"assert failed:\n{}",
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
log::logger().flush();
std::process::exit(1);
}
};
($evaluate:expr, $($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !($evaluate)
{
eprintln!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"assert failed: {}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
log::logger().flush();
std::process::exit(1);
}
};
}
#[macro_export]
macro_rules! assert_or_err {
($evaluate:expr, $err:expr, $(,)?) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !($evaluate)
{
$crate::assert!($evaluate, "{:?}", $err)
} else if !($evaluate) {
return Err($err);
}
};
($evaluate:expr, $err:expr, $($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !($evaluate)
{
$crate::assert!($evaluate, $($message)*)
} else if !($evaluate) {
return Err($err);
}
};
}
#[macro_export]
macro_rules! assert_or_error {
($evaluate:expr, $($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !($evaluate)
{
$crate::assert!($evaluate, $($message)*)
} else if !($evaluate) {
log::error!($($message)*);
}
};
}
#[macro_export]
macro_rules! assert_or_unimplemented {
($evaluate:expr, $(,)?) => {
let success = ($evaluate);
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !success
{
$crate::assert!(success)
} else if !success {
unimplemented!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
};
($evaluate:expr, $($message:tt)+) => {
let success = ($evaluate);
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !success
{
$crate::assert!(success, $($message)*)
} else if !success {
unimplemented!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
};
}
#[macro_export]
macro_rules! assert_or_panic {
($evaluate:expr, $(,)?) => {{
let success = ($evaluate);
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !success
{
$crate::assert!(success)
} else if !success {
panic!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
}};
($evaluate:expr, $($message:tt)+) => {{
let success = ($evaluate);
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
&& !success
{
$crate::assert!(success, $($message)*)
} else if !success {
panic!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
}};
}
#[macro_export]
macro_rules! die {
() => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
eprintln!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!("{}", std::backtrace::Backtrace::force_capture()).as_str()
)))
);
log::logger().flush();
std::process::exit(1);
}
};
($($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
eprintln!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
log::logger().flush();
std::process::exit(1);
}
};
}
#[macro_export]
macro_rules! die_or_warn {
($($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
eprintln!(
"{}",
$crate::Colorize::on_yellow($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
log::logger().flush();
std::process::exit(1);
} else {
log::warn!(
"{}",
$crate::Colorize::on_yellow($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
};
}
#[macro_export]
macro_rules! die_or_err {
($err:expr, $($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
{
$crate::die!($($message)*);
unreachable!();
} else {
return Err($err);
}
};
}
#[macro_export]
macro_rules! die_or_error {
($($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
eprintln!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
log::logger().flush();
std::process::exit(1);
} else {
log::error!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
};
}
#[macro_export]
macro_rules! die_or_propagate {
($evaluate:expr, $($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
match $evaluate {
Ok(x) => x,
Err(e) => $crate::die!($($message)*),
}
} else {
$evaluate?
}
};
($evaluate:expr $(,)?) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
match $evaluate {
Ok(x) => x,
Err(_e) => $crate::die!(),
}
} else {
$evaluate?
}
};
}
#[macro_export]
macro_rules! die_or_panic {
($($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
eprintln!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
log::logger().flush();
std::process::exit(1);
} else {
panic!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
};
}
#[macro_export]
macro_rules! die_or_unimplemented {
($($message:tt)+) => {
if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
eprintln!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
log::logger().flush();
std::process::exit(1);
} else {
unimplemented!(
"{}",
$crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
format!(
"{}\n{}",
$crate::Colorize::underline(format!($($message)*).as_str()),
std::backtrace::Backtrace::force_capture()
)
.as_str()
)))
);
}
};
}
#[cfg(test)]
mod tests {
#[derive(Debug, PartialEq)]
enum TestError {
InvalidValue,
Critical,
}
#[test_log::test]
fn test_assert_disabled_no_op() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert!(false);
crate::assert!(false, "this message should not appear");
}
#[test_log::test]
fn test_assert_disabled_with_true_condition() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert!(true);
crate::assert!(true, "condition is true");
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_assert_or_err_returns_error_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function(value: i32) -> Result<i32, TestError> {
crate::assert_or_err!(value >= 0, TestError::InvalidValue,);
Ok(value * 2)
}
let result = test_function(-5);
assert_eq!(result, Err(TestError::InvalidValue));
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_assert_or_err_succeeds_with_true_condition() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function(value: i32) -> Result<i32, TestError> {
crate::assert_or_err!(value >= 0, TestError::InvalidValue, "value was {}", value);
Ok(value * 2)
}
let result = test_function(5);
assert_eq!(result, Ok(10));
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_assert_or_err_with_message() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function(value: i32) -> Result<i32, TestError> {
crate::assert_or_err!(
value <= 100,
TestError::InvalidValue,
"Value {} exceeds maximum",
value
);
Ok(value)
}
let result = test_function(150);
assert_eq!(result, Err(TestError::InvalidValue));
}
#[test_log::test]
fn test_assert_or_error_logs_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert_or_error!(false, "This is a test error message");
let value = 42;
crate::assert_or_error!(false, "Value is {}", value);
}
#[test_log::test]
fn test_assert_or_error_succeeds_with_true_condition() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert_or_error!(true, "This should not log");
}
#[test_log::test]
#[should_panic(expected = "Expected panic message")]
fn test_assert_or_panic_panics_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert_or_panic!(false, "Expected panic message");
}
#[test_log::test]
fn test_assert_or_panic_succeeds_with_true_condition() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert_or_panic!(true, "Should not panic");
}
#[test_log::test]
#[should_panic(expected = "not implemented")]
fn test_assert_or_unimplemented_calls_unimplemented_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert_or_unimplemented!(false, "Feature not implemented");
}
#[test_log::test]
fn test_assert_or_unimplemented_succeeds_with_true_condition() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert_or_unimplemented!(true, "Should not call unimplemented");
}
#[test_log::test]
fn test_die_disabled_no_op() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::die!();
crate::die!("This message should not exit");
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_die_or_err_returns_error_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function() -> Result<(), TestError> {
crate::die_or_err!(TestError::Critical, "Critical failure");
}
let result = test_function();
assert_eq!(result, Err(TestError::Critical));
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_die_or_err_with_formatting() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function(code: i32) -> Result<(), TestError> {
crate::die_or_err!(TestError::Critical, "Error code: {}", code);
}
let result = test_function(500);
assert_eq!(result, Err(TestError::Critical));
}
#[test_log::test]
fn test_die_or_error_logs_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::die_or_error!("This is a critical error");
crate::die_or_error!("Error with value: {}", 42);
}
#[test_log::test]
fn test_die_or_warn_logs_warning_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::die_or_warn!("This is a warning");
crate::die_or_warn!("Warning with value: {}", 100);
}
#[test_log::test]
#[should_panic(expected = "Expected panic")]
fn test_die_or_panic_panics_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::die_or_panic!("Expected panic");
}
#[test_log::test]
#[should_panic(expected = "Panic with code: 404")]
fn test_die_or_panic_with_formatting() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::die_or_panic!("Panic with code: {}", 404);
}
#[test_log::test]
#[should_panic(expected = "not implemented")]
fn test_die_or_unimplemented_calls_unimplemented_when_disabled() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::die_or_unimplemented!("Not implemented yet");
}
#[test_log::test]
fn test_assert_with_side_effects() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
let mut counter = 0;
crate::assert!({
counter += 1;
false
});
assert_eq!(counter, 0);
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_assert_or_err_with_complex_error_types() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
#[derive(Debug, PartialEq)]
struct ComplexError {
code: i32,
message: String,
}
fn test_function() -> Result<(), ComplexError> {
crate::assert_or_err!(
false,
ComplexError {
code: 42,
message: "test error".to_string()
},
"Complex error test"
);
Ok(())
}
let result = test_function();
assert!(result.is_err());
if let Err(e) = result {
assert_eq!(e.code, 42);
assert_eq!(e.message, "test error");
}
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_macro_works_without_explicit_imports() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function() -> Result<(), TestError> {
crate::assert_or_err!(true, TestError::InvalidValue,);
Ok(())
}
assert_eq!(test_function(), Ok(()));
}
#[test_log::test]
fn test_assert_with_trailing_comma() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
crate::assert!(true,);
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_assert_or_err_with_trailing_comma() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function() -> Result<(), TestError> {
crate::assert_or_err!(true, TestError::InvalidValue,);
Ok(())
}
assert_eq!(test_function(), Ok(()));
}
#[test_log::test]
#[allow(clippy::items_after_statements)]
fn test_multiple_assert_or_err_in_sequence() {
unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
fn test_function(a: i32, b: i32) -> Result<i32, TestError> {
crate::assert_or_err!(a >= 0, TestError::InvalidValue, "a must be non-negative");
crate::assert_or_err!(b >= 0, TestError::InvalidValue, "b must be non-negative");
crate::assert_or_err!(a + b <= 100, TestError::InvalidValue, "sum too large");
Ok(a + b)
}
assert_eq!(test_function(10, 20), Ok(30));
assert_eq!(test_function(-1, 20), Err(TestError::InvalidValue));
assert_eq!(test_function(10, -1), Err(TestError::InvalidValue));
assert_eq!(test_function(60, 50), Err(TestError::InvalidValue));
}
}