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