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