tiny_bail/
lib.rs

1//! Tiny bailing convenience macros.
2//!
3//! Bailing is an error-handling pattern that takes the middle path between `unwrap` and `?`:
4//! - Compared to `unwrap`: Bailing will `return`, `continue`, or `break` instead of panicking.
5//! - Compared to `?`: Bailing will log or discard the error instead of returning it.
6//!
7//! The middle path avoids unwanted panics without the ergonomic challenges of propagating errors with `?`.
8//!
9//! This crate provides the following macro variants to determine the preferred behavior on failure:
10//! - [`or_return!`]
11//! - [`or_return_quiet!`]
12//! - [`or_return_log_once!`]
13//! - [`or_continue!`]
14//! - [`or_continue_quiet!`]
15//! - [`or_continue_log_once!`]
16//! - [`or_break!`]
17//! - [`or_break_quiet!`]
18//! - [`or_break_log_once!`]
19//!
20//! Along with their tiny aliases:
21//! [`r!`](prelude::r),
22//! [`rq!`](prelude::rq),
23//! [`ro!`](prelude::ro),
24//! [`c!`](prelude::c),
25//! [`cq!`](prelude::cq),
26//! [`co!`](prelude::co),
27//! [`b!`](prelude::b),
28//! [`bq!`](prelude::bq), and
29//! [`bo!`](prelude::bo).
30//!
31//! The macros support [`Result`], [`Option`], and [`bool`] types out of the box.
32//! Implement [`IntoResult`] to extend this to other types.
33//!
34//! # Example
35//!
36//! ```rust
37//! use tiny_bail::prelude::*;
38//!
39//! // With `tiny_bail`:
40//! fn increment_last(arr: &mut [i32]) {
41//!     *r!(arr.last_mut()) += 1;
42//! }
43//!
44//! // Without `tiny_bail`:
45//! fn increment_last_manually(arr: &mut [i32]) {
46//!     if let Some(x) = arr.last_mut() {
47//!         *x += 1;
48//!     } else {
49//!         println!("Bailed at src/example.rs:34:18: `arr.last_mut()`");
50//!         return;
51//!     }
52//! }
53//! ```
54//!
55//! # Getting started
56//!
57//! To use this crate, add it to your `Cargo.toml`:
58//!
59//! ```shell
60//! cargo add tiny_bail
61//! ```
62//!
63//! You can set features to customize the logging behavior on bail:
64//!
65//! ```shell
66//! # Log with `println!` instead of `tracing::warn!`.
67//! cargo add tiny_bail --no-default-features
68//! # Log with `log::info!` instead of `tracing::warn!`.
69//! cargo add tiny_bail --no-default-features --features log,info
70//! ```
71//!
72//! This crate has zero dependencies other than the logging backend you choose (`log`, `tracing`, or nothing).
73
74/// Re-exported macros and tiny aliases.
75///
76/// To omit tiny aliases, glob import [explicit] instead.
77///
78/// # Usage
79///
80/// ```rust
81/// use tiny_bail::prelude::*;
82/// ```
83pub mod prelude {
84    pub use super::explicit::*;
85
86    /// Tiny alias for [`or_return!`].
87    pub use or_return as r;
88
89    /// Tiny alias for [`or_return_quiet!`].
90    pub use or_return_quiet as rq;
91
92    /// Tiny alias for [`or_return_log_once!`].
93    pub use or_return_log_once as ro;
94
95    /// Tiny alias for [`or_continue!`].
96    pub use or_continue as c;
97
98    /// Tiny alias for [`or_continue_quiet!`].
99    pub use or_continue_quiet as cq;
100
101    /// Tiny alias for [`or_continue_log_once!`].
102    pub use or_continue_log_once as co;
103
104    /// Tiny alias for [`or_break!`].
105    pub use or_break as b;
106
107    /// Tiny alias for [`or_break_quiet!`].
108    pub use or_break_quiet as bq;
109
110    /// Tiny alias for [`or_break_log_once!`].
111    pub use or_break_log_once as bo;
112}
113
114/// Re-exported macros.
115///
116/// To include tiny aliases, glob import [prelude] instead.
117///
118/// # Usage
119///
120/// ```
121/// use tiny_bail::explicit::*;
122/// ```
123pub mod explicit {
124    pub use super::{
125        or_break, or_break_log_once, or_break_quiet, or_continue, or_continue_log_once,
126        or_continue_quiet, or_return, or_return_log_once, or_return_quiet,
127    };
128}
129
130// Require a sane feature combination.
131#[cfg(all(feature = "log", feature = "tracing"))]
132compile_error!("multiple log backend features are set (log, tracing)");
133#[cfg(any(
134    all(feature = "trace", feature = "debug"),
135    all(feature = "trace", feature = "info"),
136    all(feature = "trace", feature = "warn"),
137    all(feature = "trace", feature = "error"),
138    all(feature = "debug", feature = "info"),
139    all(feature = "debug", feature = "warn"),
140    all(feature = "debug", feature = "error"),
141    all(feature = "info", feature = "warn"),
142    all(feature = "info", feature = "error"),
143    all(feature = "warn", feature = "error"),
144))]
145compile_error!("multiple log level features are set (trace, debug, info, warn, error)");
146#[cfg(all(
147    any(feature = "log", feature = "tracing"),
148    not(any(
149        feature = "trace",
150        feature = "debug",
151        feature = "info",
152        feature = "warn",
153        feature = "error",
154    )),
155))]
156compile_error!(
157    "a log backend feature is set (log, tracing), but no log level feature is set (trace, debug, info, warn, error)",
158);
159#[cfg(all(
160    not(any(feature = "log", feature = "tracing")),
161    any(
162        feature = "trace",
163        feature = "debug",
164        feature = "info",
165        feature = "warn",
166        feature = "error",
167    ),
168))]
169compile_error!(
170    "a log level feature is set (trace, debug, info, warn, error), but no log backend feature is set (log, tracing)",
171);
172
173// Set the log backend.
174#[doc(hidden)]
175pub mod __log_backend {
176    #[cfg(feature = "log")]
177    pub use log::{debug, error, info, trace, warn};
178
179    #[cfg(feature = "tracing")]
180    pub use tracing::{debug, error, info, trace, warn};
181
182    #[cfg(not(any(feature = "log", feature = "tracing")))]
183    pub use std::println;
184}
185
186/// Set the log level.
187macro_rules! set_log_level {
188    ($level:ident) => {
189        /// Log the code location, expression, and error on bail.
190        #[doc(hidden)]
191        #[macro_export]
192        macro_rules! ___log_bail {
193            ($expr:expr, $err:expr) => {
194                $crate::__log_backend::$level!(
195                    "Bailed at {}:{}:{}: `{}` is `{:?}`",
196                    file!(),
197                    line!(),
198                    column!(),
199                    stringify!($expr),
200                    $err,
201                );
202            };
203        }
204
205        // Workaround for <https://github.com/rust-lang/rust/pull/52234>.
206        #[doc(hidden)]
207        pub use ___log_bail as __log_bail;
208    };
209}
210
211#[cfg(feature = "trace")]
212set_log_level!(trace);
213#[cfg(feature = "debug")]
214set_log_level!(debug);
215#[cfg(feature = "info")]
216set_log_level!(info);
217#[cfg(feature = "warn")]
218set_log_level!(warn);
219#[cfg(feature = "error")]
220set_log_level!(error);
221#[cfg(not(any(
222    feature = "trace",
223    feature = "debug",
224    feature = "info",
225    feature = "warn",
226    feature = "error",
227)))]
228set_log_level!(println);
229
230/// A trait for types that can be separated into success and failure values.
231///
232/// This trait is implemented for [`Result`], [`Option`], and [`bool`].
233pub trait IntoResult<T, E> {
234    /// Return the success or failure value as a `Result`.
235    fn into_result(self) -> Result<T, E>;
236}
237
238impl IntoResult<bool, bool> for bool {
239    fn into_result(self) -> Result<bool, bool> {
240        self.then_some(true).ok_or(false)
241    }
242}
243
244impl<T> IntoResult<T, Option<()>> for Option<T> {
245    fn into_result(self) -> Result<T, Option<()>> {
246        self.ok_or(None)
247    }
248}
249
250impl<T, E> IntoResult<T, E> for Result<T, E> {
251    fn into_result(self) -> Result<T, E> {
252        self
253    }
254}
255
256/// A helper macro to unwrap on success, or log the failure and do something else.
257#[doc(hidden)]
258#[macro_export]
259macro_rules! __unwrap_or {
260    ($expr:expr, $else:expr) => {
261        match $crate::IntoResult::into_result($expr) {
262            ::core::result::Result::Ok(x) => x,
263            ::core::result::Result::Err(__err) => {
264                $crate::__log_bail!($expr, __err);
265                $else;
266            }
267        }
268    };
269}
270
271/// Unwrap on success, or log the failure and return.
272///
273/// Returns [`Default::default()`] unless an initial argument is provided to return instead.
274#[macro_export]
275macro_rules! or_return {
276    ($return:expr, $expr:expr $(,)?) => {
277        $crate::__unwrap_or!($expr, return $return)
278    };
279
280    ($expr:expr $(,)?) => {
281        $crate::__unwrap_or!($expr, return ::core::default::Default::default())
282    };
283}
284
285/// Unwrap on success, or log the failure and continue.
286///
287/// Accepts an optional 'label as the first argument.
288#[macro_export]
289macro_rules! or_continue {
290    ($label:tt, $expr:expr $(,)?) => {
291        $crate::__unwrap_or!($expr, continue $label)
292    };
293
294    ($expr:expr $(,)?) => {
295        $crate::__unwrap_or!($expr, continue)
296    };
297}
298
299/// Unwrap on success, or log the failure and break.
300///
301/// Accepts an optional 'label as the first argument.
302#[macro_export]
303macro_rules! or_break {
304    ($label:tt, $expr:expr $(,)?) => {
305        $crate::__unwrap_or!($expr, break $label)
306    };
307
308    ($expr:expr $(,)?) => {
309        $crate::__unwrap_or!($expr, break)
310    };
311}
312
313/// A helper macro to unwrap on success, or quietly discard the failure and do something else.
314#[doc(hidden)]
315#[macro_export]
316macro_rules! __unwrap_or_quiet {
317    ($expr:expr, $else:expr) => {
318        match $crate::IntoResult::into_result($expr) {
319            ::core::result::Result::Ok(x) => x,
320            _ => {
321                $else;
322            }
323        }
324    };
325}
326
327/// Unwrap on success, or quietly discard the failure and return.
328///
329/// Returns [`Default::default()`] unless an initial argument is provided to return instead.
330#[macro_export]
331macro_rules! or_return_quiet {
332    ($return:expr, $expr:expr $(,)?) => {
333        $crate::__unwrap_or_quiet!($expr, return $return)
334    };
335
336    ($expr:expr $(,)?) => {
337        $crate::__unwrap_or_quiet!($expr, return ::core::default::Default::default())
338    };
339}
340
341/// Unwrap on success, or quietly discard the failure and continue.
342///
343/// Accepts an optional 'label as the first argument.
344#[macro_export]
345macro_rules! or_continue_quiet {
346    ($label:tt, $expr:expr $(,)?) => {
347        $crate::__unwrap_or_quiet!($expr, continue $label)
348    };
349
350    ($expr:expr $(,)?) => {
351        $crate::__unwrap_or_quiet!($expr, continue)
352    };
353}
354
355/// Unwrap on success, or quietly discard the failure and break.
356///
357/// Accepts an optional 'label as the first argument.
358#[macro_export]
359macro_rules! or_break_quiet {
360    ($label:tt, $expr:expr $(,)?) => {
361        $crate::__unwrap_or_quiet!($expr, break $label)
362    };
363
364    ($expr:expr $(,)?) => {
365        $crate::__unwrap_or_quiet!($expr, break)
366    };
367}
368
369/// A helper macro to unwrap on success, or log the first failure and do something else.
370#[doc(hidden)]
371#[macro_export]
372macro_rules! __unwrap_or_log_once {
373    ($expr:expr, $else:expr) => {
374        match $crate::IntoResult::into_result($expr) {
375            ::core::result::Result::Ok(x) => x,
376            ::core::result::Result::Err(__err) => {
377                static __SHOULD_LOG: ::core::sync::atomic::AtomicBool =
378                    ::core::sync::atomic::AtomicBool::new(true);
379                if __SHOULD_LOG.swap(false, ::core::sync::atomic::Ordering::Relaxed) {
380                    $crate::__log_bail!($expr, __err);
381                }
382                $else;
383            }
384        }
385    };
386}
387
388/// Unwrap on success, or log the first failure and return.
389///
390/// Returns [`Default::default()`] unless an initial argument is provided to return instead.
391#[macro_export]
392macro_rules! or_return_log_once {
393    ($return:expr, $expr:expr $(,)?) => {
394        $crate::__unwrap_or_log_once!($expr, return $return)
395    };
396
397    ($expr:expr $(,)?) => {
398        $crate::__unwrap_or_log_once!($expr, return ::core::default::Default::default())
399    };
400}
401
402/// Unwrap on success, or log the first failure and continue.
403///
404/// Accepts an optional 'label as the first argument.
405#[macro_export]
406macro_rules! or_continue_log_once {
407    ($label:tt, $expr:expr $(,)?) => {
408        $crate::__unwrap_or_log_once!($expr, continue $label)
409    };
410
411    ($expr:expr $(,)?) => {
412        $crate::__unwrap_or_log_once!($expr, continue)
413    };
414}
415
416/// Unwrap on success, or log the first failure and break.
417///
418/// Accepts an optional 'label as the first argument.
419#[macro_export]
420macro_rules! or_break_log_once {
421    ($label:tt, $expr:expr $(,)?) => {
422        $crate::__unwrap_or_log_once!($expr, break $label)
423    };
424
425    ($expr:expr $(,)?) => {
426        $crate::__unwrap_or_log_once!($expr, break)
427    };
428}
429
430#[cfg(test)]
431mod tests {
432    use std::fmt::Debug;
433
434    use super::IntoResult;
435
436    #[test]
437    fn r() {
438        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E>, inner: T) -> i32 {
439            assert_eq!(or_return!(outer), inner);
440            2
441        }
442
443        // Success cases should fall through.
444        let success = 2;
445        assert_eq!(bail(true, true), success);
446        assert_eq!(bail(Some(-1), -1), success);
447        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
448
449        // Failure cases should return early with the default value.
450        let failure = 0;
451        assert_eq!(bail(false, true), failure);
452        assert_eq!(bail(None, -1), failure);
453        assert_eq!(bail(Err(()), -1), failure);
454    }
455
456    #[test]
457    fn r_with_value() {
458        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E>, inner: T) -> i32 {
459            assert_eq!(or_return!(1, outer), inner);
460            2
461        }
462
463        // Success cases should fall through.
464        let success = 2;
465        assert_eq!(bail(true, true), success);
466        assert_eq!(bail(Some(-1), -1), success);
467        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
468
469        // Failure cases should return early with the provided value.
470        let failure = 1;
471        assert_eq!(bail(false, true), failure);
472        assert_eq!(bail(None, -1), failure);
473        assert_eq!(bail(Err(()), -1), failure);
474    }
475
476    #[test]
477    fn rq() {
478        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E>, inner: T) -> i32 {
479            assert_eq!(or_return_quiet!(outer), inner);
480            2
481        }
482
483        // Success cases should fall through.
484        let success = 2;
485        assert_eq!(bail(true, true), success);
486        assert_eq!(bail(Some(-1), -1), success);
487        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
488
489        // Failure cases should return early with the default value.
490        let failure = 0;
491        assert_eq!(bail(false, true), failure);
492        assert_eq!(bail(None, -1), failure);
493        assert_eq!(bail(Err(()), -1), failure);
494    }
495
496    #[test]
497    fn rq_with_value() {
498        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E>, inner: T) -> i32 {
499            assert_eq!(or_return_quiet!(1, outer), inner);
500            2
501        }
502
503        // Success cases should fall through.
504        let success = 2;
505        assert_eq!(bail(true, true), success);
506        assert_eq!(bail(Some(-1), -1), success);
507        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
508
509        // Failure cases should return early with the provided value.
510        let failure = 1;
511        assert_eq!(bail(false, true), failure);
512        assert_eq!(bail(None, -1), failure);
513        assert_eq!(bail(Err(()), -1), failure);
514    }
515
516    #[test]
517    fn ro() {
518        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E>, inner: T) -> i32 {
519            assert_eq!(or_return_log_once!(outer), inner);
520            2
521        }
522
523        // Success cases should fall through.
524        let success = 2;
525        assert_eq!(bail(true, true), success);
526        assert_eq!(bail(Some(-1), -1), success);
527        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
528
529        // Failure cases should return early with the default value.
530        let failure = 0;
531        assert_eq!(bail(false, true), failure);
532        assert_eq!(bail(None, -1), failure);
533        assert_eq!(bail(Err(()), -1), failure);
534    }
535
536    #[test]
537    fn ro_with_value() {
538        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E>, inner: T) -> i32 {
539            assert_eq!(or_return_log_once!(1, outer), inner);
540            2
541        }
542
543        // Success cases should fall through.
544        let success = 2;
545        assert_eq!(bail(true, true), success);
546        assert_eq!(bail(Some(-1), -1), success);
547        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
548
549        // Failure cases should return early with the provided value.
550        let failure = 1;
551        assert_eq!(bail(false, true), failure);
552        assert_eq!(bail(None, -1), failure);
553        assert_eq!(bail(Err(()), -1), failure);
554    }
555
556    #[test]
557    fn c() {
558        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
559            let mut val = 0;
560            '_a: for _ in 0..2 {
561                val += 1;
562                for _ in 0..2 {
563                    val += 1;
564                    assert_eq!(or_continue!(outer), inner);
565                    val += 1;
566                }
567                val += 1;
568            }
569            val
570        }
571
572        // Success cases should fall through.
573        let success = 12;
574        assert_eq!(bail(true, true), success);
575        assert_eq!(bail(Some(-1), -1), success);
576        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
577
578        // Failure cases should continue early to the inner loop.
579        let failure = 8;
580        assert_eq!(bail(false, true), failure);
581        assert_eq!(bail(None, -1), failure);
582        assert_eq!(bail(Err(()), -1), failure);
583    }
584
585    #[test]
586    fn c_with_label() {
587        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
588            let mut val = 0;
589            '_a: for _ in 0..2 {
590                val += 1;
591                for _ in 0..2 {
592                    val += 1;
593                    assert_eq!(or_continue!('_a, outer), inner);
594                    val += 1;
595                }
596                val += 1;
597            }
598            val
599        }
600
601        // Success cases should fall through.
602        let success = 12;
603        assert_eq!(bail(true, true), success);
604        assert_eq!(bail(Some(-1), -1), success);
605        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
606
607        // Failure cases should continue early to the outer loop.
608        let failure = 4;
609        assert_eq!(bail(false, true), failure);
610        assert_eq!(bail(None, -1), failure);
611        assert_eq!(bail(Err(()), -1), failure);
612    }
613
614    #[test]
615    fn cq() {
616        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
617            let mut val = 0;
618            '_a: for _ in 0..2 {
619                val += 1;
620                for _ in 0..2 {
621                    val += 1;
622                    assert_eq!(or_continue_quiet!(outer), inner);
623                    val += 1;
624                }
625                val += 1;
626            }
627            val
628        }
629
630        // Success cases should fall through.
631        let success = 12;
632        assert_eq!(bail(true, true), success);
633        assert_eq!(bail(Some(-1), -1), success);
634        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
635
636        // Failure cases should continue early to the inner loop.
637        let failure = 8;
638        assert_eq!(bail(false, true), failure);
639        assert_eq!(bail(None, -1), failure);
640        assert_eq!(bail(Err(()), -1), failure);
641    }
642
643    #[test]
644    fn cq_with_label() {
645        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
646            let mut val = 0;
647            '_a: for _ in 0..2 {
648                val += 1;
649                for _ in 0..2 {
650                    val += 1;
651                    assert_eq!(or_continue_quiet!('_a, outer), inner);
652                    val += 1;
653                }
654                val += 1;
655            }
656            val
657        }
658
659        // Success cases should fall through.
660        let success = 12;
661        assert_eq!(bail(true, true), success);
662        assert_eq!(bail(Some(-1), -1), success);
663        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
664
665        // Failure cases should continue early to the outer loop.
666        let failure = 4;
667        assert_eq!(bail(false, true), failure);
668        assert_eq!(bail(None, -1), failure);
669        assert_eq!(bail(Err(()), -1), failure);
670    }
671
672    #[test]
673    fn co() {
674        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
675            let mut val = 0;
676            '_a: for _ in 0..2 {
677                val += 1;
678                for _ in 0..2 {
679                    val += 1;
680                    assert_eq!(or_continue_log_once!(outer), inner);
681                    val += 1;
682                }
683                val += 1;
684            }
685            val
686        }
687
688        // Success cases should fall through.
689        let success = 12;
690        assert_eq!(bail(true, true), success);
691        assert_eq!(bail(Some(-1), -1), success);
692        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
693
694        // Failure cases should continue early to the inner loop.
695        let failure = 8;
696        assert_eq!(bail(false, true), failure);
697        assert_eq!(bail(None, -1), failure);
698        assert_eq!(bail(Err(()), -1), failure);
699    }
700
701    #[test]
702    fn co_with_label() {
703        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
704            let mut val = 0;
705            '_a: for _ in 0..2 {
706                val += 1;
707                for _ in 0..2 {
708                    val += 1;
709                    assert_eq!(or_continue_log_once!('_a, outer), inner);
710                    val += 1;
711                }
712                val += 1;
713            }
714            val
715        }
716
717        // Success cases should fall through.
718        let success = 12;
719        assert_eq!(bail(true, true), success);
720        assert_eq!(bail(Some(-1), -1), success);
721        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
722
723        // Failure cases should continue early to the outer loop.
724        let failure = 4;
725        assert_eq!(bail(false, true), failure);
726        assert_eq!(bail(None, -1), failure);
727        assert_eq!(bail(Err(()), -1), failure);
728    }
729    #[test]
730    fn b() {
731        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
732            let mut val = 0;
733            '_a: for _ in 0..2 {
734                val += 1;
735                for _ in 0..2 {
736                    val += 1;
737                    assert_eq!(or_break!(outer), inner);
738                    val += 1;
739                }
740                val += 1;
741            }
742            val
743        }
744
745        // Success cases should fall through.
746        let success = 12;
747        assert_eq!(bail(true, true), success);
748        assert_eq!(bail(Some(-1), -1), success);
749        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
750
751        // Failure cases should break early from the inner loop.
752        let failure = 6;
753        assert_eq!(bail(false, true), failure);
754        assert_eq!(bail(None, -1), failure);
755        assert_eq!(bail(Err(()), -1), failure);
756    }
757
758    #[test]
759    fn b_with_label() {
760        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
761            let mut val = 0;
762            '_a: for _ in 0..2 {
763                val += 1;
764                for _ in 0..2 {
765                    val += 1;
766                    assert_eq!(or_break!('_a, outer), inner);
767                    val += 1;
768                }
769                val += 1;
770            }
771            val
772        }
773
774        // Success cases should fall through.
775        let success = 12;
776        assert_eq!(bail(true, true), success);
777        assert_eq!(bail(Some(-1), -1), success);
778        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
779
780        // Failure cases should break early from the outer loop.
781        let failure = 2;
782        assert_eq!(bail(false, true), failure);
783        assert_eq!(bail(None, -1), failure);
784        assert_eq!(bail(Err(()), -1), failure);
785    }
786
787    #[test]
788    fn bq() {
789        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
790            let mut val = 0;
791            '_a: for _ in 0..2 {
792                val += 1;
793                for _ in 0..2 {
794                    val += 1;
795                    assert_eq!(or_break_quiet!(outer), inner);
796                    val += 1;
797                }
798                val += 1;
799            }
800            val
801        }
802
803        // Success cases should fall through.
804        let success = 12;
805        assert_eq!(bail(true, true), success);
806        assert_eq!(bail(Some(-1), -1), success);
807        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
808
809        // Failure cases should break early from the inner loop.
810        let failure = 6;
811        assert_eq!(bail(false, true), failure);
812        assert_eq!(bail(None, -1), failure);
813        assert_eq!(bail(Err(()), -1), failure);
814    }
815
816    #[test]
817    fn bq_with_label() {
818        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
819            let mut val = 0;
820            '_a: for _ in 0..2 {
821                val += 1;
822                for _ in 0..2 {
823                    val += 1;
824                    assert_eq!(or_break_quiet!('_a, outer), inner);
825                    val += 1;
826                }
827                val += 1;
828            }
829            val
830        }
831
832        // Success cases should fall through.
833        let success = 12;
834        assert_eq!(bail(true, true), success);
835        assert_eq!(bail(Some(-1), -1), success);
836        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
837
838        // Failure cases should break early from the outer loop.
839        let failure = 2;
840        assert_eq!(bail(false, true), failure);
841        assert_eq!(bail(None, -1), failure);
842        assert_eq!(bail(Err(()), -1), failure);
843    }
844
845    #[test]
846    fn bo() {
847        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
848            let mut val = 0;
849            '_a: for _ in 0..2 {
850                val += 1;
851                for _ in 0..2 {
852                    val += 1;
853                    assert_eq!(or_break_log_once!(outer), inner);
854                    val += 1;
855                }
856                val += 1;
857            }
858            val
859        }
860
861        // Success cases should fall through.
862        let success = 12;
863        assert_eq!(bail(true, true), success);
864        assert_eq!(bail(Some(-1), -1), success);
865        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
866
867        // Failure cases should break early from the inner loop.
868        let failure = 6;
869        assert_eq!(bail(false, true), failure);
870        assert_eq!(bail(None, -1), failure);
871        assert_eq!(bail(Err(()), -1), failure);
872    }
873
874    #[test]
875    fn bo_with_label() {
876        fn bail<T: Eq + Debug, E: Debug>(outer: impl IntoResult<T, E> + Copy, inner: T) -> i32 {
877            let mut val = 0;
878            '_a: for _ in 0..2 {
879                val += 1;
880                for _ in 0..2 {
881                    val += 1;
882                    assert_eq!(or_break_log_once!('_a, outer), inner);
883                    val += 1;
884                }
885                val += 1;
886            }
887            val
888        }
889
890        // Success cases should fall through.
891        let success = 12;
892        assert_eq!(bail(true, true), success);
893        assert_eq!(bail(Some(-1), -1), success);
894        assert_eq!(bail(Ok::<_, ()>(-1), -1), success);
895
896        // Failure cases should break early from the outer loop.
897        let failure = 2;
898        assert_eq!(bail(false, true), failure);
899        assert_eq!(bail(None, -1), failure);
900        assert_eq!(bail(Err(()), -1), failure);
901    }
902}