Skip to main content

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}