moosicbox_assert/lib.rs
1//! Environment-controlled assertion macros for conditional debugging and testing.
2//!
3//! This crate provides assertion macros that can be toggled at runtime via the `ENABLE_ASSERT`
4//! environment variable. When enabled, failed assertions exit the process with colorized error
5//! messages and backtraces. When disabled, the macros have varying fallback behaviors:
6//! returning errors, logging warnings, panicking, or becoming no-ops.
7//!
8//! # Use Cases
9//!
10//! * Development and debugging environments where you want strict checking
11//! * Production environments where you want graceful degradation instead of process termination
12//! * Testing scenarios where you need conditional assertion behavior
13//! * Gradual migration from development assertions to production error handling
14//!
15//! # Environment Variables
16//!
17//! * `ENABLE_ASSERT` - Set to "1" to enable strict assertion mode (process exits on failure),
18//! any other value uses the fallback behavior of each macro
19//!
20//! # Examples
21//!
22//! Basic assertion that exits when enabled, does nothing when disabled:
23//!
24//! ```rust,no_run
25//! use moosicbox_assert::assert;
26//!
27//! unsafe { std::env::set_var("ENABLE_ASSERT", "1"); }
28//! let value = 42;
29//! assert!(value > 0, "Value must be positive");
30//! ```
31//!
32//! Assertion that returns an error when disabled:
33//!
34//! ```rust,no_run
35//! use moosicbox_assert::assert_or_err;
36//!
37//! #[derive(Debug)]
38//! enum Error { Invalid }
39//!
40//! fn validate(x: i32) -> Result<(), Error> {
41//! assert_or_err!(x >= 0, Error::Invalid, "Value must be non-negative");
42//! Ok(())
43//! }
44//! ```
45//!
46//! # Available Macros
47//!
48//! * [`assert!`] - Conditional assertion that exits or does nothing
49//! * [`assert_or_err!`] - Returns an error when disabled
50//! * [`assert_or_error!`] - Logs an error when disabled
51//! * [`assert_or_panic!`] - Panics when disabled
52//! * [`assert_or_unimplemented!`] - Calls `unimplemented!()` when disabled
53//! * [`die!`] - Unconditional exit when enabled, no-op when disabled
54//! * [`die_or_err!`] - Returns an error when disabled
55//! * [`die_or_error!`] - Logs an error when disabled
56//! * [`die_or_warn!`] - Logs a warning when disabled
57//! * [`die_or_panic!`] - Panics when disabled
58//! * [`die_or_propagate!`] - Propagates errors using `?` when disabled
59//! * [`die_or_unimplemented!`] - Calls `unimplemented!()` when disabled
60
61#![cfg_attr(feature = "fail-on-warnings", deny(warnings))]
62#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
63#![allow(clippy::multiple_crate_versions)]
64
65/// Re-export of the [`colored::Colorize`] trait for colorizing terminal output.
66///
67/// This trait is used internally by the assertion macros to format error messages
68/// with colored backgrounds (red for errors, yellow for warnings). It's re-exported
69/// to allow users to access colorization utilities without adding `colored` as a
70/// separate dependency.
71pub use colored::Colorize;
72
73/// Re-export of the `moosicbox_env_utils` crate for environment variable utilities.
74///
75/// This module provides the `default_env!` macro used internally by assertion macros
76/// to read the `ENABLE_ASSERT` environment variable. It's re-exported to allow users
77/// to access environment utilities without adding `moosicbox_env_utils` as a separate
78/// dependency.
79pub use moosicbox_env_utils;
80
81/// Conditional assertion that exits the process on failure when assertions are enabled.
82///
83/// When `ENABLE_ASSERT` environment variable is set to "1", this macro evaluates the condition
84/// and exits the process with a colorized error message and stack trace if the condition is false.
85/// When assertions are disabled, the condition is not evaluated and the macro becomes a no-op.
86///
87/// # Environment Variables
88///
89/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
90///
91/// # Examples
92///
93/// ```rust,no_run
94/// use moosicbox_assert::assert;
95///
96/// unsafe { std::env::set_var("ENABLE_ASSERT", "1"); }
97/// let value = 42;
98/// assert!(value > 0);
99/// assert!(value == 42, "Expected 42, got {}", value);
100/// ```
101///
102/// # Panics
103///
104/// Exits the process (via `std::process::exit(1)`) when the condition is false and `ENABLE_ASSERT=1`.
105#[macro_export]
106macro_rules! assert {
107 ($evaluate:expr $(,)?) => {
108 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
109 && !($evaluate)
110 {
111 eprintln!(
112 "{}",
113 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
114 format!(
115 "assert failed:\n{}",
116 std::backtrace::Backtrace::force_capture()
117 )
118 .as_str()
119 )))
120 );
121 log::logger().flush();
122 std::process::exit(1);
123 }
124 };
125 ($evaluate:expr, $($message:tt)+) => {
126 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
127 && !($evaluate)
128 {
129 eprintln!(
130 "{}",
131 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
132 format!(
133 "assert failed: {}\n{}",
134 $crate::Colorize::underline(format!($($message)*).as_str()),
135 std::backtrace::Backtrace::force_capture()
136 )
137 .as_str()
138 )))
139 );
140 log::logger().flush();
141 std::process::exit(1);
142 }
143 };
144}
145
146/// Conditional assertion that returns an error on failure.
147///
148/// When `ENABLE_ASSERT` environment variable is set to "1" and the condition is false,
149/// this macro exits the process with a colorized error message. When assertions are disabled
150/// and the condition is false, it returns the specified error value instead.
151///
152/// # Environment Variables
153///
154/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
155///
156/// # Examples
157///
158/// ```rust,no_run
159/// use moosicbox_assert::assert_or_err;
160///
161/// #[derive(Debug)]
162/// enum MyError {
163/// InvalidValue,
164/// }
165///
166/// fn validate(value: i32) -> Result<(), MyError> {
167/// assert_or_err!(value >= 0, MyError::InvalidValue, "Value must be non-negative");
168/// assert_or_err!(value <= 100, MyError::InvalidValue, "Out of range: {}", value);
169/// Ok(())
170/// }
171/// ```
172///
173/// # Errors
174///
175/// * Returns the specified error when the condition is false and `ENABLE_ASSERT` is not "1".
176///
177/// # Panics
178///
179/// Exits the process when the condition is false and `ENABLE_ASSERT=1`.
180#[macro_export]
181macro_rules! assert_or_err {
182 ($evaluate:expr, $err:expr, $(,)?) => {
183 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
184 && !($evaluate)
185 {
186 $crate::assert!($evaluate, "{:?}", $err)
187 } else if !($evaluate) {
188 return Err($err);
189 }
190 };
191 ($evaluate:expr, $err:expr, $($message:tt)+) => {
192 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
193 && !($evaluate)
194 {
195 $crate::assert!($evaluate, $($message)*)
196 } else if !($evaluate) {
197 return Err($err);
198 }
199 };
200}
201
202/// Conditional assertion that logs an error on failure.
203///
204/// When `ENABLE_ASSERT` environment variable is set to "1" and the condition is false,
205/// this macro exits the process with a colorized error message. When assertions are disabled
206/// and the condition is false, it logs an error message using the `log` crate instead.
207///
208/// # Environment Variables
209///
210/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
211///
212/// # Examples
213///
214/// ```rust,no_run
215/// use moosicbox_assert::assert_or_error;
216///
217/// fn process_data(data: &[u8]) {
218/// assert_or_error!(!data.is_empty(), "Cannot process empty data");
219/// assert_or_error!(data.len() < 1024, "Data too large: {} bytes", data.len());
220/// }
221/// ```
222///
223/// # Panics
224///
225/// Exits the process when the condition is false and `ENABLE_ASSERT=1`.
226#[macro_export]
227macro_rules! assert_or_error {
228 ($evaluate:expr, $($message:tt)+) => {
229 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
230 && !($evaluate)
231 {
232 $crate::assert!($evaluate, $($message)*)
233 } else if !($evaluate) {
234 log::error!($($message)*);
235 }
236 };
237}
238
239/// Conditional assertion that calls `unimplemented!()` on failure.
240///
241/// When `ENABLE_ASSERT` environment variable is set to "1" and the condition is false,
242/// this macro exits the process with a colorized error message. When assertions are disabled
243/// and the condition is false, it calls `unimplemented!()` with a colorized message instead.
244///
245/// # Environment Variables
246///
247/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
248///
249/// # Examples
250///
251/// ```rust,no_run
252/// use moosicbox_assert::assert_or_unimplemented;
253///
254/// fn experimental_feature(enabled: bool) {
255/// assert_or_unimplemented!(enabled, "Feature not yet implemented");
256/// println!("Running experimental feature");
257/// }
258/// ```
259///
260/// # Panics
261///
262/// * Exits the process when the condition is false and `ENABLE_ASSERT=1`
263/// * Calls `unimplemented!()` when the condition is false and assertions are disabled
264#[macro_export]
265macro_rules! assert_or_unimplemented {
266 ($evaluate:expr, $(,)?) => {
267 let success = ($evaluate);
268 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
269 && !success
270 {
271 $crate::assert!(success)
272 } else if !success {
273 unimplemented!(
274 "{}",
275 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
276 format!(
277 "{}\n{}",
278 $crate::Colorize::underline(format!($($message)*).as_str()),
279 std::backtrace::Backtrace::force_capture()
280 )
281 .as_str()
282 )))
283 );
284 }
285 };
286 ($evaluate:expr, $($message:tt)+) => {
287 let success = ($evaluate);
288 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
289 && !success
290 {
291 $crate::assert!(success, $($message)*)
292 } else if !success {
293 unimplemented!(
294 "{}",
295 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
296 format!(
297 "{}\n{}",
298 $crate::Colorize::underline(format!($($message)*).as_str()),
299 std::backtrace::Backtrace::force_capture()
300 )
301 .as_str()
302 )))
303 );
304 }
305 };
306}
307
308/// Conditional assertion that panics on failure.
309///
310/// When `ENABLE_ASSERT` environment variable is set to "1" and the condition is false,
311/// this macro exits the process with a colorized error message. When assertions are disabled
312/// and the condition is false, it panics with a colorized message instead.
313///
314/// # Environment Variables
315///
316/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
317///
318/// # Examples
319///
320/// ```rust,no_run
321/// use moosicbox_assert::assert_or_panic;
322///
323/// fn critical_operation(value: i32) {
324/// assert_or_panic!(value > 0, "Value must be positive, got {}", value);
325/// }
326/// ```
327///
328/// # Panics
329///
330/// * Exits the process when the condition is false and `ENABLE_ASSERT=1`
331/// * Panics when the condition is false and assertions are disabled
332#[macro_export]
333macro_rules! assert_or_panic {
334 ($evaluate:expr, $(,)?) => {{
335 let success = ($evaluate);
336 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
337 && !success
338 {
339 $crate::assert!(success)
340 } else if !success {
341 panic!(
342 "{}",
343 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
344 format!(
345 "{}\n{}",
346 $crate::Colorize::underline(format!($($message)*).as_str()),
347 std::backtrace::Backtrace::force_capture()
348 )
349 .as_str()
350 )))
351 );
352 }
353 }};
354 ($evaluate:expr, $($message:tt)+) => {{
355 let success = ($evaluate);
356 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
357 && !success
358 {
359 $crate::assert!(success, $($message)*)
360 } else if !success {
361 panic!(
362 "{}",
363 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
364 format!(
365 "{}\n{}",
366 $crate::Colorize::underline(format!($($message)*).as_str()),
367 std::backtrace::Backtrace::force_capture()
368 )
369 .as_str()
370 )))
371 );
372 }
373 }};
374}
375
376/// Unconditionally exits the process when assertions are enabled.
377///
378/// When `ENABLE_ASSERT` environment variable is set to "1", this macro exits the process
379/// with a colorized error message and stack trace. When assertions are disabled,
380/// this macro becomes a no-op.
381///
382/// # Environment Variables
383///
384/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
385///
386/// # Examples
387///
388/// ```rust,no_run
389/// use moosicbox_assert::die;
390///
391/// fn check_value(value: i32) {
392/// if value < 0 {
393/// die!("Value cannot be negative: {}", value);
394/// }
395/// }
396/// ```
397///
398/// # Panics
399///
400/// Exits the process (via `std::process::exit(1)`) when `ENABLE_ASSERT=1`.
401#[macro_export]
402macro_rules! die {
403 () => {
404 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
405 eprintln!(
406 "{}",
407 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
408 format!("{}", std::backtrace::Backtrace::force_capture()).as_str()
409 )))
410 );
411 log::logger().flush();
412 std::process::exit(1);
413 }
414 };
415 ($($message:tt)+) => {
416 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
417 eprintln!(
418 "{}",
419 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
420 format!(
421 "{}\n{}",
422 $crate::Colorize::underline(format!($($message)*).as_str()),
423 std::backtrace::Backtrace::force_capture()
424 )
425 .as_str()
426 )))
427 );
428 log::logger().flush();
429 std::process::exit(1);
430 }
431 };
432}
433
434/// Exits the process or logs a warning depending on assertion mode.
435///
436/// When `ENABLE_ASSERT` environment variable is set to "1", this macro exits the process
437/// with a colorized error message (red background). When assertions are disabled,
438/// it logs a warning message with yellow background instead.
439///
440/// # Environment Variables
441///
442/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
443///
444/// # Examples
445///
446/// ```rust,no_run
447/// use moosicbox_assert::die_or_warn;
448///
449/// fn deprecated_function() {
450/// die_or_warn!("This function is deprecated and will be removed");
451/// }
452/// ```
453///
454/// # Panics
455///
456/// Exits the process when `ENABLE_ASSERT=1`.
457#[macro_export]
458macro_rules! die_or_warn {
459 ($($message:tt)+) => {
460 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
461 eprintln!(
462 "{}",
463 $crate::Colorize::on_yellow($crate::Colorize::white($crate::Colorize::bold(
464 format!(
465 "{}\n{}",
466 $crate::Colorize::underline(format!($($message)*).as_str()),
467 std::backtrace::Backtrace::force_capture()
468 )
469 .as_str()
470 )))
471 );
472 log::logger().flush();
473 std::process::exit(1);
474 } else {
475 log::warn!(
476 "{}",
477 $crate::Colorize::on_yellow($crate::Colorize::white($crate::Colorize::bold(
478 format!(
479 "{}\n{}",
480 $crate::Colorize::underline(format!($($message)*).as_str()),
481 std::backtrace::Backtrace::force_capture()
482 )
483 .as_str()
484 )))
485 );
486 }
487 };
488}
489
490/// Exits the process or returns an error depending on assertion mode.
491///
492/// When `ENABLE_ASSERT` environment variable is set to "1", this macro exits the process
493/// with a colorized error message. When assertions are disabled, it returns the specified
494/// error value instead.
495///
496/// # Environment Variables
497///
498/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
499///
500/// # Examples
501///
502/// ```rust,no_run
503/// use moosicbox_assert::die_or_err;
504///
505/// #[derive(Debug)]
506/// enum MyError {
507/// Fatal,
508/// }
509///
510/// fn critical_check() -> Result<(), MyError> {
511/// die_or_err!(MyError::Fatal, "Critical condition failed");
512/// }
513/// ```
514///
515/// # Errors
516///
517/// * Returns the specified error when `ENABLE_ASSERT` is not "1".
518///
519/// # Panics
520///
521/// Exits the process when `ENABLE_ASSERT=1`.
522#[macro_export]
523macro_rules! die_or_err {
524 ($err:expr, $($message:tt)+) => {
525 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1"
526 {
527 $crate::die!($($message)*);
528 unreachable!();
529 } else {
530 return Err($err);
531 }
532 };
533}
534
535/// Exits the process or logs an error depending on assertion mode.
536///
537/// When `ENABLE_ASSERT` environment variable is set to "1", this macro exits the process
538/// with a colorized error message. When assertions are disabled, it logs an error message
539/// with red background instead using the `log` crate.
540///
541/// # Environment Variables
542///
543/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
544///
545/// # Examples
546///
547/// ```rust,no_run
548/// use moosicbox_assert::die_or_error;
549///
550/// fn check_invariant(valid: bool) {
551/// if !valid {
552/// die_or_error!("Invariant violation detected");
553/// }
554/// }
555/// ```
556///
557/// # Panics
558///
559/// Exits the process when `ENABLE_ASSERT=1`.
560#[macro_export]
561macro_rules! die_or_error {
562 ($($message:tt)+) => {
563 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
564 eprintln!(
565 "{}",
566 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
567 format!(
568 "{}\n{}",
569 $crate::Colorize::underline(format!($($message)*).as_str()),
570 std::backtrace::Backtrace::force_capture()
571 )
572 .as_str()
573 )))
574 );
575 log::logger().flush();
576 std::process::exit(1);
577 } else {
578 log::error!(
579 "{}",
580 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
581 format!(
582 "{}\n{}",
583 $crate::Colorize::underline(format!($($message)*).as_str()),
584 std::backtrace::Backtrace::force_capture()
585 )
586 .as_str()
587 )))
588 );
589 }
590 };
591}
592
593/// Exits the process or propagates an error depending on assertion mode.
594///
595/// When `ENABLE_ASSERT` environment variable is set to "1" and the result is an error,
596/// this macro exits the process with a colorized error message. When assertions are disabled,
597/// it propagates the error using the `?` operator instead.
598///
599/// # Environment Variables
600///
601/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
602///
603/// # Examples
604///
605/// ```rust,no_run
606/// use moosicbox_assert::die_or_propagate;
607///
608/// fn process_result() -> Result<(), String> {
609/// die_or_propagate!(Ok::<(), String>(()), "Failed to process");
610/// Ok(())
611/// }
612/// ```
613///
614/// # Errors
615///
616/// * Propagates the error from the evaluated `Result` when it is `Err` and `ENABLE_ASSERT` is not
617/// "1".
618///
619/// # Panics
620///
621/// Exits the process when the result is `Err` and `ENABLE_ASSERT=1`.
622#[macro_export]
623macro_rules! die_or_propagate {
624 ($evaluate:expr, $($message:tt)+) => {
625 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
626 match $evaluate {
627 Ok(x) => x,
628 Err(e) => $crate::die!($($message)*),
629 }
630 } else {
631 $evaluate?
632 }
633 };
634
635 ($evaluate:expr $(,)?) => {
636 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
637 match $evaluate {
638 Ok(x) => x,
639 Err(_e) => $crate::die!(),
640 }
641 } else {
642 $evaluate?
643 }
644 };
645}
646
647/// Exits the process or panics depending on assertion mode.
648///
649/// When `ENABLE_ASSERT` environment variable is set to "1", this macro exits the process
650/// with a colorized error message. When assertions are disabled, it panics with a
651/// colorized message instead.
652///
653/// # Environment Variables
654///
655/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
656///
657/// # Examples
658///
659/// ```rust,no_run
660/// use moosicbox_assert::die_or_panic;
661///
662/// fn critical_failure() {
663/// die_or_panic!("Unrecoverable error occurred");
664/// }
665/// ```
666///
667/// # Panics
668///
669/// * Exits the process when `ENABLE_ASSERT=1`
670/// * Panics when assertions are disabled
671#[macro_export]
672macro_rules! die_or_panic {
673 ($($message:tt)+) => {
674 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
675 eprintln!(
676 "{}",
677 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
678 format!(
679 "{}\n{}",
680 $crate::Colorize::underline(format!($($message)*).as_str()),
681 std::backtrace::Backtrace::force_capture()
682 )
683 .as_str()
684 )))
685 );
686 log::logger().flush();
687 std::process::exit(1);
688 } else {
689 panic!(
690 "{}",
691 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
692 format!(
693 "{}\n{}",
694 $crate::Colorize::underline(format!($($message)*).as_str()),
695 std::backtrace::Backtrace::force_capture()
696 )
697 .as_str()
698 )))
699 );
700 }
701 };
702}
703
704/// Exits the process or calls `unimplemented!()` depending on assertion mode.
705///
706/// When `ENABLE_ASSERT` environment variable is set to "1", this macro exits the process
707/// with a colorized error message. When assertions are disabled, it calls `unimplemented!()`
708/// with a colorized message instead.
709///
710/// # Environment Variables
711///
712/// * `ENABLE_ASSERT` - Set to "1" to enable assertions, any other value disables them
713///
714/// # Examples
715///
716/// ```rust,no_run
717/// use moosicbox_assert::die_or_unimplemented;
718///
719/// fn not_ready_yet() {
720/// die_or_unimplemented!("This code path is not implemented");
721/// }
722/// ```
723///
724/// # Panics
725///
726/// * Exits the process when `ENABLE_ASSERT=1`
727/// * Calls `unimplemented!()` when assertions are disabled
728#[macro_export]
729macro_rules! die_or_unimplemented {
730 ($($message:tt)+) => {
731 if $crate::moosicbox_env_utils::default_env!("ENABLE_ASSERT", "false") == "1" {
732 eprintln!(
733 "{}",
734 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
735 format!(
736 "{}\n{}",
737 $crate::Colorize::underline(format!($($message)*).as_str()),
738 std::backtrace::Backtrace::force_capture()
739 )
740 .as_str()
741 )))
742 );
743 log::logger().flush();
744 std::process::exit(1);
745 } else {
746 unimplemented!(
747 "{}",
748 $crate::Colorize::on_red($crate::Colorize::white($crate::Colorize::bold(
749 format!(
750 "{}\n{}",
751 $crate::Colorize::underline(format!($($message)*).as_str()),
752 std::backtrace::Backtrace::force_capture()
753 )
754 .as_str()
755 )))
756 );
757 }
758 };
759}
760
761#[cfg(test)]
762mod tests {
763
764 #[derive(Debug, PartialEq)]
765 enum TestError {
766 InvalidValue,
767 Critical,
768 }
769
770 // Test assert! macro with ENABLE_ASSERT disabled (no-op)
771 #[test_log::test]
772 fn test_assert_disabled_no_op() {
773 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
774 // Should not exit or do anything when condition is false
775 crate::assert!(false);
776 crate::assert!(false, "this message should not appear");
777 }
778
779 #[test_log::test]
780 fn test_assert_disabled_with_true_condition() {
781 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
782 crate::assert!(true);
783 crate::assert!(true, "condition is true");
784 }
785
786 // Test assert_or_err! macro with ENABLE_ASSERT disabled
787 #[test_log::test]
788 #[allow(clippy::items_after_statements)]
789 fn test_assert_or_err_returns_error_when_disabled() {
790 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
791
792 fn test_function(value: i32) -> Result<i32, TestError> {
793 crate::assert_or_err!(value >= 0, TestError::InvalidValue,);
794 Ok(value * 2)
795 }
796
797 let result = test_function(-5);
798 assert_eq!(result, Err(TestError::InvalidValue));
799 }
800
801 #[test_log::test]
802 #[allow(clippy::items_after_statements)]
803 fn test_assert_or_err_succeeds_with_true_condition() {
804 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
805
806 fn test_function(value: i32) -> Result<i32, TestError> {
807 crate::assert_or_err!(value >= 0, TestError::InvalidValue, "value was {}", value);
808 Ok(value * 2)
809 }
810
811 let result = test_function(5);
812 assert_eq!(result, Ok(10));
813 }
814
815 #[test_log::test]
816 #[allow(clippy::items_after_statements)]
817 fn test_assert_or_err_with_message() {
818 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
819
820 fn test_function(value: i32) -> Result<i32, TestError> {
821 crate::assert_or_err!(
822 value <= 100,
823 TestError::InvalidValue,
824 "Value {} exceeds maximum",
825 value
826 );
827 Ok(value)
828 }
829
830 let result = test_function(150);
831 assert_eq!(result, Err(TestError::InvalidValue));
832 }
833
834 // Test assert_or_error! macro with ENABLE_ASSERT disabled
835 #[test_log::test]
836 fn test_assert_or_error_logs_when_disabled() {
837 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
838
839 // This should log an error but not exit
840 crate::assert_or_error!(false, "This is a test error message");
841
842 // With formatting
843 let value = 42;
844 crate::assert_or_error!(false, "Value is {}", value);
845 }
846
847 #[test_log::test]
848 fn test_assert_or_error_succeeds_with_true_condition() {
849 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
850 crate::assert_or_error!(true, "This should not log");
851 }
852
853 // Test assert_or_panic! macro with ENABLE_ASSERT disabled
854 #[test_log::test]
855 #[should_panic(expected = "Expected panic message")]
856 fn test_assert_or_panic_panics_when_disabled() {
857 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
858 crate::assert_or_panic!(false, "Expected panic message");
859 }
860
861 #[test_log::test]
862 fn test_assert_or_panic_succeeds_with_true_condition() {
863 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
864 crate::assert_or_panic!(true, "Should not panic");
865 }
866
867 // Test assert_or_unimplemented! macro with ENABLE_ASSERT disabled
868 #[test_log::test]
869 #[should_panic(expected = "not implemented")]
870 fn test_assert_or_unimplemented_calls_unimplemented_when_disabled() {
871 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
872 crate::assert_or_unimplemented!(false, "Feature not implemented");
873 }
874
875 #[test_log::test]
876 fn test_assert_or_unimplemented_succeeds_with_true_condition() {
877 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
878 crate::assert_or_unimplemented!(true, "Should not call unimplemented");
879 }
880
881 // Test die! macro with ENABLE_ASSERT disabled (no-op)
882 #[test_log::test]
883 fn test_die_disabled_no_op() {
884 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
885 crate::die!();
886 crate::die!("This message should not exit");
887 }
888
889 // Test die_or_err! macro with ENABLE_ASSERT disabled
890 #[test_log::test]
891 #[allow(clippy::items_after_statements)]
892 fn test_die_or_err_returns_error_when_disabled() {
893 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
894
895 fn test_function() -> Result<(), TestError> {
896 crate::die_or_err!(TestError::Critical, "Critical failure");
897 }
898
899 let result = test_function();
900 assert_eq!(result, Err(TestError::Critical));
901 }
902
903 #[test_log::test]
904 #[allow(clippy::items_after_statements)]
905 fn test_die_or_err_with_formatting() {
906 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
907
908 fn test_function(code: i32) -> Result<(), TestError> {
909 crate::die_or_err!(TestError::Critical, "Error code: {}", code);
910 }
911
912 let result = test_function(500);
913 assert_eq!(result, Err(TestError::Critical));
914 }
915
916 // Test die_or_error! macro with ENABLE_ASSERT disabled
917 #[test_log::test]
918 fn test_die_or_error_logs_when_disabled() {
919 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
920 crate::die_or_error!("This is a critical error");
921 crate::die_or_error!("Error with value: {}", 42);
922 }
923
924 // Test die_or_warn! macro with ENABLE_ASSERT disabled
925 #[test_log::test]
926 fn test_die_or_warn_logs_warning_when_disabled() {
927 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
928 crate::die_or_warn!("This is a warning");
929 crate::die_or_warn!("Warning with value: {}", 100);
930 }
931
932 // Test die_or_panic! macro with ENABLE_ASSERT disabled
933 #[test_log::test]
934 #[should_panic(expected = "Expected panic")]
935 fn test_die_or_panic_panics_when_disabled() {
936 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
937 crate::die_or_panic!("Expected panic");
938 }
939
940 #[test_log::test]
941 #[should_panic(expected = "Panic with code: 404")]
942 fn test_die_or_panic_with_formatting() {
943 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
944 crate::die_or_panic!("Panic with code: {}", 404);
945 }
946
947 // Test die_or_unimplemented! macro with ENABLE_ASSERT disabled
948 #[test_log::test]
949 #[should_panic(expected = "not implemented")]
950 fn test_die_or_unimplemented_calls_unimplemented_when_disabled() {
951 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
952 crate::die_or_unimplemented!("Not implemented yet");
953 }
954
955 // Note: die_or_propagate! tests omitted due to macro expansion issues with std::process::exit
956 // The macro works correctly at runtime but has type-checking issues during test compilation
957 // when the ENABLE_ASSERT=1 branch is analyzed. Since we can't test the exit path anyway,
958 // and the macro is tested through actual usage, we skip these tests.
959
960 // Test with complex expressions and side effects
961 #[test_log::test]
962 fn test_assert_with_side_effects() {
963 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
964 let mut counter = 0;
965
966 // When disabled, the expression should NOT be evaluated
967 crate::assert!({
968 counter += 1;
969 false
970 });
971
972 // Counter should remain 0 since assertion is disabled
973 assert_eq!(counter, 0);
974 }
975
976 #[test_log::test]
977 #[allow(clippy::items_after_statements)]
978 fn test_assert_or_err_with_complex_error_types() {
979 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
980
981 #[derive(Debug, PartialEq)]
982 struct ComplexError {
983 code: i32,
984 message: String,
985 }
986
987 fn test_function() -> Result<(), ComplexError> {
988 crate::assert_or_err!(
989 false,
990 ComplexError {
991 code: 42,
992 message: "test error".to_string()
993 },
994 "Complex error test"
995 );
996 Ok(())
997 }
998
999 let result = test_function();
1000 assert!(result.is_err());
1001 if let Err(e) = result {
1002 assert_eq!(e.code, 42);
1003 assert_eq!(e.message, "test error");
1004 }
1005 }
1006
1007 // Test macro hygiene - ensure macros work with different imports
1008 #[test_log::test]
1009 #[allow(clippy::items_after_statements)]
1010 fn test_macro_works_without_explicit_imports() {
1011 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
1012
1013 fn test_function() -> Result<(), TestError> {
1014 crate::assert_or_err!(true, TestError::InvalidValue,);
1015 Ok(())
1016 }
1017
1018 assert_eq!(test_function(), Ok(()));
1019 }
1020
1021 // Test with trailing commas
1022 #[test_log::test]
1023 fn test_assert_with_trailing_comma() {
1024 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
1025 crate::assert!(true,);
1026 }
1027
1028 #[test_log::test]
1029 #[allow(clippy::items_after_statements)]
1030 fn test_assert_or_err_with_trailing_comma() {
1031 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
1032
1033 fn test_function() -> Result<(), TestError> {
1034 crate::assert_or_err!(true, TestError::InvalidValue,);
1035 Ok(())
1036 }
1037
1038 assert_eq!(test_function(), Ok(()));
1039 }
1040
1041 // Test multiple consecutive assertions
1042 #[test_log::test]
1043 #[allow(clippy::items_after_statements)]
1044 fn test_multiple_assert_or_err_in_sequence() {
1045 unsafe { std::env::set_var("ENABLE_ASSERT", "0") };
1046
1047 fn test_function(a: i32, b: i32) -> Result<i32, TestError> {
1048 crate::assert_or_err!(a >= 0, TestError::InvalidValue, "a must be non-negative");
1049 crate::assert_or_err!(b >= 0, TestError::InvalidValue, "b must be non-negative");
1050 crate::assert_or_err!(a + b <= 100, TestError::InvalidValue, "sum too large");
1051 Ok(a + b)
1052 }
1053
1054 // Should pass all assertions
1055 assert_eq!(test_function(10, 20), Ok(30));
1056
1057 // Should fail first assertion
1058 assert_eq!(test_function(-1, 20), Err(TestError::InvalidValue));
1059
1060 // Should fail second assertion
1061 assert_eq!(test_function(10, -1), Err(TestError::InvalidValue));
1062
1063 // Should fail third assertion
1064 assert_eq!(test_function(60, 50), Err(TestError::InvalidValue));
1065 }
1066}