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