snafu/lib.rs
1#![deny(missing_docs)]
2#![allow(stable_features)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![no_std]
5#![cfg_attr(
6 feature = "unstable-provider-api",
7 feature(error_generic_member_access)
8)]
9#![cfg_attr(feature = "unstable-try-trait", feature(try_trait_v2))]
10
11//! # SNAFU
12//!
13//! SNAFU is a library to easily generate errors and add information
14//! to underlying errors, especially when the same underlying error
15//! type can occur in different contexts.
16//!
17//! For detailed information, please see the [`Snafu`][] macro and the
18//! [user's guide](guide).
19//!
20//! ## Features
21//!
22//! - [Turnkey errors based on strings](Whatever)
23//! - [Custom error types](Snafu)
24//! - Including a conversion path from turnkey errors
25//! - [Backtraces](Backtrace)
26//! - Extension traits for
27//! - [`Results`](ResultExt)
28//! - [`Options`](OptionExt)
29#")]
30#")]
31//! - [Error reporting](#reporting)
32//! - Suitable for libraries and applications
33//! - `no_std` compatibility
34//! - Generic types and lifetimes
35//!
36//! ## Quick start
37//!
38//! If you want to report errors without hassle, start with the
39//! [`Whatever`][] type and the [`whatever!`][] macro:
40//!
41//! ```rust
42//! use snafu::{prelude::*, Whatever};
43//!
44//! fn is_valid_id(id: u16) -> Result<(), Whatever> {
45//! if id < 10 {
46//! whatever!("ID may not be less than 10, but it was {id}");
47//! }
48//! Ok(())
49//! }
50//! ```
51//!
52//! You can also use it to wrap any other error:
53//!
54//! ```rust
55//! use snafu::{prelude::*, Whatever};
56//!
57//! fn read_config_file(path: &str) -> Result<String, Whatever> {
58//! std::fs::read_to_string(path)
59//! .with_whatever_context(|_| format!("Could not read file {path}"))
60//! }
61//! ```
62//!
63//! [`Whatever`][] allows for a short message and tracks a
64//! [`Backtrace`][] for every error:
65//!
66//! ```rust
67//! use snafu::{prelude::*, ErrorCompat, Whatever};
68//!
69//! # fn returns_an_error() -> Result<(), Whatever> { Ok(()) }
70//! if let Err(e) = returns_an_error() {
71//! eprintln!("An error occurred: {e}");
72//! if let Some(bt) = ErrorCompat::backtrace(&e) {
73//! # #[cfg(not(feature = "backtraces-impl-backtrace-crate"))]
74//! eprintln!("{bt}");
75//! }
76//! }
77//! ```
78//!
79//! ## Custom error types
80//!
81//! Many projects will hit limitations of the `Whatever` type. When
82//! that occurs, it's time to create your own error type by deriving
83//! [`Snafu`][]!
84//!
85//! ### Struct style
86//!
87//! SNAFU will read your error struct definition and create a *context
88//! selector* type (called `InvalidIdSnafu` in this example). These
89//! context selectors are used with the [`ensure!`][] macro to provide
90//! ergonomic error creation:
91//!
92//! ```rust
93//! use snafu::prelude::*;
94//!
95//! #[derive(Debug, Snafu)]
96//! #[snafu(display("ID may not be less than 10, but it was {id}"))]
97//! struct InvalidIdError {
98//! id: u16,
99//! }
100//!
101//! fn is_valid_id(id: u16) -> Result<(), InvalidIdError> {
102//! ensure!(id >= 10, InvalidIdSnafu { id });
103//! Ok(())
104//! }
105//! ```
106//!
107//! If you add a `source` field to your error, you can then wrap an
108//! underlying error using the [`context`](ResultExt::context)
109//! extension method:
110//!
111//! ```rust
112//! use snafu::prelude::*;
113//!
114//! #[derive(Debug, Snafu)]
115//! #[snafu(display("Could not read file {path}"))]
116//! struct ConfigFileError {
117//! source: std::io::Error,
118//! path: String,
119//! }
120//!
121//! fn read_config_file(path: &str) -> Result<String, ConfigFileError> {
122//! std::fs::read_to_string(path).context(ConfigFileSnafu { path })
123//! }
124//! ```
125//!
126//! ### Enum style
127//!
128//! While error structs are good for constrained cases, they don't
129//! allow for reporting multiple possible kinds of errors at one
130//! time. Error enums solve that problem.
131//!
132//! SNAFU will read your error enum definition and create a *context
133//! selector* type for each variant (called `InvalidIdSnafu` in this
134//! example). These context selectors are used with the [`ensure!`][]
135//! macro to provide ergonomic error creation:
136//!
137//! ```rust
138//! use snafu::prelude::*;
139//!
140//! #[derive(Debug, Snafu)]
141//! enum Error {
142//! #[snafu(display("ID may not be less than 10, but it was {id}"))]
143//! InvalidId { id: u16 },
144//! }
145//!
146//! fn is_valid_id(id: u16) -> Result<(), Error> {
147//! ensure!(id >= 10, InvalidIdSnafu { id });
148//! Ok(())
149//! }
150//! ```
151//!
152//! If you add a `source` field to a variant, you can then wrap an
153//! underlying error using the [`context`](ResultExt::context)
154//! extension method:
155//!
156//! ```rust
157//! use snafu::prelude::*;
158//!
159//! #[derive(Debug, Snafu)]
160//! enum Error {
161//! #[snafu(display("Could not read file {path}"))]
162//! ConfigFile {
163//! source: std::io::Error,
164//! path: String,
165//! },
166//! }
167//!
168//! fn read_config_file(path: &str) -> Result<String, Error> {
169//! std::fs::read_to_string(path).context(ConfigFileSnafu { path })
170//! }
171//! ```
172//!
173//! You can combine the power of the [`whatever!`][] macro with an
174//! enum error type. This is great if you started out with
175//! [`Whatever`][] and are moving to a custom error type:
176//!
177//! ```rust
178//! use snafu::prelude::*;
179//!
180//! #[derive(Debug, Snafu)]
181//! enum Error {
182//! #[snafu(display("ID may not be less than 10, but it was {id}"))]
183//! InvalidId { id: u16 },
184//!
185//! #[snafu(whatever, display("{message}"))]
186//! Whatever {
187//! message: String,
188//! #[snafu(source(from(Box<dyn std::error::Error>, Some)))]
189//! source: Option<Box<dyn std::error::Error>>,
190//! },
191//! }
192//!
193//! fn is_valid_id(id: u16) -> Result<(), Error> {
194//! ensure!(id >= 10, InvalidIdSnafu { id });
195//! whatever!("Just kidding... this function always fails!");
196//! Ok(())
197//! }
198//! ```
199//!
200//! You may wish to make the type `Send` and/or `Sync`, allowing
201//! your error type to be used in multithreaded programs, by changing
202//! `dyn std::error::Error` to `dyn std::error::Error + Send + Sync`.
203//!
204//! ## Reporting
205//!
206//! Printing an error via [`Display`][]
207//! will only show the top-level error message without the underlying sources.
208//! For an extended error report,
209//! SNAFU offers a user-friendly error output mechanism.
210//! It prints the main error and all underlying errors in the chain,
211//! from the most recent to the oldest,
212//! plus the [backtrace](Backtrace) if applicable.
213//! This is done by using the [`macro@report`] procedural macro
214//! or the [`Report`] type directly.
215//!
216//! ```no_run
217//! use snafu::prelude::*;
218//!
219//! #[derive(Debug, Snafu)]
220//! #[snafu(display("Could not load configuration file {path}"))]
221//! struct ConfigFileError {
222//! source: std::io::Error,
223//! path: String,
224//! }
225//!
226//! fn read_config_file(path: &str) -> Result<String, ConfigFileError> {
227//! std::fs::read_to_string(path).context(ConfigFileSnafu { path })
228//! }
229//!
230//! #[snafu::report]
231//! fn main() -> Result<(), ConfigFileError> {
232//! read_config_file("bad-config.ini")?;
233//! Ok(())
234//! }
235//! ```
236//!
237//! This will print:
238//!
239//! ```none
240//! Error: Could not load configuration file bad-config.ini
241//!
242//! Caused by this error:
243//! 1: No such file or directory (os error 2)
244//! ```
245//!
246//! Which shows the underlying errors, unlike [`Display`]:
247//!
248//! ```none
249//! Error: Could not load configuration file bad-config.ini
250//! ```
251//!
252//! ... and is also more readable than the [`Debug`] output:
253//!
254//! ```none
255//! Error: ConfigFileError { source: Os { code: 2, kind: NotFound, message: "No such file or directory" }, path: "bad-config.ini" }
256//! ```
257//!
258//! [`Display`]: core::fmt::Display
259//! [`Debug`]: core::fmt::Debug
260//!
261//! ## Next steps
262//!
263//! Read the documentation for the [`Snafu`][] macro to see all of the
264//! capabilities, then read the [user's guide](guide) for deeper
265//! understanding.
266
267#[cfg(feature = "alloc")]
268extern crate alloc;
269#[cfg(feature = "alloc")]
270use alloc::{boxed::Box, string::String};
271
272#[cfg(feature = "std")]
273extern crate std;
274
275pub mod prelude {
276 //! Traits and macros used by most projects. Add `use
277 //! snafu::prelude::*` to your code to quickly get started with
278 //! SNAFU.
279
280 pub use crate::{ensure, OptionExt as _, ResultExt as _};
281
282 // https://github.com/rust-lang/rust/issues/89020
283 #[doc = include_str!("Snafu.md")]
284 // Links are reported as broken, but don't appear to be
285 #[allow(rustdoc::broken_intra_doc_links)]
286 pub use snafu_derive::Snafu;
287
288 #[cfg(any(feature = "alloc", test))]
289 pub use crate::{ensure_whatever, whatever};
290
291 #[cfg(feature = "futures")]
292 pub use crate::futures::{TryFutureExt as _, TryStreamExt as _};
293}
294
295#[cfg(not(any(feature = "std", feature = "backtraces-impl-backtrace-crate")))]
296#[path = "backtrace_impl_inert.rs"]
297mod backtrace_impl;
298
299#[cfg(feature = "backtraces-impl-backtrace-crate")]
300#[path = "backtrace_impl_backtrace_crate.rs"]
301mod backtrace_impl;
302
303#[cfg(all(feature = "std", not(feature = "backtraces-impl-backtrace-crate")))]
304#[path = "backtrace_impl_std.rs"]
305mod backtrace_impl;
306
307pub use backtrace_impl::*;
308
309#[cfg(any(feature = "std", test))]
310mod once_bool;
311
312#[cfg(feature = "futures")]
313pub mod futures;
314
315mod error_chain;
316pub use crate::error_chain::*;
317
318mod report;
319#[cfg(feature = "alloc")]
320pub use report::CleanedErrorText;
321pub use report::{__InternalExtractErrorType, Report};
322
323#[doc = include_str!("Snafu.md")]
324#[doc(alias(
325 "backtrace",
326 "context",
327 "crate_root",
328 "display",
329 "implicit",
330 "module",
331 "provide",
332 "source",
333 "transparent",
334 "visibility",
335 "whatever",
336))]
337pub use snafu_derive::Snafu;
338
339#[doc = include_str!("report.md")]
340pub use snafu_derive::report;
341
342macro_rules! generate_guide {
343 (pub mod $name:ident { $($children:tt)* } $($rest:tt)*) => {
344 generate_guide!(@gen ".", pub mod $name { $($children)* } $($rest)*);
345 };
346 (@gen $prefix:expr, ) => {};
347 (@gen $prefix:expr, pub mod $name:ident; $($rest:tt)*) => {
348 generate_guide!(@gen $prefix, pub mod $name { } $($rest)*);
349 };
350 (@gen $prefix:expr, @code pub mod $name:ident; $($rest:tt)*) => {
351 #[cfg(feature = "guide")]
352 pub mod $name;
353
354 #[cfg(not(feature = "guide"))]
355 /// Not currently built; please add the `guide` feature flag.
356 pub mod $name {}
357
358 generate_guide!(@gen $prefix, $($rest)*);
359 };
360 (@gen $prefix:expr, pub mod $name:ident { $($children:tt)* } $($rest:tt)*) => {
361 #[cfg(feature = "guide")]
362 #[doc = include_str!(concat!($prefix, "/", stringify!($name), ".md"))]
363 pub mod $name {
364 use crate::*;
365 generate_guide!(@gen concat!($prefix, "/", stringify!($name)), $($children)*);
366 }
367 #[cfg(not(feature = "guide"))]
368 /// Not currently built; please add the `guide` feature flag.
369 pub mod $name {
370 generate_guide!(@gen concat!($prefix, "/", stringify!($name)), $($children)*);
371 }
372
373 generate_guide!(@gen $prefix, $($rest)*);
374 };
375}
376
377generate_guide! {
378 pub mod guide {
379 pub mod comparison {
380 pub mod failure;
381 }
382 pub mod compatibility;
383 pub mod feature_flags;
384 pub mod generics;
385 pub mod opaque;
386 pub mod philosophy;
387 pub mod structs;
388 pub mod what_code_is_generated;
389 pub mod troubleshooting {
390 pub mod missing_field_source;
391 }
392 pub mod upgrading;
393
394 @code pub mod examples;
395 }
396}
397
398#[cfg(feature = "rust_1_81")]
399#[doc(hidden)]
400pub use core::error;
401
402#[cfg(feature = "rust_1_81")]
403#[doc(hidden)]
404pub use core::error::Error;
405
406#[cfg(all(not(feature = "rust_1_81"), any(feature = "std", test)))]
407#[doc(hidden)]
408pub use std::error;
409
410#[cfg(all(not(feature = "rust_1_81"), any(feature = "std", test)))]
411#[doc(hidden)]
412pub use std::error::Error;
413
414#[cfg(not(any(feature = "rust_1_81", feature = "std", test)))]
415mod fallback_error;
416#[cfg(not(any(feature = "rust_1_81", feature = "std", test)))]
417#[doc(hidden)]
418pub use fallback_error::Error;
419
420#[cfg(any(feature = "alloc", test))]
421mod boxed_impls;
422
423#[cfg(any(feature = "alloc", test))]
424mod whatever;
425#[cfg(any(feature = "alloc", test))]
426pub use whatever::*;
427
428/// Ensure a condition is true. If it is not, return from the function
429/// with an error.
430///
431/// ## Examples
432///
433/// ```rust
434/// use snafu::prelude::*;
435///
436/// #[derive(Debug, Snafu)]
437/// enum Error {
438/// InvalidUser { user_id: i32 },
439/// }
440///
441/// fn example(user_id: i32) -> Result<(), Error> {
442/// ensure!(user_id > 0, InvalidUserSnafu { user_id });
443/// // After this point, we know that `user_id` is positive.
444/// let user_id = user_id as u32;
445/// Ok(())
446/// }
447/// ```
448#[macro_export]
449macro_rules! ensure {
450 ($predicate:expr, $context_selector:expr $(,)?) => {
451 if !$predicate {
452 return $context_selector
453 .fail()
454 .map_err(::core::convert::Into::into);
455 }
456 };
457}
458
459#[cfg(feature = "alloc")]
460#[doc(hidden)]
461pub use alloc::format as __format;
462
463/// Instantiate and return a stringly-typed error message.
464///
465/// This can be used with the provided [`Whatever`][] type or with a
466/// custom error type that uses `snafu(whatever)`.
467///
468/// # Without an underlying error
469///
470/// Provide a format string and any optional arguments. The macro will
471/// unconditionally exit the calling function with an error.
472///
473/// ## Examples
474///
475/// ```rust
476/// use snafu::{Whatever, prelude::*};
477///
478/// type Result<T, E = Whatever> = std::result::Result<T, E>;
479///
480/// enum Status {
481/// Sleeping,
482/// Chilling,
483/// Working,
484/// }
485///
486/// # fn stand_up() {}
487/// # fn go_downstairs() {}
488/// fn do_laundry(status: Status, items: u8) -> Result<()> {
489/// match status {
490/// Status::Sleeping => whatever!("Cannot launder {items} clothes when I am asleep"),
491/// Status::Chilling => {
492/// stand_up();
493/// go_downstairs();
494/// }
495/// Status::Working => {
496/// go_downstairs();
497/// }
498/// }
499/// Ok(())
500/// }
501/// ```
502///
503/// # With an underlying error
504///
505/// Provide a `Result` as the first argument, followed by a format
506/// string and any optional arguments. If the `Result` is an error,
507/// the formatted string will be appended to the error and the macro
508/// will exit the calling function with an error. If the `Result` is
509/// not an error, the macro will evaluate to the `Ok` value of the
510/// `Result`.
511///
512/// ## Examples
513///
514/// ```rust
515/// use snafu::prelude::*;
516///
517/// #[derive(Debug, Snafu)]
518/// #[snafu(whatever, display("Error was: {message}"))]
519/// struct Error {
520/// message: String,
521/// #[snafu(source(from(Box<dyn std::error::Error>, Some)))]
522/// source: Option<Box<dyn std::error::Error>>,
523/// }
524/// type Result<T, E = Error> = std::result::Result<T, E>;
525///
526/// fn calculate_brightness_factor() -> Result<u8> {
527/// let angle = calculate_angle_of_refraction();
528/// let angle = whatever!(angle, "There was no angle");
529/// Ok(angle * 2)
530/// }
531///
532/// fn calculate_angle_of_refraction() -> Result<u8> {
533/// whatever!("The programmer forgot to implement this...");
534/// }
535/// ```
536#[macro_export]
537#[cfg(any(feature = "alloc", test))]
538macro_rules! whatever {
539 ($fmt:literal$(, $($arg:expr),* $(,)?)?) => {
540 return core::result::Result::Err({
541 $crate::FromString::without_source(
542 $crate::__format!($fmt$(, $($arg),*)*),
543 )
544 })
545 };
546 ($source:expr, $fmt:literal$(, $($arg:expr),* $(,)?)*) => {
547 match $source {
548 core::result::Result::Ok(v) => v,
549 core::result::Result::Err(e) => {
550 return core::result::Result::Err({
551 $crate::FromString::with_source(
552 core::convert::Into::into(e),
553 $crate::__format!($fmt$(, $($arg),*)*),
554 )
555 });
556 }
557 }
558 };
559}
560
561/// Ensure a condition is true. If it is not, return a stringly-typed
562/// error message.
563///
564/// This can be used with the provided [`Whatever`][] type or with a
565/// custom error type that uses `snafu(whatever)`.
566///
567/// ## Examples
568///
569/// ```rust
570/// use snafu::prelude::*;
571///
572/// #[derive(Debug, Snafu)]
573/// #[snafu(whatever, display("Error was: {message}"))]
574/// struct Error {
575/// message: String,
576/// }
577/// type Result<T, E = Error> = std::result::Result<T, E>;
578///
579/// fn get_bank_account_balance(account_id: &str) -> Result<u8> {
580/// # fn moon_is_rising() -> bool { false }
581/// ensure_whatever!(
582/// moon_is_rising(),
583/// "We are recalibrating the dynamos for account {account_id}, sorry",
584/// );
585///
586/// Ok(100)
587/// }
588/// ```
589#[macro_export]
590#[cfg(any(feature = "alloc", test))]
591macro_rules! ensure_whatever {
592 ($predicate:expr, $fmt:literal$(, $($arg:expr),* $(,)?)?) => {
593 if !$predicate {
594 $crate::whatever!($fmt$(, $($arg),*)*);
595 }
596 };
597}
598
599/// Additions to [`Result`][].
600pub trait ResultExt<T, E>: Sized {
601 /// Extend a [`Result`]'s error with additional context-sensitive information.
602 ///
603 /// [`Result`]: std::result::Result
604 ///
605 /// ```rust
606 /// use snafu::prelude::*;
607 ///
608 /// #[derive(Debug, Snafu)]
609 /// enum Error {
610 /// Authenticating {
611 /// user_name: String,
612 /// user_id: i32,
613 /// source: ApiError,
614 /// },
615 /// }
616 ///
617 /// fn example() -> Result<(), Error> {
618 /// another_function().context(AuthenticatingSnafu {
619 /// user_name: "admin",
620 /// user_id: 42,
621 /// })?;
622 /// Ok(())
623 /// }
624 ///
625 /// # type ApiError = Box<dyn std::error::Error>;
626 /// fn another_function() -> Result<i32, ApiError> {
627 /// /* ... */
628 /// # Ok(42)
629 /// }
630 /// ```
631 ///
632 /// Note that the context selector will call [`Into::into`][] on each field,
633 /// so the types are not required to exactly match.
634 fn context<C, E2>(self, context: C) -> Result<T, E2>
635 where
636 C: IntoError<E2, Source = E>,
637 E2: Error + ErrorCompat;
638
639 /// Extend a [`Result`][]'s error with lazily-generated context-sensitive information.
640 ///
641 /// [`Result`]: std::result::Result
642 ///
643 /// ```rust
644 /// use snafu::prelude::*;
645 ///
646 /// #[derive(Debug, Snafu)]
647 /// enum Error {
648 /// Authenticating {
649 /// user_name: String,
650 /// user_id: i32,
651 /// source: ApiError,
652 /// },
653 /// }
654 ///
655 /// fn example() -> Result<(), Error> {
656 /// another_function().with_context(|_| AuthenticatingSnafu {
657 /// user_name: "admin".to_string(),
658 /// user_id: 42,
659 /// })?;
660 /// Ok(())
661 /// }
662 ///
663 /// # type ApiError = std::io::Error;
664 /// fn another_function() -> Result<i32, ApiError> {
665 /// /* ... */
666 /// # Ok(42)
667 /// }
668 /// ```
669 ///
670 /// Note that this *may not* be needed in many cases because the context
671 /// selector will call [`Into::into`][] on each field.
672 fn with_context<F, C, E2>(self, context: F) -> Result<T, E2>
673 where
674 F: FnOnce(&mut E) -> C,
675 C: IntoError<E2, Source = E>,
676 E2: Error + ErrorCompat;
677
678 /// Extend a [`Result`]'s error with information from a string.
679 ///
680 /// The target error type must implement [`FromString`] by using
681 /// the
682 /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors]
683 /// attribute. The premade [`Whatever`] type is also available.
684 ///
685 /// In many cases, you will want to use
686 /// [`with_whatever_context`][Self::with_whatever_context] instead
687 /// as it gives you access to the error and is only called in case
688 /// of error. This method is best suited for when you have a
689 /// string literal.
690 ///
691 /// ```rust
692 /// use snafu::{prelude::*, Whatever};
693 ///
694 /// fn example() -> Result<(), Whatever> {
695 /// std::fs::read_to_string("/this/does/not/exist")
696 /// .whatever_context("couldn't open the file")?;
697 /// Ok(())
698 /// }
699 ///
700 /// let err = example().unwrap_err();
701 /// assert_eq!("couldn't open the file", err.to_string());
702 /// ```
703 #[cfg(any(feature = "alloc", test))]
704 fn whatever_context<S, E2>(self, context: S) -> Result<T, E2>
705 where
706 S: Into<String>,
707 E2: FromString,
708 E: Into<E2::Source>;
709
710 /// Extend a [`Result`]'s error with information from a
711 /// lazily-generated string.
712 ///
713 /// The target error type must implement [`FromString`] by using
714 /// the
715 /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors]
716 /// attribute. The premade [`Whatever`] type is also available.
717 ///
718 /// ```rust
719 /// use snafu::{prelude::*, Whatever};
720 ///
721 /// fn example() -> Result<(), Whatever> {
722 /// let filename = "/this/does/not/exist";
723 /// std::fs::read_to_string(filename)
724 /// .with_whatever_context(|_| format!("couldn't open the file {filename}"))?;
725 /// Ok(())
726 /// }
727 ///
728 /// let err = example().unwrap_err();
729 /// assert_eq!(
730 /// "couldn't open the file /this/does/not/exist",
731 /// err.to_string(),
732 /// );
733 /// ```
734 ///
735 /// The closure is not called when the `Result` is `Ok`:
736 ///
737 /// ```rust
738 /// use snafu::{prelude::*, Whatever};
739 ///
740 /// let value: std::io::Result<i32> = Ok(42);
741 /// let result = value.with_whatever_context::<_, String, Whatever>(|_| {
742 /// panic!("This block will not be evaluated");
743 /// });
744 ///
745 /// assert!(result.is_ok());
746 /// ```
747 #[cfg(any(feature = "alloc", test))]
748 fn with_whatever_context<F, S, E2>(self, context: F) -> Result<T, E2>
749 where
750 F: FnOnce(&mut E) -> S,
751 S: Into<String>,
752 E2: FromString,
753 E: Into<E2::Source>;
754
755 /// Convert a [`Result`]'s error into a boxed trait object
756 /// compatible with multiple threads.
757 ///
758 /// This is useful when you have errors of multiple types that you
759 /// wish to treat as one type. This may occur when dealing with
760 /// errors in a generic context, such as when the error is a
761 /// trait's associated type.
762 ///
763 /// In cases like this, you cannot name the original error type
764 /// without making the outer error type generic as well. Using an
765 /// error trait object offers an alternate solution.
766 ///
767 /// ```rust
768 /// # use std::convert::TryInto;
769 /// use snafu::prelude::*;
770 ///
771 /// fn convert_value_into_u8<V>(v: V) -> Result<u8, ConversionFailedError>
772 /// where
773 /// V: TryInto<u8>,
774 /// V::Error: snafu::Error + Send + Sync + 'static,
775 /// {
776 /// v.try_into().boxed().context(ConversionFailedSnafu)
777 /// }
778 ///
779 /// #[derive(Debug, Snafu)]
780 /// struct ConversionFailedError {
781 /// source: Box<dyn snafu::Error + Send + Sync + 'static>,
782 /// }
783 /// ```
784 ///
785 /// ## Avoiding misapplication
786 ///
787 /// We recommended **against** using this to create fewer error
788 /// variants which in turn would group unrelated errors. While
789 /// convenient for the programmer, doing so usually makes lower
790 /// quality error messages for the user.
791 ///
792 /// ```rust
793 /// use snafu::prelude::*;
794 /// use std::fs;
795 ///
796 /// fn do_not_do_this() -> Result<i32, UselessError> {
797 /// let content = fs::read_to_string("/path/to/config/file")
798 /// .boxed()
799 /// .context(UselessSnafu)?;
800 /// content.parse().boxed().context(UselessSnafu)
801 /// }
802 ///
803 /// #[derive(Debug, Snafu)]
804 /// struct UselessError {
805 /// source: Box<dyn snafu::Error + Send + Sync + 'static>,
806 /// }
807 /// ```
808 #[cfg(any(feature = "alloc", test))]
809 fn boxed<'a>(self) -> Result<T, Box<dyn Error + Send + Sync + 'a>>
810 where
811 E: Error + Send + Sync + 'a;
812
813 /// Convert a [`Result`]'s error into a boxed trait object.
814 ///
815 /// This is useful when you have errors of multiple types that you
816 /// wish to treat as one type. This may occur when dealing with
817 /// errors in a generic context, such as when the error is a
818 /// trait's associated type.
819 ///
820 /// In cases like this, you cannot name the original error type
821 /// without making the outer error type generic as well. Using an
822 /// error trait object offers an alternate solution.
823 ///
824 /// ```rust
825 /// # use std::convert::TryInto;
826 /// use snafu::prelude::*;
827 ///
828 /// fn convert_value_into_u8<V>(v: V) -> Result<u8, ConversionFailedError>
829 /// where
830 /// V: TryInto<u8>,
831 /// V::Error: snafu::Error + 'static,
832 /// {
833 /// v.try_into().boxed_local().context(ConversionFailedSnafu)
834 /// }
835 ///
836 /// #[derive(Debug, Snafu)]
837 /// struct ConversionFailedError {
838 /// source: Box<dyn snafu::Error + 'static>,
839 /// }
840 /// ```
841 ///
842 /// ## Avoiding misapplication
843 ///
844 /// We recommended **against** using this to create fewer error
845 /// variants which in turn would group unrelated errors. While
846 /// convenient for the programmer, doing so usually makes lower
847 /// quality error messages for the user.
848 ///
849 /// ```rust
850 /// use snafu::prelude::*;
851 /// use std::fs;
852 ///
853 /// fn do_not_do_this() -> Result<i32, UselessError> {
854 /// let content = fs::read_to_string("/path/to/config/file")
855 /// .boxed_local()
856 /// .context(UselessSnafu)?;
857 /// content.parse().boxed_local().context(UselessSnafu)
858 /// }
859 ///
860 /// #[derive(Debug, Snafu)]
861 /// struct UselessError {
862 /// source: Box<dyn snafu::Error + 'static>,
863 /// }
864 /// ```
865 #[cfg(any(feature = "alloc", test))]
866 fn boxed_local<'a>(self) -> Result<T, Box<dyn Error + 'a>>
867 where
868 E: Error + 'a;
869
870 /// Unwrap this result, returning the contained [`Ok`] value.
871 ///
872 /// # Panics
873 ///
874 /// Panics if the value is an [`Err`], after printing the error as a
875 /// [`Report`]. Unlike [`Result::unwrap`], which formats the error with
876 /// its `Debug` implementation, this formats the error and its source
877 /// chain with their `Display` implementation, which is usually the more
878 /// readable choice when reporting an error to a person.
879 ///
880 /// ```rust
881 /// use snafu::prelude::*;
882 ///
883 /// #[derive(Debug, Snafu)]
884 /// struct PlaceholderError;
885 ///
886 /// fn may_succeed() -> Result<u8, PlaceholderError> {
887 /// Ok(42)
888 /// }
889 ///
890 /// let value = may_succeed().unwrap_report();
891 /// assert_eq!(value, 42);
892 /// ```
893 ///
894 /// [`Ok`]: std::result::Result::Ok
895 /// [`Err`]: std::result::Result::Err
896 /// [`Result::unwrap`]: std::result::Result::unwrap
897 /// [`Report`]: crate::Report
898 #[track_caller]
899 fn unwrap_report(self) -> T
900 where
901 E: Error;
902}
903
904impl<T, E> ResultExt<T, E> for Result<T, E> {
905 #[track_caller]
906 fn context<C, E2>(self, context: C) -> Result<T, E2>
907 where
908 C: IntoError<E2, Source = E>,
909 E2: Error + ErrorCompat,
910 {
911 // https://github.com/rust-lang/rust/issues/74042
912 match self {
913 Ok(v) => Ok(v),
914 Err(error) => Err(context.into_error(error)),
915 }
916 }
917
918 #[track_caller]
919 fn with_context<F, C, E2>(self, context: F) -> Result<T, E2>
920 where
921 F: FnOnce(&mut E) -> C,
922 C: IntoError<E2, Source = E>,
923 E2: Error + ErrorCompat,
924 {
925 // https://github.com/rust-lang/rust/issues/74042
926 match self {
927 Ok(v) => Ok(v),
928 Err(mut error) => {
929 let context = context(&mut error);
930 Err(context.into_error(error))
931 }
932 }
933 }
934
935 #[cfg(any(feature = "alloc", test))]
936 #[track_caller]
937 fn whatever_context<S, E2>(self, context: S) -> Result<T, E2>
938 where
939 S: Into<String>,
940 E2: FromString,
941 E: Into<E2::Source>,
942 {
943 // https://github.com/rust-lang/rust/issues/74042
944 match self {
945 Ok(v) => Ok(v),
946 Err(error) => Err(FromString::with_source(error.into(), context.into())),
947 }
948 }
949
950 #[cfg(any(feature = "alloc", test))]
951 #[track_caller]
952 fn with_whatever_context<F, S, E2>(self, context: F) -> Result<T, E2>
953 where
954 F: FnOnce(&mut E) -> S,
955 S: Into<String>,
956 E2: FromString,
957 E: Into<E2::Source>,
958 {
959 // https://github.com/rust-lang/rust/issues/74042
960 match self {
961 Ok(t) => Ok(t),
962 Err(mut e) => {
963 let context = context(&mut e);
964 Err(FromString::with_source(e.into(), context.into()))
965 }
966 }
967 }
968
969 #[cfg(any(feature = "alloc", test))]
970 fn boxed<'a>(self) -> Result<T, Box<dyn Error + Send + Sync + 'a>>
971 where
972 E: Error + Send + Sync + 'a,
973 {
974 self.map_err(|e| Box::new(e) as _)
975 }
976
977 #[cfg(any(feature = "alloc", test))]
978 fn boxed_local<'a>(self) -> Result<T, Box<dyn Error + 'a>>
979 where
980 E: Error + 'a,
981 {
982 self.map_err(|e| Box::new(e) as _)
983 }
984
985 #[track_caller]
986 fn unwrap_report(self) -> T
987 where
988 E: Error,
989 {
990 match self {
991 Ok(v) => v,
992 Err(e) => panic!("{}", Report::from_error(e)),
993 }
994 }
995}
996
997/// A temporary error type used when converting an [`Option`][] into a
998/// [`Result`][]
999///
1000/// [`Option`]: std::option::Option
1001/// [`Result`]: std::result::Result
1002pub struct NoneError;
1003
1004/// Additions to [`Option`][].
1005pub trait OptionExt<T>: Sized {
1006 /// Convert an [`Option`][] into a [`Result`][] with additional
1007 /// context-sensitive information.
1008 ///
1009 /// [Option]: std::option::Option
1010 /// [Result]: std::result::Result
1011 ///
1012 /// ```rust
1013 /// use snafu::prelude::*;
1014 ///
1015 /// #[derive(Debug, Snafu)]
1016 /// enum Error {
1017 /// UserLookup { user_id: i32 },
1018 /// }
1019 ///
1020 /// fn example(user_id: i32) -> Result<(), Error> {
1021 /// let name = username(user_id).context(UserLookupSnafu { user_id })?;
1022 /// println!("Username was {name}");
1023 /// Ok(())
1024 /// }
1025 ///
1026 /// fn username(user_id: i32) -> Option<String> {
1027 /// /* ... */
1028 /// # None
1029 /// }
1030 /// ```
1031 ///
1032 /// Note that the context selector will call [`Into::into`][] on each field,
1033 /// so the types are not required to exactly match.
1034 fn context<C, E>(self, context: C) -> Result<T, E>
1035 where
1036 C: IntoError<E, Source = NoneError>,
1037 E: Error + ErrorCompat;
1038
1039 /// Convert an [`Option`][] into a [`Result`][] with
1040 /// lazily-generated context-sensitive information.
1041 ///
1042 /// [`Option`]: std::option::Option
1043 /// [`Result`]: std::result::Result
1044 ///
1045 /// ```
1046 /// use snafu::prelude::*;
1047 ///
1048 /// #[derive(Debug, Snafu)]
1049 /// enum Error {
1050 /// UserLookup {
1051 /// user_id: i32,
1052 /// previous_ids: Vec<i32>,
1053 /// },
1054 /// }
1055 ///
1056 /// fn example(user_id: i32) -> Result<(), Error> {
1057 /// let name = username(user_id).with_context(|| UserLookupSnafu {
1058 /// user_id,
1059 /// previous_ids: Vec::new(),
1060 /// })?;
1061 /// println!("Username was {name}");
1062 /// Ok(())
1063 /// }
1064 ///
1065 /// fn username(user_id: i32) -> Option<String> {
1066 /// /* ... */
1067 /// # None
1068 /// }
1069 /// ```
1070 ///
1071 /// Note that this *may not* be needed in many cases because the context
1072 /// selector will call [`Into::into`][] on each field.
1073 fn with_context<F, C, E>(self, context: F) -> Result<T, E>
1074 where
1075 F: FnOnce() -> C,
1076 C: IntoError<E, Source = NoneError>,
1077 E: Error + ErrorCompat;
1078
1079 /// Convert an [`Option`] into a [`Result`] with information
1080 /// from a string.
1081 ///
1082 /// The target error type must implement [`FromString`] by using
1083 /// the
1084 /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors]
1085 /// attribute. The premade [`Whatever`] type is also available.
1086 ///
1087 /// In many cases, you will want to use
1088 /// [`with_whatever_context`][Self::with_whatever_context] instead
1089 /// as it is only called in case of error. This method is best
1090 /// suited for when you have a string literal.
1091 ///
1092 /// ```rust
1093 /// use snafu::{prelude::*, Whatever};
1094 ///
1095 /// fn example(env_var_name: &str) -> Result<(), Whatever> {
1096 /// std::env::var_os(env_var_name).whatever_context("couldn't get the environment variable")?;
1097 /// Ok(())
1098 /// }
1099 ///
1100 /// let err = example("UNDEFINED_ENVIRONMENT_VARIABLE").unwrap_err();
1101 /// assert_eq!("couldn't get the environment variable", err.to_string());
1102 /// ```
1103 #[cfg(any(feature = "alloc", test))]
1104 fn whatever_context<S, E>(self, context: S) -> Result<T, E>
1105 where
1106 S: Into<String>,
1107 E: FromString;
1108
1109 /// Convert an [`Option`] into a [`Result`][] with information from a
1110 /// lazily-generated string.
1111 ///
1112 /// The target error type must implement [`FromString`][] by using
1113 /// the
1114 /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors]
1115 /// attribute. The premade [`Whatever`][] type is also available.
1116 ///
1117 /// ```rust
1118 /// use snafu::{prelude::*, Whatever};
1119 ///
1120 /// fn example(env_var_name: &str) -> Result<(), Whatever> {
1121 /// std::env::var_os(env_var_name).with_whatever_context(|| {
1122 /// format!("couldn't get the environment variable {env_var_name}")
1123 /// })?;
1124 /// Ok(())
1125 /// }
1126 ///
1127 /// let err = example("UNDEFINED_ENVIRONMENT_VARIABLE").unwrap_err();
1128 /// assert_eq!(
1129 /// "couldn't get the environment variable UNDEFINED_ENVIRONMENT_VARIABLE",
1130 /// err.to_string()
1131 /// );
1132 /// ```
1133 ///
1134 /// The closure is not called when the `Option` is `Some`:
1135 ///
1136 /// ```rust
1137 /// use snafu::{prelude::*, Whatever};
1138 ///
1139 /// let value = Some(42);
1140 /// let result = value.with_whatever_context::<_, String, Whatever>(|| {
1141 /// panic!("This block will not be evaluated");
1142 /// });
1143 ///
1144 /// assert!(result.is_ok());
1145 /// ```
1146 #[cfg(any(feature = "alloc", test))]
1147 fn with_whatever_context<F, S, E>(self, context: F) -> Result<T, E>
1148 where
1149 F: FnOnce() -> S,
1150 S: Into<String>,
1151 E: FromString;
1152}
1153
1154impl<T> OptionExt<T> for Option<T> {
1155 #[track_caller]
1156 fn context<C, E>(self, context: C) -> Result<T, E>
1157 where
1158 C: IntoError<E, Source = NoneError>,
1159 E: Error + ErrorCompat,
1160 {
1161 // https://github.com/rust-lang/rust/issues/74042
1162 match self {
1163 Some(v) => Ok(v),
1164 None => Err(context.into_error(NoneError)),
1165 }
1166 }
1167
1168 #[track_caller]
1169 fn with_context<F, C, E>(self, context: F) -> Result<T, E>
1170 where
1171 F: FnOnce() -> C,
1172 C: IntoError<E, Source = NoneError>,
1173 E: Error + ErrorCompat,
1174 {
1175 // https://github.com/rust-lang/rust/issues/74042
1176 match self {
1177 Some(v) => Ok(v),
1178 None => Err(context().into_error(NoneError)),
1179 }
1180 }
1181
1182 #[cfg(any(feature = "alloc", test))]
1183 #[track_caller]
1184 fn whatever_context<S, E>(self, context: S) -> Result<T, E>
1185 where
1186 S: Into<String>,
1187 E: FromString,
1188 {
1189 match self {
1190 Some(v) => Ok(v),
1191 None => Err(FromString::without_source(context.into())),
1192 }
1193 }
1194
1195 #[cfg(any(feature = "alloc", test))]
1196 #[track_caller]
1197 fn with_whatever_context<F, S, E>(self, context: F) -> Result<T, E>
1198 where
1199 F: FnOnce() -> S,
1200 S: Into<String>,
1201 E: FromString,
1202 {
1203 match self {
1204 Some(v) => Ok(v),
1205 None => {
1206 let context = context();
1207 Err(FromString::without_source(context.into()))
1208 }
1209 }
1210 }
1211}
1212
1213/// Backports changes to the [`Error`][] trait to versions of Rust
1214/// lacking them.
1215///
1216/// It is recommended to always call these methods explicitly so that
1217/// it is easy to replace usages of this trait when you start
1218/// supporting a newer version of Rust.
1219///
1220/// ```
1221/// # use snafu::{prelude::*, ErrorCompat};
1222/// # #[derive(Debug, Snafu)] enum Example {};
1223/// # fn example(error: Example) {
1224/// ErrorCompat::backtrace(&error); // Recommended
1225/// error.backtrace(); // Discouraged
1226/// # }
1227/// ```
1228pub trait ErrorCompat {
1229 /// Returns a [`Backtrace`][] that may be printed.
1230 fn backtrace(&self) -> Option<&Backtrace> {
1231 None
1232 }
1233
1234 /// Returns an iterator for traversing the chain of errors,
1235 /// starting with the current error
1236 /// and continuing with recursive calls to `Error::source`.
1237 ///
1238 /// To omit the current error and only traverse its sources,
1239 /// use `skip(1)`.
1240 fn iter_chain(&self) -> ChainCompat<'_, '_>
1241 where
1242 Self: AsErrorSource,
1243 {
1244 ChainCompat::new(self.as_error_source())
1245 }
1246}
1247
1248impl<'a, E> ErrorCompat for &'a E
1249where
1250 E: ErrorCompat,
1251{
1252 fn backtrace(&self) -> Option<&Backtrace> {
1253 (**self).backtrace()
1254 }
1255}
1256
1257/// Converts the receiver into an [`Error`][] trait object, suitable
1258/// for use in [`Error::source`][].
1259///
1260/// It is expected that most users of SNAFU will not directly interact
1261/// with this trait.
1262///
1263/// [`Error`]: std::error::Error
1264/// [`Error::source`]: std::error::Error::source
1265//
1266// Given an error enum with multiple types of underlying causes:
1267//
1268// ```rust
1269// enum Error {
1270// BoxTraitObjectSendSync(Box<dyn error::Error + Send + Sync + 'static>),
1271// BoxTraitObject(Box<dyn error::Error + 'static>),
1272// Boxed(Box<io::Error>),
1273// Unboxed(io::Error),
1274// }
1275// ```
1276//
1277// This trait provides the answer to what consistent expression can go
1278// in each match arm:
1279//
1280// ```rust
1281// impl error::Error for Error {
1282// fn source(&self) -> Option<&(dyn error::Error + 'static)> {
1283// use Error::*;
1284//
1285// let v = match *self {
1286// BoxTraitObjectSendSync(ref e) => ...,
1287// BoxTraitObject(ref e) => ...,
1288// Boxed(ref e) => ...,
1289// Unboxed(ref e) => ...,
1290// };
1291//
1292// Some(v)
1293// }
1294// }
1295//
1296// Existing methods like returning `e`, `&**e`, `Borrow::borrow(e)`,
1297// `Deref::deref(e)`, and `AsRef::as_ref(e)` do not work for various
1298// reasons.
1299pub trait AsErrorSource {
1300 /// For maximum effectiveness, this needs to be called as a method
1301 /// to benefit from Rust's automatic dereferencing of method
1302 /// receivers.
1303 fn as_error_source(&self) -> &(dyn Error + 'static);
1304}
1305
1306impl AsErrorSource for dyn Error + 'static {
1307 fn as_error_source(&self) -> &(dyn Error + 'static) {
1308 self
1309 }
1310}
1311
1312impl AsErrorSource for dyn Error + Send + 'static {
1313 fn as_error_source(&self) -> &(dyn Error + 'static) {
1314 self
1315 }
1316}
1317
1318impl AsErrorSource for dyn Error + Sync + 'static {
1319 fn as_error_source(&self) -> &(dyn Error + 'static) {
1320 self
1321 }
1322}
1323
1324impl AsErrorSource for dyn Error + Send + Sync + 'static {
1325 fn as_error_source(&self) -> &(dyn Error + 'static) {
1326 self
1327 }
1328}
1329
1330impl<T> AsErrorSource for T
1331where
1332 T: Error + 'static,
1333{
1334 fn as_error_source(&self) -> &(dyn Error + 'static) {
1335 self
1336 }
1337}
1338
1339/// Combines an underlying error with additional information
1340/// about the error.
1341///
1342/// It is expected that most users of SNAFU will not directly interact
1343/// with this trait.
1344pub trait IntoError<E>
1345where
1346 E: Error + ErrorCompat,
1347{
1348 /// The underlying error
1349 type Source;
1350
1351 /// Combine the information to produce the error
1352 fn into_error(self, source: Self::Source) -> E;
1353}
1354
1355/// Takes a string message and builds the corresponding error.
1356///
1357/// It is expected that most users of SNAFU will not directly interact
1358/// with this trait.
1359#[cfg(any(feature = "alloc", test))]
1360pub trait FromString {
1361 /// The underlying error
1362 type Source;
1363
1364 /// Create a brand new error from the given string
1365 fn without_source(message: String) -> Self;
1366
1367 /// Wrap an existing error with the given string
1368 fn with_source(source: Self::Source, message: String) -> Self;
1369}
1370
1371/// Construct data to be included as part of an error. The data must
1372/// require no arguments to be created.
1373pub trait GenerateImplicitData {
1374 /// Build the data.
1375 fn generate() -> Self;
1376
1377 /// Build the data using the given source
1378 #[track_caller]
1379 fn generate_with_source(source: &dyn crate::Error) -> Self
1380 where
1381 Self: Sized,
1382 {
1383 let _source = source;
1384 Self::generate()
1385 }
1386}
1387
1388/// View a backtrace-like value as an optional backtrace.
1389pub trait AsBacktrace {
1390 /// Retrieve the optional backtrace
1391 fn as_backtrace(&self) -> Option<&Backtrace>;
1392}
1393
1394/// Only create a backtrace when an environment variable is set.
1395///
1396/// This looks first for the value of `RUST_LIB_BACKTRACE` then
1397/// `RUST_BACKTRACE`. If the value is set to `1`, backtraces will be
1398/// enabled.
1399///
1400/// This value will be tested only once per program execution;
1401/// changing the environment variable after it has been checked will
1402/// have no effect.
1403///
1404/// ## Interaction with the Provider API
1405///
1406/// If you enable the [`unstable-provider-api` feature
1407/// flag][provider-ff], a backtrace will not be captured if the
1408/// original error is able to provide a `Backtrace`, even if the
1409/// appropriate environment variables are set. This prevents capturing
1410/// a redundant backtrace.
1411///
1412/// [provider-ff]: crate::guide::feature_flags#unstable-provider-api
1413#[cfg(any(feature = "std", test))]
1414impl GenerateImplicitData for Option<Backtrace> {
1415 fn generate() -> Self {
1416 if backtrace_collection_enabled() {
1417 Some(Backtrace::generate())
1418 } else {
1419 None
1420 }
1421 }
1422
1423 fn generate_with_source(source: &dyn crate::Error) -> Self {
1424 #[cfg(feature = "unstable-provider-api")]
1425 {
1426 if !backtrace_collection_enabled() {
1427 None
1428 } else if backtraces(source).next().is_some() {
1429 None
1430 } else {
1431 Some(Backtrace::generate_with_source(source))
1432 }
1433 }
1434
1435 #[cfg(not(feature = "unstable-provider-api"))]
1436 {
1437 let _source = source;
1438 Self::generate()
1439 }
1440 }
1441}
1442
1443#[cfg(any(feature = "std", test))]
1444impl AsBacktrace for Option<Backtrace> {
1445 fn as_backtrace(&self) -> Option<&Backtrace> {
1446 self.as_ref()
1447 }
1448}
1449
1450#[cfg(any(feature = "std", test))]
1451fn backtrace_collection_enabled() -> bool {
1452 use crate::once_bool::OnceBool;
1453 use std::env;
1454
1455 static ENABLED: OnceBool = OnceBool::new();
1456
1457 ENABLED.get(|| {
1458 // TODO: What values count as "true"?
1459 env::var_os("RUST_LIB_BACKTRACE")
1460 .or_else(|| env::var_os("RUST_BACKTRACE"))
1461 .map_or(false, |v| v == "1")
1462 })
1463}
1464
1465/// The source code location where the error was reported.
1466///
1467/// To use it, add a field of type `Location` to your error and
1468/// register it as [implicitly generated data][implicit]. When
1469/// constructing the error, you do not need to provide the location:
1470///
1471/// ```rust
1472/// # use snafu::prelude::*;
1473/// #[derive(Debug, Snafu)]
1474/// struct NeighborhoodError {
1475/// #[snafu(implicit)]
1476/// loc: snafu::Location,
1477/// }
1478///
1479/// fn check_next_door() -> Result<(), NeighborhoodError> {
1480/// ensure!(everything_quiet(), NeighborhoodSnafu);
1481/// Ok(())
1482/// }
1483/// # fn everything_quiet() -> bool { false }
1484/// ```
1485///
1486/// [implicit]: Snafu#controlling-implicitly-generated-data
1487///
1488/// ## Limitations
1489///
1490/// Implicitly generated data, including `Location`, is generated when
1491/// the wrapping error value is constructed:
1492///
1493/// ```rust
1494/// # use snafu::{prelude::*, Location, location};
1495/// # fn fallible_code() -> Result<(), InnerError> { Err(InnerError) }
1496/// # #[derive(Debug, Snafu)] struct InnerError;
1497/// # #[derive(Debug, Snafu)]
1498/// # struct InterestingError {
1499/// # source: InnerError,
1500/// # #[snafu(implicit)] location: Location,
1501/// # }
1502/// # let base_loc = location!();
1503/// # let r: Result<(), InterestingError> = (|| {
1504/// // The first we know about the error is on this line:
1505/// let e = fallible_code();
1506/// // but the location will correspond to this line:
1507/// e.context(InterestingSnafu)?;
1508/// # Ok(())
1509/// # })();
1510/// # let e = r.unwrap_err();
1511/// # assert_eq!(e.location.line(), base_loc.line() + 5);
1512/// ```
1513///
1514/// If you have [disabled the context selector][disabled], the
1515/// `Location` will correspond to where the `From` implementation is
1516/// invoked. This is usually part of the `?` operator:
1517///
1518/// ```rust
1519/// # use snafu::{prelude::*, Location, location};
1520/// # fn fallible_code() -> Result<(), InnerError> { Err(InnerError) }
1521/// # #[derive(Debug, Snafu)] struct InnerError;
1522/// # #[derive(Debug, Snafu)]
1523/// # #[snafu(context(false))]
1524/// # struct InterestingError {
1525/// # source: InnerError,
1526/// # #[snafu(implicit)] location: Location,
1527/// # }
1528/// # let base_loc = location!();
1529/// # let r: Result<(), InterestingError> = (|| {
1530/// // The first we know about the error is on this line:
1531/// let e = fallible_code();
1532/// // but the location will correspond to this line:
1533/// e?;
1534/// # Ok(())
1535/// # })();
1536/// # let e = r.unwrap_err();
1537/// # assert_eq!(e.location.line(), base_loc.line() + 5);
1538/// ```
1539///
1540/// Inspecting the code at the generated `Location` will usually
1541/// quickly lead back to the original error, but it's recommended to
1542/// create the wrapping error as close to the original error location
1543/// to reduce confusion.
1544///
1545/// [disabled]: Snafu#disabling-the-context-selector
1546///
1547/// ### Asynchronous code
1548///
1549/// When using SNAFU's
1550#[cfg_attr(feature = "futures", doc = " [`TryFutureExt`][futures::TryFutureExt]")]
1551#[cfg_attr(not(feature = "futures"), doc = " `TryFutureExt`")]
1552/// or
1553#[cfg_attr(feature = "futures", doc = " [`TryStreamExt`][futures::TryStreamExt]")]
1554#[cfg_attr(not(feature = "futures"), doc = " `TryStreamExt`")]
1555/// extension traits, the automatically captured location will
1556/// correspond to where the future or stream was **polled**, not where
1557/// it was created. Additionally, many `Future` or `Stream`
1558/// combinators do not forward the caller's location to their
1559/// closures, causing the recorded location to be inside of the future
1560/// combinator's library.
1561///
1562/// There are two workarounds:
1563/// 1. Avoid combinators and use the non-async [`ResultExt`]
1564/// 1. Construct the location explicitly, such as by the [`location!`] macro
1565///
1566/// ```rust
1567/// # #[cfg(all(feature = "futures", feature = "internal-dev-dependencies"))] {
1568/// # use snafu::{prelude::*, Location, location};
1569/// # let body = async {
1570/// // Non-ideal: will report where `wrapped_error_future` is `.await`ed.
1571/// # let base_location = location!();
1572/// # let error_future = async { AnotherSnafu.fail::<()>() };
1573/// let wrapped_error_future = error_future.context(ImplicitLocationSnafu);
1574/// # let wrapped_error = wrapped_error_future.await.unwrap_err();
1575/// # assert_eq!(wrapped_error.location.line(), base_location.line() + 3);
1576///
1577/// // Better: will report the location of `.context`.
1578/// # let base_location = location!();
1579/// # let error_future = async { AnotherSnafu.fail::<()>() };
1580/// let wrapped_error_future = async { error_future.await.context(ImplicitLocationSnafu) };
1581/// # let wrapped_error = wrapped_error_future.await.unwrap_err();
1582/// # assert_eq!(wrapped_error.location.line(), base_location.line() + 2);
1583///
1584/// // Better: Will report the location of `location!`
1585/// # let base_location = location!();
1586/// # let error_future = async { AnotherSnafu.fail::<()>() };
1587/// let wrapped_error_future = error_future.with_context(|_| ExplicitLocationSnafu {
1588/// location: location!(),
1589/// });
1590/// # let wrapped_error = wrapped_error_future.await.unwrap_err();
1591/// # assert_eq!(wrapped_error.location.line(), base_location.line() + 3);
1592///
1593/// # #[derive(Debug, Snafu)] struct AnotherError;
1594/// #[derive(Debug, Snafu)]
1595/// struct ImplicitLocationError {
1596/// source: AnotherError,
1597/// #[snafu(implicit)]
1598/// location: snafu::Location,
1599/// }
1600///
1601/// #[derive(Debug, Snafu)]
1602/// struct ExplicitLocationError {
1603/// source: AnotherError,
1604/// location: snafu::Location,
1605/// }
1606/// # };
1607/// # futures::executor::block_on(body);
1608/// # }
1609/// ```
1610pub type Location = &'static core::panic::Location<'static>;
1611
1612impl GenerateImplicitData for Location {
1613 #[inline]
1614 #[track_caller]
1615 fn generate() -> Self {
1616 core::panic::Location::caller()
1617 }
1618}
1619
1620/// Constructs a [`Location`] using the current file, line, and column.
1621#[macro_export]
1622macro_rules! location {
1623 () => {
1624 core::panic::Location::caller()
1625 };
1626}
1627
1628#[cfg(feature = "unstable-provider-api")]
1629fn backtraces(error: &dyn Error) -> impl Iterator<Item = &Backtrace> {
1630 ChainCompat::new(error).filter_map(error::request_ref)
1631}
1632
1633mod tests {
1634 #[cfg(doc)]
1635 #[doc = include_str!("../README.md")]
1636 fn readme_tests() {}
1637}