snafu-0.7.1/.cargo_vcs_info.json0000644000000001360000000000100121550ustar { "git": { "sha1": "6159ea9f3a3e189f1faa2f63904402f04dea54ec" }, "path_in_vcs": "" }snafu-0.7.1/.editorconfig000064400000000000000000000005020072674642500134470ustar 00000000000000# EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 [*.md] trim_trailing_whitespace = false snafu-0.7.1/CHANGELOG.md000064400000000000000000000346330072674642500126170ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). See the [upgrading guide][] for more detailed information about modifying code to account for new releases. [upgrading guide]: https://docs.rs/snafu/*/snafu/guide/upgrading/index.html ## [0.7.1] - 2022-05-02 ### Added - The macro `ensure_whatever` provides the functionality of the `ensure` macro for stringly-typed errors. ### Changed - No longer require the `futures` feature flag to support the shim implementations of standard library errors that have existed since Rust 1.34. - Documentation improved to demonstrate that custom Whatever errors can optionally be made `Send` and `Sync`. ## [0.7.0] - 2022-01-03 Many breaking changes in this release can be automatically addressed with the [snafu-upgrade-assistant][]. [snafu-upgrade-assistant]: https://github.com/shepmaster/snafu-upgrade-assistant ### Added - A crate prelude containing common macros and traits can be imported via `use snafu::prelude::*`. - A ready-to-use error type `Whatever` is available to quickly start reporting errors with little hassle. - "Stringly typed" error cases can be added to existing error types, allowing you to construct errors without defining them first. - Formatting shorthand syntax for error type data fields is now supported: `#[snafu(display("Hello {name}"))]`. - `[snafu(module)]` can be specified on an error type. This will create a module for the error type and all associated context selectors will be placed in that module. - `snafu::Location` can be added to an error type to provide lightweight tracking of the source location where the error was created. - `[snafu(implicit)]` can be specified on context selector data fields to automatically generate it via `snafu::GenerateImplicitData` when the error is created. - `ErrorCompat::iter_chain` provides an iterator over the list of causal errors. ### Changed - Generated context selectors now have the suffix `Snafu`. This is a **breaking change**. - `ResultExt::with_context`, `TryFutureExt::with_context`, and `TryStreamExt::with_context` now pass the error into the closure. This is a **breaking change**. - The `GenerateBacktrace` trait has been split into `GenerateImplicitData` and `AsBacktrace`. This is a **breaking change**. - Rust 1.34 is now the minimum supported Rust version. This is a **breaking change**. ### Removed - String attribute parsing (`#[snafu(foo = "...")]`) is no longer supported. This is a **breaking change**. - The deprecated `eager_context` and `with_eager_context` methods have been removed. This is a **breaking change**. [0.7.0]: https://github.com/shepmaster/snafu/releases/tag/0.7.0 ## [0.6.10] - 2020-12-03 ### Fixed - `ensure!` now uses a fully-qualified path to avoid a name clash when the path `core` is ambiguous. [0.6.10]: https://github.com/shepmaster/snafu/releases/tag/0.6.10 ## [0.6.9] - 2020-09-21 ### Added - `#[derive(Snafu)]` is now supported on unit structs and structs with fields. - `ensure!` now supports trailing commas. ### Fixed - The error text for a misuse of `#[snafu(context)]` was corrected. - More usages of `Option` in the generated code are now fully qualified. [0.6.9]: https://github.com/shepmaster/snafu/releases/tag/0.6.9 ## [0.6.8] - 2020-05-11 ### Fixed - The code generated by the `Snafu` macro no longer conflicts with a local module called `core` or `snafu`. [0.6.8]: https://github.com/shepmaster/snafu/releases/tag/0.6.8 ## [0.6.7] - 2020-05-03 ### Added - Demonstration error types are now present in the guide. - The user's guide is now an optional feature flag. To preserve compatibility, it is enabled by default, but most users can disable it. - It is now possible to import the `snafu` crate under a different name using `#[snafu(crate_root)]`. [0.6.7]: https://github.com/shepmaster/snafu/releases/tag/0.6.7 ## [0.6.6] - 2020-04-05 ### Added - Context selectors without an underlying cause now have a `build` method in addition to the existing `fail` method. `build` creates the error but does not wrap it in a `Result`. [0.6.6]: https://github.com/shepmaster/snafu/releases/tag/0.6.6 ## [0.6.5] - 2020-04-05 - This version was a failed publish; please use 0.6.6 instead. [0.6.4]: https://github.com/shepmaster/snafu/releases/tag/0.6.4 ## [0.6.4] - 2020-04-05 - This version was a failed publish; please use 0.6.6 instead. [0.6.4]: https://github.com/shepmaster/snafu/releases/tag/0.6.4 ## [0.6.3] - 2020-03-18 ### Fixed - License files are now included with the snafu-derive package. [0.6.3]: https://github.com/shepmaster/snafu/releases/tag/0.6.3 ## [0.6.2] - 2020-01-17 ### Added - Automatically-generated code no longer triggers the `single_use_lifetimes` lint. [0.6.2]: https://github.com/shepmaster/snafu/releases/tag/0.6.2 ## [0.6.1] - 2020-01-07 ### Added - It is now possible to create errors that have no context using `#[snafu(context(false))]`. This allows using the question mark operator without calling `.context(...)`. ### Fixed - Reduced the possibility for a name collision when implementing `Display` when a formatted value was called `f`. [0.6.1]: https://github.com/shepmaster/snafu/releases/tag/0.6.1 ## [0.6.0] - 2019-11-07 ### Added - Optional support for using the unstable `std::backtrace::Backtrace` type and implementing `std::error::Error::backtrace` via the `unstable-backtraces-impl-std` feature flag. - Error variants can now use `Option` for the `backtrace` field. `Backtrace` will always have the backtrace collected, while `Option` requires that an environment variable be set. - Basic support for no-std environments. - The `ensure!` macro now allows creating opaque errors. - Context selectors have basic documentation generated. This allows using `#[deny(missing_docs)]`. ### Changed - Rust 1.31 is now the minimum supported Rust version. This is a **breaking change**. - The `Backtrace` type is now always available, but does nothing by default. It is recommended that the end application enables backtrace functionality. This is a **breaking change**. - Support for `std::future::Future` has been stabilized, which means the feature flag has been renamed from `unstable-futures` to `futures`. This is a **breaking change**. - The `backtrace-crate` feature flag has been renamed to `backtraces-impl-backtrace-crate`. Enabling this flag now *replaces* `snafu::Backtrace` with `backtrace::Backtrace`. The `AsRef` implementation has been removed. This is a **breaking change**. - A new trait for constructing backtraces is used instead of `Default` so the `Backtrace` type no longer implements `Default` or has any inherent methods. This is a **breaking change**. [0.6.0]: https://github.com/shepmaster/snafu/releases/tag/0.6.0 ## [0.5.0] - 2019-08-26 ### Added - Compiler errors are generated when SNAFU attributes are used in incorrect locations. This is a **breaking change**. - Compiler errors are generated when SNAFU attributes are duplicated. This is a **breaking change**. ### Changed - `#[snafu(source(from))` implies `#[snafu(source)]` (which implies `#[snafu(source(true))]`); `#[snafu(source)]` and `#[snafu(source(true))]` can be removed in these cases. ### Fixed - Multiple attributes can be specified inside of a single `#[snafu(...)]`. ### Removed - `#[snafu(backtrace(delegate))]` on source fields is replaced by `#[snafu(backtrace)]`. This is a **breaking change**. [0.5.0]: https://github.com/shepmaster/snafu/releases/tag/0.5.0 ## [0.4.4] - 2019-08-07 ### Fixed - Ignore `#[doc]` attributes that do not correspond to documentation comments. This allows `#[doc(hidden)]` to be used again. ### Changed - Implement `Future` and `Stream` instead of `TryFuture` and `TryStream` for the combinators for the standard library's futures. This allows the `Context` future combinator to be directly used with `.await` and for the `Context` stream combinator to be used without calling `.into_stream`. [0.4.4]: https://github.com/shepmaster/snafu/releases/tag/0.4.4 ## [0.4.3] - 2019-07-23 ### Added - Add optional conversion of `&snafu::Backtrace` into `&backtrace::Backtrace`. ### Fixed - Support default generic parameters on error types. [0.4.3]: https://github.com/shepmaster/snafu/releases/tag/0.4.3 ## [0.4.2] - 2019-07-21 ### Added - Documentation comment summaries are used as the default `Display` text. ### Fixed - Quieted warnings from usages of bare trait objects. - The `From` trait is fully-qualified to avoid name clashes. ### Changed - More errors are reported per compilation attempt. [0.4.2]: https://github.com/shepmaster/snafu/releases/tag/0.4.2 ## [0.4.1] - 2018-05-18 ### Fixed - A feature flag name was rejected by crates.io and needed to be updated; this release has no substantial changes beyond 0.4.0. [0.4.1]: https://github.com/shepmaster/snafu/releases/tag/0.4.1 ## [0.4.0] - 2018-05-18 ### Added - Context selectors now automatically implement `Debug`, `Copy`, and `Clone`. This is a **breaking change**. - Support for futures 0.1 futures and streams is available using the `futures-01` feature flag. - **Experimental** support for standard library futures and streams is available using the `unstable-futures` feature flag. ### Deprecated - `eager_context` and `with_eager_context` have been deprecated. ### Removed - The `Context` type is no longer needed. This is a **breaking change**. - SNAFU types no longer implement `Borrow`. This is a **breaking change**. [0.4.0]: https://github.com/shepmaster/snafu/releases/tag/0.4.0 ## [0.3.1] - 2019-05-10 ### Fixed - Underlying error causes of `Box` are now supported. ### Deprecated - `Borrow` is no longer required to be implemented for underlying error causes. In the next release containing breaking changes, the automatic implementation of `Borrow` for SNAFU types will be removed. [0.3.1]: https://github.com/shepmaster/snafu/releases/tag/0.3.1 ## [0.3.0] - 2019-05-08 ### Added - `Borrow` is now automatically implemented for SNAFU types. This is a **breaking change** as it may conflict with an existing user implementation of the same trait. It is expected that the number of affected users is very small. - `#[snafu(source)]` can be used to identify the field that corresponds to the underlying error if it is not called `source`. It can also be used to disable automatically using a field called `source` for the underlying error. - `#[snafu(backtrace)]` can be used to identify the field that corresponds to the backtrace if it is not called `backtrace`. It can also be used to disable automatically using a field called `backtrace` for the backtrace. - `#[snafu(source(from(...type..., ...expression...)))]` can be used to perform transformations on the underlying error before it is stored. This allows boxing of large errors to avoid bloated return types or recursive errors. - The user guide has a basic comparison to Failure and migration paths for common Failure patterns. ### Changed - The default `Display` implementation includes the underlying error message. [0.3.0]: https://github.com/shepmaster/snafu/releases/tag/0.3.0 ## [0.2.3] - 2019-04-24 ### Fixed - User-provided `where` clauses on error types are now copied to SNAFU-created `impl` blocks. - User-provided inline trait bounds (``) are no longer included in SNAFU-generated type names. [0.2.3]: https://github.com/shepmaster/snafu/releases/tag/0.2.3 ## [0.2.2] - 2019-04-19 ### Fixed - Error enums with variants named `Some` or `None` no longer cause name conflicts in the generated code. [0.2.2]: https://github.com/shepmaster/snafu/releases/tag/0.2.2 ## [0.2.1] - 2019-04-14 ### Added - Deriving `Snafu` on a newtype struct now creates an opaque error type, suitable for conservative public APIs. [0.2.1]: https://github.com/shepmaster/snafu/releases/tag/0.2.1 ## [0.2.0] - 2019-03-02 ### Removed - `snafu::display` and `snafu_display` have been replaced with `snafu(display)` - `snafu_visibility` has been replaced with `snafu(visibility)` ### Added - Backtraces can now be delegated to an underlying error via `#[snafu(backtrace(delegate))]`. [0.2.0]: https://github.com/shepmaster/snafu/releases/tag/0.2.0 ## [0.1.9] - 2019-03-02 ### Added - Error enums with generic lifetimes and types are now supported. ### Changed - The trait bounds applied to the `fail` method have been moved from the implementation block to the function itself. [0.1.9]: https://github.com/shepmaster/snafu/releases/tag/0.1.9 ## [0.1.8] - 2019-02-27 ### Fixed - Visibility is now applied to context selector fields. [0.1.8]: https://github.com/shepmaster/snafu/releases/tag/0.1.8 ## [0.1.7] - 2019-02-27 ### Added - `#[snafu_visibility]` can be used to configure the visibility of context selectors. [0.1.7]: https://github.com/shepmaster/snafu/releases/tag/0.1.7 ## [0.1.6] - 2019-02-24 ### Added - The `OptionExt` extension trait is now available for converting `Option`s into `Result`s while adding context. [0.1.6]: https://github.com/shepmaster/snafu/releases/tag/0.1.6 ## [0.1.5] - 2019-02-05 ### Changed - Errors from the macro are more detailed and point to reasonable sections of code. [0.1.5]: https://github.com/shepmaster/snafu/releases/tag/0.1.5 ## [0.1.4] - 2019-02-05 ### Added - The `ensure` macro is now available. [0.1.4]: https://github.com/shepmaster/snafu/releases/tag/0.1.4 ## [0.1.3] - 2019-02-04 ### Added - Ability to automatically capture backtraces. ### Changed - Version requirements for dependencies loosened to allow compiling with more crate versions. [0.1.3]: https://github.com/shepmaster/snafu/releases/tag/0.1.3 ## [0.1.2] - 2019-02-02 ### Added - Support for Rust 1.18 [0.1.2]: https://github.com/shepmaster/snafu/releases/tag/0.1.2 ## [0.1.1] - 2019-02-01 ### Added - Context selectors without an underlying source now have a `fail` method. - `ResultExt` now has the `eager_context` and `with_eager_context` methods to eagerly convert a source `Result` into a final `Result` type, skipping the intermediate `Result<_, Context<_>>` type. [0.1.1]: https://github.com/shepmaster/snafu/releases/tag/0.1.1 ## [0.1.0] - 2019-01-27 Initial version [0.1.0]: https://github.com/shepmaster/snafu/releases/tag/0.1.0 snafu-0.7.1/CODE_OF_CONDUCT.md000064400000000000000000000075070072674642500136050ustar 00000000000000# Code of Conduct ## Conduct * We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. * Please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. * Please be kind and courteous. There's no need to be mean or rude. * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct][CCoC]; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. ## Moderation These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please notify [@shepmaster][]. 1. Remarks that violate our standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) 2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. 3. Moderators will first respond to such remarks with a warning. 4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. 5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. 6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. 7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. 8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. We strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. If someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow humans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. *Adapted from the [Rust Code of Conduct][RCoC]* [@shepmaster]: https://github.com/shepmaster/ [CCoc]: http://citizencodeofconduct.org/ [RCoC]: https://www.rust-lang.org/policies/code-of-conduct snafu-0.7.1/CONTRIBUTING.md000064400000000000000000000070400072674642500132270ustar 00000000000000# Contributor's Guide Thank you for helping with SNAFU! This document has some guidelines and tips that can help you make a contribution. Feel free to make a pull request to this file, too, if you learn anything during your contribution that can help others. ## Code of Conduct This project is governed by the [Code of Conduct](https://github.com/shepmaster/snafu/blob/master/CODE_OF_CONDUCT.md). Please understand those guidelines, and report violations to @shepmaster. ## Getting Started If you're looking for a way to contribute - first of all, thanks! Here are some ideas: * Issues that we need help on are tagged [help wanted](https://github.com/shepmaster/snafu/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) * Issues good for beginners are tagged [good first issue](https://github.com/shepmaster/snafu/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) * Anything that's not clear to you in the documentation, particularly in the [user's guide](https://docs.rs/snafu/latest/snafu/guide/index.html) ## Communication tips * Open an issue for discussion before writing any non-trivial changes. Often the author or other contributors can help shape an even better idea. Or maybe someone is already working on it! * Even if you're making a breaking change, don't worry about updating the version number or changelog. They're done together before a release. Feel free to suggest some wording you like in the pull request, though. * We value correctness and clarity in the code, API, and docs, and it's worth putting in the time for thorough review in issues and pull requests. * Don't try to fix the world in a single issue or pull request. Even small issues can sprout many good ideas, and feel free to split those into new issues. ## Testing tips * We maintain compatibility with older versions of Rust, and this is enforced through compatibility testing that runs automatically when you create or update a pull request. You can run these earlier, locally, by running `cargo test` in one of the directories under `compatibility-tests/`. The `rust-toolchain` files there will cause the right version of Rust to be used, assuming you use rustup. The `Cargo.toml` files there will make sure that compatible dependency versions are used, too. * If you're adding a new compile-time error, add a sample under `compatibility-tests/compile-fail/tests/ui/` to be sure it fails in the way you expect. * If you're adding a feature, please add a test for it. This helps show your intent, and makes sure others don't accidentally break the feature. * Because the majority of SNAFU code lives in snafu-derive and deals with procedural macros, integration tests are often simpler than unit tests. They live under `tests/`. Add to the file that sounds most relevant, or create a new one if necessary. * Unit tests are still great when you're working on something that doesn't need to parse Rust source. They follow standard unit testing practice in Rust - a `#[test]` function in a `tests` module at the bottom of the relevant source module. ## General tips * Breaking changes (changes in SNAFU's interface) are OK if they're adding value. * Before 1.0, this may happen relatively frequently, and will result in new minor versions. * After 1.0, this should be relatively rare, but new major versions are OK with good reasons. * If you're making a code change, please run the code through rustfmt (`cargo fmt`) and check it with clippy (`cargo clippy`). * The user's guide is a valuable resource! It's worth the time to keep up to date when adding or changing the library. snafu-0.7.1/Cargo.toml0000644000000036760000000000100101670ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "snafu" version = "0.7.1" authors = ["Jake Goulding "] exclude = [ "/.cirrus.yml", "/.gitignore", "/rust-toolchain", ] description = "An ergonomic error handling library" documentation = "https://docs.rs/snafu" readme = "README.md" keywords = [ "error", "ergonomic", "library", "no_std", ] categories = [ "rust-patterns", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/shepmaster/snafu" [package.metadata.docs.rs] features = [ "std", "backtraces", "futures", "guide", ] [dependencies.backtrace] version = "0.3.0" optional = true [dependencies.doc-comment] version = "0.3.1" default-features = false [dependencies.futures-core-crate] version = "0.3.0" optional = true default-features = false package = "futures-core" [dependencies.futures-crate] version = "0.3.0" optional = true default-features = false package = "futures" [dependencies.pin-project] version = "1.0" optional = true default-features = false [dependencies.snafu-derive] version = "0.7.1" [features] backtraces = [ "std", "backtrace", ] backtraces-impl-backtrace-crate = ["backtraces"] default = [ "std", "rust_1_46", ] futures = [ "futures-core-crate", "pin-project", ] guide = [] internal-dev-dependencies = ["futures-crate"] rust_1_46 = ["snafu-derive/rust_1_46"] std = [] unstable-backtraces-impl-std = [ "backtraces", "snafu-derive/unstable-backtraces-impl-std", ] snafu-0.7.1/Cargo.toml.orig000064400000000000000000000040730072674642500136700ustar 00000000000000[package] name = "snafu" version = "0.7.1" authors = ["Jake Goulding "] edition = "2018" readme = "README.md" description = "An ergonomic error handling library" documentation = "https://docs.rs/snafu" repository = "https://github.com/shepmaster/snafu" license = "MIT OR Apache-2.0" keywords = ["error", "ergonomic", "library", "no_std"] categories = ["rust-patterns", "no-std"] exclude = [ "/.cirrus.yml", "/.gitignore", "/rust-toolchain", ] [package.metadata.docs.rs] # The backtraces-impl-* features are incompatible with each other features = [ "std", "backtraces", "futures", "guide" ] [features] default = ["std", "rust_1_46"] # Implement the `std::error::Error` trait. std = [] # Add support for `#[track_caller]` rust_1_46 = ["snafu-derive/rust_1_46"] # Makes the backtrace type live backtraces = ["std", "backtrace"] # The backtrace type becomes `backtrace::Backtrace` backtraces-impl-backtrace-crate = ["backtraces"] # The backtrace type becomes `std::backtrace::Backtrace` and we # implement `std::error::Error::backtrace` unstable-backtraces-impl-std = ["backtraces", "snafu-derive/unstable-backtraces-impl-std"] # The standard library's implementation of futures futures = ["futures-core-crate", "pin-project"] # Include the built-in user guide documentation guide = [] # No public user should make use of this feature # https://github.com/rust-lang/cargo/issues/1596 "internal-dev-dependencies" = ["futures-crate"] [workspace] # The compatibility tests each set feature flags for the library and # cannot be in the same crate graph. exclude = ["compatibility-tests"] [dependencies] snafu-derive = { path = "snafu-derive", version = "0.7.1" } doc-comment = { version = "0.3.1", default-features = false } backtrace = { version = "0.3.0", optional = true } futures-crate = { package = "futures", version = "0.3.0", optional = true, default-features = false } futures-core-crate = { package = "futures-core", version = "0.3.0", optional = true, default-features = false } pin-project = { version = "1.0", optional = true, default-features = false } snafu-0.7.1/LICENSE-APACHE000064400000000000000000000251220072674642500127230ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2019- Jake Goulding Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. snafu-0.7.1/LICENSE-MIT000064400000000000000000000020420072674642500124270ustar 00000000000000Copyright (c) 2019- Jake Goulding Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. snafu-0.7.1/README.md000064400000000000000000000027770072674642500122710ustar 00000000000000# SNAFU **S**ituation **N**ormal: **A**ll **F**ouled **U**p [![crates.io][Crate Logo]][Crate] [![Documentation][Doc Logo]][Doc] [![Build Status][CI Logo]][CI] SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. ```rust use snafu::prelude::*; use std::{fs, io, path::PathBuf}; #[derive(Debug, Snafu)] enum Error { #[snafu(display("Unable to read configuration from {}: {}", path.display(), source))] ReadConfiguration { source: io::Error, path: PathBuf }, #[snafu(display("Unable to write result to {}: {}", path.display(), source))] WriteResult { source: io::Error, path: PathBuf }, } type Result = std::result::Result; fn process_data() -> Result<()> { let path = "config.toml"; let configuration = fs::read_to_string(path).context(ReadConfigurationSnafu { path })?; let path = unpack_config(&configuration); fs::write(&path, b"My complex calculation").context(WriteResultSnafu { path })?; Ok(()) } fn unpack_config(data: &str) -> &str { "/some/path/that/does/not/exist" } ``` Please see [the documentation][Doc] and the [user's guide][Guide] for a full description. [Crate]: https://crates.io/crates/snafu [Crate Logo]: https://img.shields.io/crates/v/snafu.svg [Doc]: https://docs.rs/snafu [Doc Logo]: https://docs.rs/snafu/badge.svg [Guide]: https://docs.rs/snafu/*/snafu/guide/index.html [CI]: https://cirrus-ci.com/github/shepmaster/snafu [CI Logo]: https://api.cirrus-ci.com/github/shepmaster/snafu.svg snafu-0.7.1/compatibility-tests/.gitignore000064400000000000000000000000410072674642500167710ustar 00000000000000*/target **/*.rs.bk */Cargo.lock snafu-0.7.1/fmt-markdown.sh000075500000000000000000000011270072674642500137430ustar 00000000000000#!/usr/bin/env bash set -euo pipefail scratch_dir=$(mktemp -d) project_dir="${scratch_dir}/scratch" cargo_toml="${project_dir}/Cargo.toml" rustfmt_toml="${project_dir}/rustfmt.toml" lib_rs="${project_dir}/src/lib.rs" cargo new --lib "${project_dir}" cat <<-EOF > "${rustfmt_toml}" format_code_in_doc_comments = true EOF for markdown_file in $(git ls-files | rg '.md$'); do awk '{ print "/// " $0 } END { print "fn dummy() {}"}' "${markdown_file}" > "${lib_rs}" cargo +nightly fmt --manifest-path="${cargo_toml}" sed -E -e '$ d' -e 's@/// ?@@' "${lib_rs}" > "${markdown_file}" done snafu-0.7.1/netlify.toml000064400000000000000000000003240072674642500133430ustar 00000000000000[build] command = """ rustup install nightly --profile minimal && \ cargo +nightly doc --no-deps --features=std,backtraces,futures,guide """ publish = "target/doc" [[redirects]] from = "/*" to = "/snafu/:splat" snafu-0.7.1/src/Snafu.md000064400000000000000000000277360072674642500132010ustar 00000000000000The `Snafu` macro is the entrypoint to defining your own error types. It is designed to require little configuration for the recommended and typical usecases while still offering flexibility for unique situations. - [`backtrace`](#controlling-backtraces) - [`context`](#controlling-context) - [`crate_root`](#controlling-how-the-snafu-crate-is-resolved) - [`display`](#controlling-display) - [`implicit`](#controlling-implicitly-generated-data) - [`module`](#placing-context-selectors-in-modules) - [`source`](#controlling-error-sources) - [`visibility`](#controlling-visibility) - [`whatever`](#controlling-stringly-typed-errors) ## Controlling `Display` You can specify how the `Display` trait will be implemented for each variant. The argument is a format string and the arguments. All of the fields of the variant will be available and you can call methods on them, such as `filename.display()`. As an extension to the current format string capabilities, a shorthand is available for named arguments that match a field. **Example** ```rust # use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { #[snafu(display("{username} may not log in until they pay USD {amount:E}"))] UserMustPayForService { username: String, amount: f32 }, } fn main() { assert_eq!( UserMustPayForServiceSnafu { username: "Stefani", amount: 1_000_000.0, } .build() .to_string(), "Stefani may not log in until they pay USD 1E6", ); } ``` ### The default `Display` implementation It is recommended that you provide a value for `snafu(display)`, but if it is omitted, the summary of the documentation comment will be used. If that is not present, the name of the variant will be used. ```rust # use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { /// No user available. /// You may need to specify one. MissingUser, MissingPassword, } fn main() { assert_eq!( MissingUserSnafu.build().to_string(), "No user available. You may need to specify one.", ); assert_eq!(MissingPasswordSnafu.build().to_string(), "MissingPassword"); } ``` ## Controlling context ### Changing the context selector suffix When context selectors are generated, any `Error` suffix is removed and the suffix `Snafu` is added by default. If you'd prefer a different suffix, such as `Ctx` or `Context`, you can specify that with `#[snafu(context(suffix(SomeIdentifier)))]`. If you'd like to disable the suffix entirely, you can use `#[snafu(context(suffix(false)))]`. **Example** ```rust # use snafu::prelude::*; # #[derive(Debug, Snafu)] enum Error { UsesTheDefaultSuffixError, #[snafu(context(suffix(Ctx)))] HasAnotherSuffix, #[snafu(context(suffix(false)))] DoesNotHaveASuffix, } fn my_code() -> Result<(), Error> { UsesTheDefaultSuffixSnafu.fail()?; HasAnotherSuffixCtx.fail()?; DoesNotHaveASuffix.fail()?; Ok(()) } ``` `#[snafu(context(suffix))]` can be specified on an enum as the default suffix for variants of the enum. In that case, if you wish to have one variant with a suffix, you will need to express it explicitly with `#[snafu(context(suffix(SomeIdentifier)))]`. ### Disabling the context selector Sometimes, an underlying error can only occur in exactly one context and there's no additional information that can be provided to the caller. In these cases, you can use `#[snafu(context(false))]` to indicate that no context selector should be created. This allows using the `?` operator directly on the underlying error. Please think about your end users before making liberal use of this feature. Adding context to an error is often what distinguishes an actionable error from a frustrating one. **Example** ```rust # use snafu::prelude::*; # #[derive(Debug, Snafu)] enum Error { #[snafu(context(false))] NeedsNoIntroduction { source: VeryUniqueError }, } fn my_code() -> Result { let val = do_something_unique()?; Ok(val + 10) } # #[derive(Debug, Snafu)] # enum VeryUniqueError {} fn do_something_unique() -> Result { // ... # Ok(42) } ``` ## Controlling visibility By default, each of the context selectors and their inherent methods will be private. It is our opinion that each module should have one or more error types that are scoped to that module, reducing the need to deal with unrelated errors when matching and increasing cohesiveness. If you need to access the context selectors from outside of their module, you can use the `#[snafu(visibility)]` attribute. This can be applied to the error type as a default visibility or to specific context selectors. There are multiple forms of the attribute: - `#[snafu(visibility(X))]` `X` is a normal Rust visibility modifier (`pub`, `pub(crate)`, `pub(in some::path)`, etc.). - `#[snafu(visibility)]` will reset back to private visibility. ``` # use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(visibility(pub(crate)))] // Sets the default visibility for these context selectors pub(crate) enum Error { IsPubCrate, // Uses the default #[snafu(visibility)] IsPrivate, // Will be private } ``` It should be noted that API stability of context selectors is not guaranteed. Therefore, exporting them in a crate's public API could cause semver breakage for such crates, should SNAFU internals change. ## Placing context selectors in modules When you have multiple error enums that would generate conflicting context selectors, you can choose to place the context selectors into a module using `snafu(module)`: ```rust use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(module)] enum ReadError { Opening, } fn example() -> Result<(), ReadError> { read_error::OpeningSnafu.fail() } #[derive(Debug, Snafu)] enum WriteError { Opening, // Would conflict if `snafu(module)` was not used above. } # // https://github.com/rust-lang/rust/issues/83583 # fn main() {} ``` By default, the module name will be the `snake_case` equivalent of the enum name. You can override the default by providing an argument to `#[snafu(module(...))]`: ```rust use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(module(read))] enum ReadError { Opening, } fn example() -> Result<(), ReadError> { read::OpeningSnafu.fail() } # // https://github.com/rust-lang/rust/issues/83583 # fn main() {} ``` As placing the context selectors in a module naturally namespaces them, you may wish to combine this option with `#[snafu(context(suffix(false)))]`: ```rust use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(module, context(suffix(false)))] enum ReadError { Opening, } fn example() -> Result<(), ReadError> { read_error::Opening.fail() } # // https://github.com/rust-lang/rust/issues/83583 # fn main() {} ``` The generated module starts with `use super::*`, so any types or traits used by the context selectors need to be in scope — complicated paths may need to be simplified or made absolute. By default, the visibility of the generated module will be private while the context selectors inside will be `pub(super)`. Using [`#[snafu(visibility)]`](#controlling-visibility) to control the visibility will change the visibility of *both* the module and the context selectors. ## Controlling error sources ### Selecting the source field If your error enum variant contains other errors but the field cannot be named `source`, or if it contains a field named `source` which is not actually an error, you can use `#[snafu(source)]` to indicate if a field is an underlying cause or not: ```rust # mod another { # use snafu::prelude::*; # #[derive(Debug, Snafu)] # pub enum Error {} # } # use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { SourceIsNotAnError { #[snafu(source(false))] source: String, }, CauseIsAnError { #[snafu(source)] cause: another::Error, }, } ``` ### Transforming the source If your error type contains an underlying cause that needs to be transformed, you can use `#[snafu(source(from(...)))]`. This takes two arguments: the real type and an expression to transform from that type to the type held by the error. ```rust # mod another { # use snafu::prelude::*; # #[derive(Debug, Snafu)] # pub enum Error {} # } # use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { SourceNeedsToBeBoxed { #[snafu(source(from(another::Error, Box::new)))] source: Box, }, } #[derive(Debug, Snafu)] #[snafu(source(from(Error, Box::new)))] struct ApiError(Box); ``` Note: If you specify `#[snafu(source(from(...)))]` then the field will be treated as a source, even if it's not named "source" - in other words, `#[snafu(source(from(...)))]` implies `#[snafu(source)]`. ## Controlling backtraces If your error enum variant contains a backtrace but the field cannot be named `backtrace`, or if it contains a field named `backtrace` which is not actually a backtrace, you can use `#[snafu(backtrace)]` to indicate if a field is actually a backtrace or not: ```rust # use snafu::{prelude::*, Backtrace}; #[derive(Debug, Snafu)] enum Error { BacktraceIsNotABacktrace { #[snafu(backtrace(false))] backtrace: bool, }, TraceIsABacktrace { #[snafu(backtrace)] trace: Backtrace, }, } ``` If your error contains other SNAFU errors which can report backtraces, you may wish to delegate returning a backtrace to those errors. To specify this, use `#[snafu(backtrace)]` on the source field representing the other error: ```rust # mod another { # use snafu::prelude::*; # #[derive(Debug, Snafu)] # pub enum Error {} # } # use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { MyError { #[snafu(backtrace)] source: another::Error, }, } ``` ## Controlling implicitly generated data Sometimes, you can capture contextual error data without needing any arguments. [Backtraces][`Backtrace`] are a common example, but other global information like the current time or thread ID could also be useful. In these cases, you can use `#[snafu(implicit)]` on a field that implements [`GenerateImplicitData`] to remove the need to specify that data at error construction time: ```rust use snafu::prelude::*; use std::time::Instant; #[derive(Debug, PartialEq)] struct Timestamp(Instant); impl snafu::GenerateImplicitData for Timestamp { fn generate() -> Self { Timestamp(Instant::now()) } } #[derive(Debug, Snafu)] struct RequestError { #[snafu(implicit)] timestamp: Timestamp, } fn do_request() -> Result<(), RequestError> { // ... # let request_count = 10; ensure!(request_count < 3, RequestSnafu); Ok(()) } ``` You can use `#[snafu(implicit(false))]` if a field is incorrectly automatically identified as containing implicit data. ## Controlling stringly-typed errors This allows your custom error type to behave like the [`Whatever`][] error type. Since it is your type, you can implement additional methods or traits. When placed on a struct or enum variant, you will be able to use the type with the [`whatever!`][] macro as well as `whatever_context` methods, such as [`ResultExt::whatever_context`][]. ```rust # use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { SpecificError { username: String, }, #[snafu(whatever, display("{message}"))] GenericError { message: String, // Having a `source` is optional, but if it is present, it must // have this specific attribute and type: #[snafu(source(from(Box, Some)))] source: Option>, }, } ``` ## Controlling how the `snafu` crate is resolved If the `snafu` crate is not called `snafu` for some reason, you can use `#[snafu(crate_root)]` to instruct the macro how to find the crate root: ```rust # use snafu as my_custom_naming_of_snafu; use my_custom_naming_of_snafu::Snafu; #[derive(Debug, Snafu)] #[snafu(crate_root(my_custom_naming_of_snafu))] enum Error { SomeFailureMode, } #[derive(Debug, Snafu)] #[snafu(crate_root(my_custom_naming_of_snafu))] struct ApiError(Error); ``` snafu-0.7.1/src/backtrace_inert.rs000064400000000000000000000011630072674642500152530ustar 00000000000000use core::fmt; /// A backtrace starting from the beginning of the thread. /// /// Backtrace functionality is currently **disabled**. Please review /// [the feature flags](crate::guide::feature_flags) to enable it. #[derive(Debug)] pub struct Backtrace(()); impl crate::GenerateImplicitData for Backtrace { fn generate() -> Self { Backtrace(()) } } impl crate::AsBacktrace for Backtrace { fn as_backtrace(&self) -> Option<&Backtrace> { Some(self) } } impl fmt::Display for Backtrace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "disabled backtrace") } } snafu-0.7.1/src/backtrace_shim.rs000064400000000000000000000063510072674642500150760ustar 00000000000000use backtrace; use std::{fmt, path}; /// A backtrace starting from the beginning of the thread. /// /// Backtrace functionality is currently **enabled**. Please review /// [the feature flags](crate::guide::feature_flags) to disable it. #[derive(Debug)] pub struct Backtrace(backtrace::Backtrace); impl crate::GenerateImplicitData for Backtrace { // Inlining in an attempt to remove this function from the backtrace #[inline(always)] fn generate() -> Self { Backtrace(backtrace::Backtrace::new()) } } impl crate::AsBacktrace for Backtrace { fn as_backtrace(&self) -> Option<&Backtrace> { Some(self) } } impl fmt::Display for Backtrace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let frames = self.0.frames(); let width = (frames.len() as f32).log10().floor() as usize + 1; for (index, frame) in frames.iter().enumerate() { let mut symbols = frame.symbols().iter().map(SymbolDisplay); if let Some(symbol) = symbols.next() { writeln!( f, "{index:width$} {name}", index = index, width = width, name = symbol.name() )?; if let Some(location) = symbol.location() { writeln!( f, "{index:width$} {location}", index = "", width = width, location = location )?; } for symbol in symbols { writeln!( f, "{index:width$} {name}", index = "", width = width, name = symbol.name() )?; if let Some(location) = symbol.location() { writeln!( f, "{index:width$} {location}", index = "", width = width, location = location )?; } } } } Ok(()) } } struct SymbolDisplay<'a>(&'a backtrace::BacktraceSymbol); impl<'a> SymbolDisplay<'a> { fn name(&self) -> SymbolNameDisplay<'a> { SymbolNameDisplay(self.0) } fn location(&self) -> Option> { self.0.filename().map(|f| SymbolLocationDisplay(self.0, f)) } } struct SymbolNameDisplay<'a>(&'a backtrace::BacktraceSymbol); impl<'a> fmt::Display for SymbolNameDisplay<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.0.name() { Some(n) => write!(f, "{}", n)?, None => write!(f, "")?, } Ok(()) } } struct SymbolLocationDisplay<'a>(&'a backtrace::BacktraceSymbol, &'a path::Path); impl<'a> fmt::Display for SymbolLocationDisplay<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.1.display())?; if let Some(l) = self.0.lineno() { write!(f, ":{}", l)?; } Ok(()) } } snafu-0.7.1/src/error_chain.rs000064400000000000000000000013650072674642500144320ustar 00000000000000/// Backported version of the [`Chain`](std::error::Chain) struct, /// to versions of Rust lacking it. /// /// Can be created via [`ErrorCompat::iter_chain`][crate::ErrorCompat::iter_chain]. pub struct ChainCompat<'a> { inner: Option<&'a dyn std::error::Error>, } impl<'a> ChainCompat<'a> { /// Creates a new error chain iterator. pub fn new(error: &'a dyn std::error::Error) -> Self { ChainCompat { inner: Some(error) } } } impl<'a> Iterator for ChainCompat<'a> { type Item = &'a dyn std::error::Error; fn next(&mut self) -> Option { match self.inner { None => None, Some(e) => { self.inner = e.source(); Some(e) } } } } snafu-0.7.1/src/futures/mod.rs000064400000000000000000000007030072674642500144060ustar 00000000000000//! Additions to the [`TryFuture`] and [`TryStream`] traits. //! //! This module is only available when the `futures` [feature flag] is //! enabled. //! //! [`TryFuture`]: futures_core_crate::TryFuture //! [`TryStream`]: futures_core_crate::TryStream //! [feature flag]: crate::guide::feature_flags pub mod try_future; pub mod try_stream; #[doc(inline)] pub use self::try_future::TryFutureExt; #[doc(inline)] pub use self::try_stream::TryStreamExt; snafu-0.7.1/src/futures/try_future.rs000064400000000000000000000271270072674642500160500ustar 00000000000000//! Additions to the [`TryFuture`] trait. //! //! [`TryFuture`]: futures_core_crate::future::TryFuture use crate::{Error, ErrorCompat, FromString, IntoError}; use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context as TaskContext, Poll}, }; use futures_core_crate::future::TryFuture; use pin_project::pin_project; /// Additions to [`TryFuture`]. pub trait TryFutureExt: TryFuture + Sized { /// Extend a [`TryFuture`]'s error with additional context-sensitive /// information. /// /// ```rust /// # use futures_crate as futures; /// use futures::future::TryFuture; /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// Authenticating { /// user_name: String, /// user_id: i32, /// source: ApiError, /// }, /// } /// /// fn example() -> impl TryFuture { /// another_function().context(AuthenticatingSnafu { /// user_name: "admin", /// user_id: 42, /// }) /// } /// /// # type ApiError = Box; /// fn another_function() -> impl TryFuture { /// /* ... */ /// # futures::future::ok(42) /// } /// ``` /// /// Note that the context selector will call [`Into::into`] on /// each field, so the types are not required to exactly match. fn context(self, context: C) -> Context where C: IntoError, E: Error + ErrorCompat; /// Extend a [`TryFuture`]'s error with lazily-generated context-sensitive /// information. /// /// ```rust /// # use futures_crate as futures; /// use futures::future::TryFuture; /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// Authenticating { /// user_name: String, /// user_id: i32, /// source: ApiError, /// }, /// } /// /// fn example() -> impl TryFuture { /// another_function().with_context(|_| AuthenticatingSnafu { /// user_name: "admin".to_string(), /// user_id: 42, /// }) /// } /// /// # type ApiError = Box; /// fn another_function() -> impl TryFuture { /// /* ... */ /// # futures::future::ok(42) /// } /// ``` /// /// Note that this *may not* be needed in many cases because the /// context selector will call [`Into::into`] on each field. fn with_context(self, context: F) -> WithContext where F: FnOnce(&mut Self::Error) -> C, C: IntoError, E: Error + ErrorCompat; /// Extend a [`TryFuture`]'s error with information from a string. /// /// The target error type must implement [`FromString`] by using /// the /// [`#[snafu(whatever)]`][crate::Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`](crate::Whatever) type is also available. /// /// In many cases, you will want to use /// [`with_whatever_context`][Self::with_whatever_context] instead /// as it is only called in case of error. This method is best /// suited for when you have a string literal. /// /// ```rust /// # use futures_crate as futures; /// use futures::future::TryFuture; /// use snafu::{prelude::*, Whatever}; /// /// fn example() -> impl TryFuture { /// api_function().whatever_context("The API failed") /// } /// /// # type ApiError = Box; /// fn api_function() -> impl TryFuture { /// /* ... */ /// # futures::future::ok(42) /// } /// ``` fn whatever_context(self, context: S) -> WhateverContext where S: Into, E: FromString; /// Extend a [`TryFuture`]'s error with information from a /// lazily-generated string. /// /// The target error type must implement [`FromString`] by using /// the /// [`#[snafu(whatever)]`][crate::Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`](crate::Whatever) type is also available. /// /// ```rust /// # use futures_crate as futures; /// use futures::future::TryFuture; /// use snafu::{prelude::*, Whatever}; /// /// fn example(arg: &'static str) -> impl TryFuture { /// api_function(arg) /// .with_whatever_context(move |_| format!("The API failed for argument {}", arg)) /// } /// /// # type ApiError = Box; /// fn api_function(arg: &'static str) -> impl TryFuture { /// /* ... */ /// # futures::future::ok(42) /// } /// ``` fn with_whatever_context(self, context: F) -> WithWhateverContext where F: FnOnce(&mut Self::Error) -> S, S: Into, E: FromString; } impl TryFutureExt for Fut where Fut: TryFuture, { fn context(self, context: C) -> Context where C: IntoError, E: Error + ErrorCompat, { Context { inner: self, context: Some(context), _e: PhantomData, } } fn with_context(self, context: F) -> WithContext where F: FnOnce(&mut Self::Error) -> C, C: IntoError, E: Error + ErrorCompat, { WithContext { inner: self, context: Some(context), _e: PhantomData, } } fn whatever_context(self, context: S) -> WhateverContext where S: Into, E: FromString, { WhateverContext { inner: self, context: Some(context), _e: PhantomData, } } fn with_whatever_context(self, context: F) -> WithWhateverContext where F: FnOnce(&mut Self::Error) -> S, S: Into, E: FromString, { WithWhateverContext { inner: self, context: Some(context), _e: PhantomData, } } } /// Future for the [`context`](TryFutureExt::context) combinator. /// /// See the [`TryFutureExt::context`] method for more details. #[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless polled"] pub struct Context { #[pin] inner: Fut, context: Option, _e: PhantomData, } impl Future for Context where Fut: TryFuture, C: IntoError, E: Error + ErrorCompat, { type Output = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll { let this = self.project(); let inner = this.inner; let context = this.context; // https://github.com/rust-lang/rust/issues/74042 match inner.try_poll(ctx) { Poll::Ready(Ok(v)) => Poll::Ready(Ok(v)), Poll::Ready(Err(error)) => { let error = context .take() .expect("Cannot poll Context after it resolves") .into_error(error); Poll::Ready(Err(error)) } Poll::Pending => Poll::Pending, } } } /// Future for the [`with_context`](TryFutureExt::with_context) combinator. /// /// See the [`TryFutureExt::with_context`] method for more details. #[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless polled"] pub struct WithContext { #[pin] inner: Fut, context: Option, _e: PhantomData, } impl Future for WithContext where Fut: TryFuture, F: FnOnce(&mut Fut::Error) -> C, C: IntoError, E: Error + ErrorCompat, { type Output = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll { let this = self.project(); let inner = this.inner; let context = this.context; // https://github.com/rust-lang/rust/issues/74042 match inner.try_poll(ctx) { Poll::Ready(Ok(v)) => Poll::Ready(Ok(v)), Poll::Ready(Err(mut error)) => { let context = context .take() .expect("Cannot poll WithContext after it resolves"); let error = context(&mut error).into_error(error); Poll::Ready(Err(error)) } Poll::Pending => Poll::Pending, } } } /// Future for the /// [`whatever_context`](TryFutureExt::whatever_context) combinator. /// /// See the [`TryFutureExt::whatever_context`] method for more /// details. #[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless polled"] pub struct WhateverContext { #[pin] inner: Fut, context: Option, _e: PhantomData, } impl Future for WhateverContext where Fut: TryFuture, S: Into, E: FromString, Fut::Error: Into, { type Output = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll { let this = self.project(); let inner = this.inner; let context = this.context; // https://github.com/rust-lang/rust/issues/74042 match inner.try_poll(ctx) { Poll::Ready(Ok(v)) => Poll::Ready(Ok(v)), Poll::Ready(Err(error)) => { let context = context .take() .expect("Cannot poll WhateverContext after it resolves"); let error = FromString::with_source(error.into(), context.into()); Poll::Ready(Err(error)) } Poll::Pending => Poll::Pending, } } } /// Future for the /// [`with_whatever_context`](TryFutureExt::with_whatever_context) /// combinator. /// /// See the [`TryFutureExt::with_whatever_context`] method for more /// details. #[pin_project] #[derive(Debug)] #[must_use = "futures do nothing unless polled"] pub struct WithWhateverContext { #[pin] inner: Fut, context: Option, _e: PhantomData, } impl Future for WithWhateverContext where Fut: TryFuture, F: FnOnce(&mut Fut::Error) -> S, S: Into, E: FromString, Fut::Error: Into, { type Output = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll { let this = self.project(); let inner = this.inner; let context = this.context; // https://github.com/rust-lang/rust/issues/74042 match inner.try_poll(ctx) { Poll::Ready(Ok(v)) => Poll::Ready(Ok(v)), Poll::Ready(Err(mut error)) => { let context = context .take() .expect("Cannot poll WhateverContext after it resolves"); let context = context(&mut error); let error = FromString::with_source(error.into(), context.into()); Poll::Ready(Err(error)) } Poll::Pending => Poll::Pending, } } } snafu-0.7.1/src/futures/try_stream.rs000064400000000000000000000261630072674642500160300ustar 00000000000000//! Additions to the [`TryStream`] trait. //! //! [`TryStream`]: futures_core_crate::TryStream use crate::{Error, ErrorCompat, FromString, IntoError}; use core::{ marker::PhantomData, pin::Pin, task::{Context as TaskContext, Poll}, }; use futures_core_crate::stream::{Stream, TryStream}; use pin_project::pin_project; /// Additions to [`TryStream`]. pub trait TryStreamExt: TryStream + Sized { /// Extend a [`TryStream`]'s error with additional context-sensitive /// information. /// /// ```rust /// # use futures_crate as futures; /// use futures::TryStream; /// # use futures::stream; /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// Authenticating { /// user_name: String, /// user_id: i32, /// source: ApiError, /// }, /// } /// /// fn example() -> impl TryStream { /// stock_prices().context(AuthenticatingSnafu { /// user_name: "admin", /// user_id: 42, /// }) /// } /// /// # type ApiError = Box; /// fn stock_prices() -> impl TryStream { /// /* ... */ /// # stream::empty() /// } /// ``` /// /// Note that the context selector will call [`Into::into`] on /// each field, so the types are not required to exactly match. fn context(self, context: C) -> Context where C: IntoError + Clone, E: Error + ErrorCompat; /// Extend a [`TryStream`]'s error with lazily-generated /// context-sensitive information. /// /// ```rust /// # use futures_crate as futures; /// use futures::TryStream; /// # use futures::stream; /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// Authenticating { /// user_name: String, /// user_id: i32, /// source: ApiError, /// }, /// } /// /// fn example() -> impl TryStream { /// stock_prices().with_context(|_| AuthenticatingSnafu { /// user_name: "admin", /// user_id: 42, /// }) /// } /// /// # type ApiError = Box; /// fn stock_prices() -> impl TryStream { /// /* ... */ /// # stream::empty() /// } /// ``` /// /// Note that this *may not* be needed in many cases because the /// context selector will call [`Into::into`] on each field. fn with_context(self, context: F) -> WithContext where F: FnMut(&mut Self::Error) -> C, C: IntoError, E: Error + ErrorCompat; /// Extend a [`TryStream`]'s error with information from a string. /// /// The target error type must implement [`FromString`] by using /// the /// [`#[snafu(whatever)]`][crate::Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`](crate::Whatever) type is also available. /// /// In many cases, you will want to use /// [`with_whatever_context`][Self::with_whatever_context] instead /// as it is only called in case of error. This method is best /// suited for when you have a string literal. /// /// ```rust /// # use futures_crate as futures; /// use futures::TryStream; /// # use futures::stream; /// use snafu::{prelude::*, Whatever}; /// /// fn example() -> impl TryStream { /// stock_prices().whatever_context("Couldn't get stock prices") /// } /// /// # type ApiError = Box; /// fn stock_prices() -> impl TryStream { /// /* ... */ /// # stream::empty() /// } /// ``` fn whatever_context(self, context: S) -> WhateverContext where S: Into, E: FromString; /// Extend a [`TryStream`]'s error with information from a /// lazily-generated string. /// /// The target error type must implement [`FromString`] by using /// the /// [`#[snafu(whatever)]`][crate::Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`](crate::Whatever) type is also available. /// /// ```rust /// # use futures_crate as futures; /// use futures::TryStream; /// # use futures::stream; /// use snafu::{prelude::*, Whatever}; /// /// fn example(symbol: &'static str) -> impl TryStream { /// stock_prices(symbol) /// .with_whatever_context(move |_| format!("Couldn't get stock prices for {}", symbol)) /// } /// /// # type ApiError = Box; /// fn stock_prices(symbol: &'static str) -> impl TryStream { /// /* ... */ /// # stream::empty() /// } /// ``` fn with_whatever_context(self, context: F) -> WithWhateverContext where F: FnMut(&mut Self::Error) -> S, S: Into, E: FromString; } impl TryStreamExt for St where St: TryStream, { fn context(self, context: C) -> Context where C: IntoError + Clone, E: Error + ErrorCompat, { Context { inner: self, context, _e: PhantomData, } } fn with_context(self, context: F) -> WithContext where F: FnMut(&mut Self::Error) -> C, C: IntoError, E: Error + ErrorCompat, { WithContext { inner: self, context, _e: PhantomData, } } fn whatever_context(self, context: S) -> WhateverContext where S: Into, E: FromString, { WhateverContext { inner: self, context, _e: PhantomData, } } fn with_whatever_context(self, context: F) -> WithWhateverContext where F: FnMut(&mut Self::Error) -> S, S: Into, E: FromString, { WithWhateverContext { inner: self, context, _e: PhantomData, } } } /// Stream for the [`context`](TryStreamExt::context) combinator. /// /// See the [`TryStreamExt::context`] method for more details. #[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct Context { #[pin] inner: St, context: C, _e: PhantomData, } impl Stream for Context where St: TryStream, C: IntoError + Clone, E: Error + ErrorCompat, { type Item = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll_next(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll> { let this = self.project(); let inner = this.inner; let context = this.context; match inner.try_poll_next(ctx) { Poll::Pending => Poll::Pending, Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(Ok(v))) => Poll::Ready(Some(Ok(v))), Poll::Ready(Some(Err(error))) => { let error = context.clone().into_error(error); Poll::Ready(Some(Err(error))) } } } } /// Stream for the [`with_context`](TryStreamExt::with_context) combinator. /// /// See the [`TryStreamExt::with_context`] method for more details. #[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct WithContext { #[pin] inner: St, context: F, _e: PhantomData, } impl Stream for WithContext where St: TryStream, F: FnMut(&mut St::Error) -> C, C: IntoError, E: Error + ErrorCompat, { type Item = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll_next(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll> { let this = self.project(); let inner = this.inner; let context = this.context; match inner.try_poll_next(ctx) { Poll::Pending => Poll::Pending, Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(Ok(v))) => Poll::Ready(Some(Ok(v))), Poll::Ready(Some(Err(mut error))) => { let error = context(&mut error).into_error(error); Poll::Ready(Some(Err(error))) } } } } /// Stream for the /// [`whatever_context`](TryStreamExt::whatever_context) combinator. /// /// See the [`TryStreamExt::whatever_context`] method for more /// details. #[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct WhateverContext { #[pin] inner: St, context: S, _e: PhantomData, } impl Stream for WhateverContext where St: TryStream, S: Into + Clone, E: FromString, St::Error: Into, { type Item = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll_next(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll> { let this = self.project(); let inner = this.inner; let context = this.context; match inner.try_poll_next(ctx) { Poll::Pending => Poll::Pending, Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(Ok(v))) => Poll::Ready(Some(Ok(v))), Poll::Ready(Some(Err(error))) => { let error = E::with_source(error.into(), context.clone().into()); Poll::Ready(Some(Err(error))) } } } } /// Stream for the /// [`with_whatever_context`](TryStreamExt::with_whatever_context) /// combinator. /// /// See the [`TryStreamExt::with_whatever_context`] method for more /// details. #[pin_project] #[derive(Debug)] #[must_use = "streams do nothing unless polled"] pub struct WithWhateverContext { #[pin] inner: St, context: F, _e: PhantomData, } impl Stream for WithWhateverContext where St: TryStream, F: FnMut(&mut St::Error) -> S, S: Into, E: FromString, St::Error: Into, { type Item = Result; #[cfg_attr(feature = "rust_1_46", track_caller)] fn poll_next(self: Pin<&mut Self>, ctx: &mut TaskContext) -> Poll> { let this = self.project(); let inner = this.inner; let context = this.context; match inner.try_poll_next(ctx) { Poll::Pending => Poll::Pending, Poll::Ready(None) => Poll::Ready(None), Poll::Ready(Some(Ok(v))) => Poll::Ready(Some(Ok(v))), Poll::Ready(Some(Err(mut error))) => { let context = context(&mut error); let error = E::with_source(error.into(), context.into()); Poll::Ready(Some(Err(error))) } } } } snafu-0.7.1/src/guide/comparison/failure.md000064400000000000000000000072210072674642500170060ustar 00000000000000# SNAFU vs. Failure This comparison was made against the examples in [the guide for failure 0.1.8][failure-guide]. [failure-guide]: https://rust-lang-nursery.github.io/failure/guidance.html ## "Strings as errors" If you wanted to do something similar with SNAFU, you can use the [`Whatever`](crate::Whatever) type: ```rust use snafu::{prelude::*, Whatever}; use std::ops::Range; fn check_range(x: usize, range: Range) -> Result { if x < range.start { whatever!("{} is below {}", x, range.start); } if x >= range.end { whatever!("{} is above {}", x, range.end); } Ok(x) } ``` ## "A Custom Fail type" and "Using the Error type" These two idioms from Failure are combined into one primary use case in SNAFU. Additionally, SNAFU avoids the downsides listed in the Failure guide. You can represent multiple types of errors, allocation is not required, and you can include any extra information relevant to the error: ```rust use snafu::prelude::*; use std::ops::Range; #[derive(Debug, Snafu)] enum Error { #[snafu(display("{value} is below {bound}"))] Below { value: usize, bound: usize }, #[snafu(display("{value} is above {bound}"))] Above { value: usize, bound: usize }, } fn check_range(value: usize, range: Range) -> Result { ensure!( value >= range.start, BelowSnafu { value, bound: range.start, }, ); ensure!( value < range.end, AboveSnafu { value, bound: range.end, }, ); Ok(value) } ``` You do not have to have a one-to-one relationship between an underlying error and an error variant: ```rust use snafu::prelude::*; use std::num::ParseIntError; #[derive(Debug, Snafu)] enum Error { #[snafu(display(r#"Could not parse the area code from "{value}": {source}"#))] AreaCodeInvalid { value: String, source: ParseIntError, }, #[snafu(display(r#"Could not parse the phone exchange from "{value}": {source}"#))] PhoneExchangeInvalid { value: String, source: ParseIntError, }, } fn two_errors_from_same_underlying_error( area_code: &str, exchange: &str, ) -> Result<(i32, i32), Error> { let area_code: i32 = area_code .parse() .context(AreaCodeInvalidSnafu { value: area_code })?; let exchange: i32 = exchange .parse() .context(PhoneExchangeInvalidSnafu { value: exchange })?; Ok((area_code, exchange)) } ``` ## "An Error and ErrorKind pair" If you choose to make your error type [opaque][], you can implement methods on the opaque type, allowing you to selectively choose what your public API is. This includes the ability to return a different public enum that users can match on without knowing the details of your error implementation. ```rust use snafu::prelude::*; #[derive(Debug, Snafu)] enum InnerError { MyError1 { username: String }, MyError2 { username: String }, MyError3 { address: String }, } #[derive(Debug, Snafu)] pub struct Error(InnerError); #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ErrorKind { Authorization, Network, } impl Error { pub fn kind(&self) -> ErrorKind { use InnerError::*; match self.0 { MyError1 { .. } | MyError2 { .. } => ErrorKind::Authorization, MyError3 { .. } => ErrorKind::Network, } } pub fn username(&self) -> Option<&str> { use InnerError::*; match &self.0 { MyError1 { username } | MyError2 { username } => Some(username), _ => None, } } } # fn main() {} ``` [opaque]: crate::guide::opaque snafu-0.7.1/src/guide/comparison.md000064400000000000000000000011300072674642500153500ustar 00000000000000# Comparisons to similar libraries This section of the guide contains comparisons of other popular libraries and SNAFU, as well as lightweight migration tips to SNAFU from those libraries. Please note that these comparisons are *intended* to be fair, but are written by the author of SNAFU. If there are any factual errors, please [file an issue][issue] so it can be fixed. If you've identified a better technique for migrating from another crate to SNAFU, please let us know as well! - [Failure][] [issue]: https://github.com/shepmaster/snafu/issues [Failure]: crate::guide::comparison::failure snafu-0.7.1/src/guide/compatibility.md000064400000000000000000000006510072674642500160560ustar 00000000000000## Rust version compatibility SNAFU is tested and compatible back to Rust 1.34, released on 2019-05-14. Compatibility is controlled by Cargo feature flags. ## `rust_1_46` **default**: enabled When enabled, SNAFU will assume that it's safe to target features available in Rust 1.46. Notably, the `#[track_caller]` feature is needed to allow [`Location`][crate::Location] to automatically discern the source code location. snafu-0.7.1/src/guide/examples/backtrace.rs000064400000000000000000000076570072674642500170030ustar 00000000000000//! Exposing complete backtraces to the location of the error. //! //! Start by looking at the error type [`Error`]. use crate::{Snafu, Backtrace, ErrorCompat, GenerateImplicitData}; /// Backtraces aren't yet supported by the stable Rust compiler. SNAFU /// provides a stable-compatible way of getting backtraces as well as /// support for the backtrace support in the nightly compiler. **By /// default, backtraces are disabled**. It is /// expected that the final executable adds SNAFU as a dependency and /// chooses the appropriate [feature /// flag](crate::guide::feature_flags) to enable backtraces. /// /// When using SNAFU to define error types, it's recommended to start /// with a [`Backtrace`] field on every leaf error variant (those /// without a `source`). Backtraces are only captured on /// failure. Since backtraces are disabled by default, adding them in /// a library does not force any users to pay the price of backtraces /// if they are not used; they can be zero cost. /// /// Certain errors are used for flow control. Those don't need a /// backtrace as they don't represent actual failures. However, /// sometimes an error is *mostly* used for flow control but might /// also indicate an error. In those cases, you can use /// `Option` to avoid capturing a backtrace unless an /// environment variable is set by the end user to provide additional /// debugging. /// /// For variants that do have a source, you need to evaluate if the /// source error provides a backtrace of some kind. If it is another /// SNAFU error, for example, you can *delegate* retrieval of the /// backtrace to the source error. If the source error doesn't provide /// its own backtrace, you should capture your own backtrace. This /// backtrace would not be as useful as one captured by the source /// error, but it's as useful as you can get. /// /// When you wish to display the backtrace of an error, you can use /// the [`ErrorCompat::backtrace`] method. It's recommended to always /// use this in the fully-qualified form so it will be easy to find /// and replace when Rust stabilizes backtraces. /// /// ``` /// # use snafu::guide::examples::backtrace::*; /// use snafu::ErrorCompat; /// /// fn inner_process() -> Result<(), Error> { /// // Complicated logic /// # UsualCaseSnafu.fail() /// } /// /// fn main() { /// if let Err(e) = inner_process() { /// eprintln!("An error occurred: {}", e); /// if let Some(bt) = ErrorCompat::backtrace(&e) { /// eprintln!("{:?}", bt); /// } /// } /// } /// ``` #[derive(Debug, Snafu)] // This line is only needed to generate documentation; it is not // needed in most cases: #[snafu(crate_root(crate), visibility(pub))] pub enum Error { /// The most common leaf error should always include a backtrace field. UsualCase { backtrace: Backtrace, }, /// When an error is expected to be created frequently but the /// backtrace is rarely needed, you can wrap it in an /// `Option`. See [the instructions][] on how to access the /// backtrace in this case. /// /// [the instructions]: GenerateImplicitData#impl-GenerateImplicitData-for-Option UsedInTightLoop { backtrace: Option, }, /// This error wraps another error that already has a /// backtrace. Instead of capturing our own, we forward the /// request for the backtrace to the inner error. This gives a /// more accurate backtrace. SnafuErrorAsSource { #[snafu(backtrace)] source: ConfigFileError, }, /// This error wraps another error that does not expose a /// backtrace. We capture our own backtrace to provide something /// useful. SourceErrorDoesNotHaveBacktrace { source: std::io::Error, backtrace: Backtrace, }, } /// This is a placeholder example and can be ignored. #[derive(Debug, Snafu)] #[snafu(crate_root(crate))] pub enum ConfigFileError { Dummy { backtrace: Backtrace }, } snafu-0.7.1/src/guide/examples/basic.rs000064400000000000000000000033310072674642500161260ustar 00000000000000//! The most common usage of SNAFU — an enumeration of possible errors. //! //! Start by looking at the error type [`Error`], then view the //! *context selectors* [`LeafSnafu`] and [`IntermediateSnafu`]. use crate::{Snafu, ResultExt}; /// An enumeration of possible errors. /// /// This will create a number of *context selectors*: /// /// - [`LeafSnafu`] /// - [`IntermediateSnafu`] /// /// ## Leaf errors /// /// Context selectors for error variants without a `source`, such /// as [`LeafSnafu`], have methods to construct them, such as /// [`LeafSnafu::build`] or [`LeafSnafu::fail`]. The [`ensure`] macro also /// accepts these kinds of context selectors. /// /// ``` /// # use snafu::guide::examples::basic::*; /// use snafu::prelude::*; /// /// fn always_fails() -> Result<(), Error> { /// LeafSnafu { user_id: 42 }.fail() /// } /// /// fn sometimes_fails(user_id: i32) -> Result<(), Error> { /// ensure!(user_id > 0, LeafSnafu { user_id }); /// Ok(()) /// } /// ``` /// /// ## Intermediate errors /// /// Context selectors for error variants with a `source`, such as /// [`IntermediateSnafu`], are intended to be used with the /// [`ResultExt::context`] family of methods. /// /// ``` /// # use snafu::guide::examples::basic::*; /// use snafu::prelude::*; /// /// fn load_config_file() -> Result { /// let config = std::fs::read_to_string("/path/to/my/config/file").context(IntermediateSnafu)?; /// Ok(config.len()) /// } /// ``` #[derive(Debug, Snafu)] // This line is only needed to generate documentation; it is not // needed in most cases: #[snafu(crate_root(crate), visibility(pub))] pub enum Error { Leaf { user_id: i32, }, Intermediate { source: std::io::Error, }, } snafu-0.7.1/src/guide/examples.rs000064400000000000000000000014260072674642500150500ustar 00000000000000#![allow(unused, missing_docs)] // This file is for demo purposes //! Examples of using SNAFU //! //! You are encouraged to view the Rust source for each error type by //! clicking the `[src]` link in the upper-right of each documentation //! page. This shows how you would accomplish the same task in your //! code. //! //! The suggested order to browse them is: //! //! 1. [`basic`] //! 1. [`backtrace`] //! //! You should also feel free to browse the [acceptance tests][tests] //! and [compatibility tests][compat], which cover a broad range of //! functionality but with minimal descriptive prose. //! //! [tests]: https://github.com/shepmaster/snafu/tree/master/tests //! [compat]: https://github.com/shepmaster/snafu/tree/master/compatibility-tests pub mod backtrace; pub mod basic; snafu-0.7.1/src/guide/feature_flags.md000064400000000000000000000045310072674642500160150ustar 00000000000000# Optional extensions to the crate In addition to the feature flags [controlling compatibility], there are Cargo [feature flags] that extend SNAFU for various use cases: - [`std`](#std) - [`guide`](#guide) - [`backtraces`](#backtraces) - [`backtraces-impl-backtrace-crate`](#backtraces-impl-backtrace-crate) - [`unstable-backtraces-impl-std`](#unstable-backtraces-impl-std) - [`futures`](#futures) [controlling compatibility]: super::guide::compatibility [feature flags]: https://doc.rust-lang.org/stable/cargo/reference/specifying-dependencies.html#choosing-features ## `std` **default**: enabled When enabled, SNAFU will implement the `std::error::Error` trait. When disabled, SNAFU will instead implement a custom `Error` trait that is similar, but does not need any features from the standard library. Most usages of SNAFU will want this feature enabled. ## `guide` **default**: disabled When enabled, the `guide` module containing the user's guide will be built. Most usages of SNAFU will want this feature disabled. ## `backtraces` **default**: disabled When enabled, the [`Backtrace`] type in your enum variant will capture a backtrace when the error is generated. If you never use backtraces, you can omit this feature to speed up compilation a small amount. It is recommended that only applications make use of this feature. [`Backtrace`]: crate::Backtrace ## `backtraces-impl-backtrace-crate` **default**: disabled When enabled, the SNAFU [`Backtrace`] type becomes an alias to the `backtrace::Backtrace` type. This allows interoperability with other crates that require this type. It is recommended that only applications make use of this feature. When the standard library stabilizes its own backtrace type, this feature will no longer be supported and will be removed. ## `unstable-backtraces-impl-std` **default**: disabled When enabled, the SNAFU [`Backtrace`] type becomes an alias to the [`std::backtrace::Backtrace`] type and `std::error::Error::backtrace` is implemented. It is recommended that only applications make use of this feature. ## `futures` **default**: disabled When enabled, you can use the [`futures::TryFutureExt`] and [`futures::TryStreamExt`] traits to add context methods to futures and streams returning `Result`s. [`futures::TryFutureExt`]: crate::futures::TryFutureExt [`futures::TryStreamExt`]: crate::futures::TryStreamExt snafu-0.7.1/src/guide/generics.md000064400000000000000000000046270072674642500150130ustar 00000000000000# Using generic types Error types enhanced by SNAFU may contain generic type and lifetime parameters. ## Types ```rust # use snafu::prelude::*; # #[derive(Debug, Snafu)] enum Error where T: std::fmt::Display, { #[snafu(display("The value {value} was too large"))] TooLarge { value: T, limit: u32 }, #[snafu(display("The value {value} was too small"))] TooSmall { value: T, limit: u32 }, } fn validate_number(value: u8) -> Result> { ensure!( value <= 200, TooLargeSnafu { value, limit: 100u32, }, ); ensure!( value >= 100, TooSmallSnafu { value, limit: 200u32, }, ); Ok(value) } fn validate_string(value: &str) -> Result<&str, Error> { ensure!( value.len() <= 20, TooLargeSnafu { value, limit: 10u32, }, ); ensure!( value.len() >= 10, TooSmallSnafu { value, limit: 20u32, }, ); Ok(value) } ``` ## Lifetimes ```rust # use snafu::prelude::*; # #[derive(Debug, Snafu)] enum Error<'a> { #[snafu(display("The username {value} contains the bad word {word}"))] BadWord { value: &'a str, word: &'static str }, } fn validate_username<'a>(value: &'a str) -> Result<&'a str, Error<'a>> { ensure!( !value.contains("stinks"), BadWordSnafu { value, word: "stinks", }, ); ensure!( !value.contains("smells"), BadWordSnafu { value, word: "smells", }, ); Ok(value) } ``` ## Caveats A SNAFU [opaque type](crate::guide::opaque) requires that the contained type implements several traits, such as `Display`. However, type constraints cannot be automatically added to the opaque type because they are not allowed to reference the inner type without also exposing it publicly. The best option is to avoid using a generic opaque error. If you choose to expose a generic opaque error, you will likely need to add explicit duplicate type constraints: ```rust use snafu::prelude::*; #[derive(Debug, Snafu)] struct ApiError(Error) // The bound is required to ensure that delegation can work. where T: std::fmt::Debug; #[derive(Debug, Snafu)] enum Error where T: std::fmt::Debug, { #[snafu(display("Boom: {value:?}"))] Boom { value: T }, } ``` snafu-0.7.1/src/guide/opaque.md000064400000000000000000000030140072674642500144730ustar 00000000000000# How to create opaque error types for public APIs While creating error types on top of Rust enums allows for great flexibility inside your code, that same flexibility may not be desired in a public API. Public enums also expose their variants and the variant's fields, allowing consumers to rely on those details. The most conservative approach is to create an *opaque* error type that only implements a handful of traits. This can be done by deriving `Snafu` for a newtype struct that contains another SNAFU error: ```rust # use snafu::prelude::*; #[derive(Debug, Snafu)] pub struct Error(InnerError); // That's all it takes! The rest is demonstration of how to use it. pub fn login(id: i32) -> Result<(), Error> { validate_user(id)?; is_user_locked(id)?; Ok(()) } #[derive(Debug, Snafu)] enum InnerError { #[snafu(display("User ID {user_id} is invalid"))] InvalidUser { user_id: i32 }, #[snafu(display("User ID {user_id} is locked"))] UserLocked { user_id: i32 }, } fn validate_user(user_id: i32) -> Result<(), InnerError> { InvalidUserSnafu { user_id }.fail() } fn is_user_locked(user_id: i32) -> Result<(), InnerError> { UserLockedSnafu { user_id }.fail() } ``` ## Delegated traits - [`Error`][] - [`Display`][] - [`ErrorCompat`][] [`Error`]: std::error::Error [`Display`]: std::fmt::Display [`ErrorCompat`]: crate::ErrorCompat ## `From` The `From` trait is also implemented to convert the inner type into the opaque type. This makes converting from internal errors to public errors very easy. snafu-0.7.1/src/guide/philosophy.md000064400000000000000000000013630072674642500154040ustar 00000000000000# SNAFU's design philosophy SNAFU believes in several points that are reflected in its design, development, and maintenance. Knowing them may help users more effectively use SNAFU. ## Categorize underlying errors by their context It should be easy to bin one underlying error type (such as [`io::Error`][Error]) into multiple domain-specific errors while optionally adding contextual information. [Error]: std::io::Error ## Library vs. application SNAFU is designed to be used equally well in libraries and end-user applications. ## Many error types Each module should have one (or more!) error types that are scoped to that module, reducing the need to deal with unrelated errors when matching and increasing cohesiveness of a single error type. snafu-0.7.1/src/guide/structs.md000064400000000000000000000014260072674642500147150ustar 00000000000000# Struct errors You may not always need the flexibility of an enum for your error type. In those cases, you can use the familiar SNAFU attributes with a struct: ```rust # use std::convert::TryFrom; # use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(display("Unable to parse {value} as MyEnum"))] struct ParseError { value: u8, } // That's all it takes! The rest is demonstration of how to use it. #[derive(Debug)] enum MyEnum { Alpha, Beta, Gamma, } impl TryFrom for MyEnum { type Error = ParseError; fn try_from(other: u8) -> Result { match other { 0 => Ok(Self::Alpha), 1 => Ok(Self::Beta), 2 => Ok(Self::Gamma), value => ParseSnafu { value }.fail(), } } } ``` snafu-0.7.1/src/guide/the_macro.md000064400000000000000000000122340072674642500151460ustar 00000000000000# Details about the output of the `Snafu` macro This procedural macro: - produces the corresponding context selectors - implements the [`Error`][Error] trait - implements the [`Display`][Display] trait - implements the [`ErrorCompat`][ErrorCompat] trait ## Detailed example ```rust use snafu::{prelude::*, Backtrace}; use std::path::PathBuf; #[derive(Debug, Snafu)] enum Error { #[snafu(display("Could not open config at {}", filename.display()))] OpenConfig { filename: PathBuf, source: std::io::Error, }, #[snafu(display("Could not open config"))] SaveConfig { source: std::io::Error }, #[snafu(display("The user id {user_id} is invalid"))] UserIdInvalid { user_id: i32, backtrace: Backtrace }, #[snafu(display("Could not validate config with key {key}: checksum was {checksum}"))] ConfigValidationFailed { checksum: u64, key: String, source: crypto::Error, }, } # mod crypto { #[derive(Debug, snafu::Snafu)] pub struct Error; } ``` ### Generated code
**Note** — The actual generated code may differ in exact names and details. This section is only intended to provide general guidance. Use a tool like [cargo-expand][] to see the exact generated code for your situation.
[cargo-expand]: https://crates.io/crates/cargo-expand #### Context selectors Each enum variant will generate an additional type called a *context selector*: ```rust,ignore struct OpenConfigSnafu

{ filename: P, } struct SaveConfigSnafu

; struct UserIdInvalidSnafu { user_id: I, } struct ConfigValidationFailedSnafu { checksum: u64, key: String, source: crypto::Error, } ``` Notably: 1. The name of the selector is the enum variant's name with the suffix `Snafu` added. If the name originally ended in `Error`, that is removed. 1. The `source` and `backtrace` fields have been removed; the library will automatically handle these for you. 1. Each remaining field's type has been replaced with a generic type. 1. If there are no fields remaining for the user to specify, the selector will not require curly braces. If the original variant had a `source` field, its context selector will have an implementation of [`IntoError`][IntoError]: ```rust,ignore impl

IntoError for OpenConfigSnafu

where P: Into, ``` Otherwise, the context selector will have the inherent methods `build` and `fail` and can be used with the [`ensure`](ensure) macro: ```rust,ignore impl UserIdInvalidSnafu where I: Into, { fn build(self) -> Error { /* ... */ } fn fail(self) -> Result { /* ... */ } } ``` If the original variant had a `backtrace` field, the backtrace will be automatically constructed when either `IntoError` or `build`/`fail` are called. #### `Error` [`Error::source`][source] will return the underlying error, if there is one: ```rust,ignore impl std::error::Error for Error { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Error::OpenConfig { source, .. } => Some(source), Error::SaveConfig { source, .. } => Some(source), Error::UserIdInvalid { .. } => None, Error::ConfigValidationFailed { source, .. } => Some(source), } } } ``` [`Error::cause`][cause] will return the same as `source`. As [`Error::description`][description] is soft-deprecated, it will return a string matching the name of the variant. #### `Display` Every field of the enum variant is made available to the format string, even if they are not used: ```rust,ignore impl std::fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter ) -> fmt::Result { match self { Error::OpenConfig { filename, source } => write!(f, "Could not open config at {}", filename.display()), Error::SaveConfig { source } => write!(f, "Could not open config"), Error::UserIdInvalid { user_id, backtrace } => write!(f, "The user id {} is invalid", user_id = user_id), Error::ConfigValidationFailed { key, checksum, source } => write!(f, "Could not validate config with key {}: checksum was {}", key = key, checksum = checksum), } } } ``` If no display format is specified, the variant's name will be used by default. If the field is an underlying error, that error's `Display` implementation will also be included. #### `ErrorCompat` Every variant that carries a backtrace will return a reference to that backtrace. ```rust,ignore impl snafu::ErrorCompat for Error { fn backtrace(&self) -> Option<&Backtrace> { match self { Error::OpenConfig { .. } => None, Error::SaveConfig { .. } => None, Error::UserIdInvalid { backtrace, .. } => Some(backtrace), Error::ConfigValidationFailed { .. } => None, } } } ``` [Display]: std::fmt::Display [ErrorCompat]: crate::ErrorCompat [Error]: std::error::Error [IntoError]: crate::IntoError [cause]: std::error::Error::cause [description]: std::error::Error::description [source]: std::error::Error::source snafu-0.7.1/src/guide/troubleshooting/missing_field_source.md000064400000000000000000000045740072674642500226400ustar 00000000000000# Missing field source / IntoError is not implemented This error is encountered in multi-module / multi-file projects when the error type is defined in one module and constructed in another. ## Failing Example **project_error.rs** ```rust,ignore use snafu::prelude::*; use std::io; #[derive(Debug, Snafu)] #[snafu(visibility(pub(crate)))] pub enum ProjectError { #[snafu(display("Unable to read configuration from {path}: {source}"))] IOConfigError { path: &'static str, source: io::Error, }, } ``` **main.rs** ```rust,ignore mod project_error; use project_error::ProjectError; use snafu::prelude::*; use std::fs; const CONFIG_PATH: &str = "/etc/example/conf.conf"; pub fn read_config() -> Result { fs::read_to_string(CONFIG_PATH).context(ProjectError::IOConfigError { path: CONFIG_PATH }) } pub fn main() { println!("{}", read_config().unwrap()); } ``` ## Errors ```text error[E0063]: missing field `source` in initializer of `ProjectError` --> examples/scratch.rs:23:45 | 23 | fs::read_to_string(CONFIG_PATH).context(ProjectError::IOConfigError { path: CONFIG_PATH }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `source` ``` and ```text error[E0277]: the trait bound `ProjectError: IntoError<_>` is not satisfied --> examples/scratch.rs:23:45 | 23 | fs::read_to_string(CONFIG_PATH).context(ProjectError::IOConfigError { path: CONFIG_PATH }) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoError<_>` is not implemented for `ProjectError` ``` ## Solution Replace the `ProjectError::IOConfigError` in the `read_config()` function with `project_error::IOConfigSnafu`. ## Explanation This works because the `#[derive(Snafu)]` macro creates the *context selector* type `IoConfigSnafu`: ```rust,ignore #[derive(Debug, Snafu)] pub enum ProjectError { IOConfigError { source: io::Error, path: &'static str, }, } // some details removed struct IOConfigSnafu

{ path: P, } // Some impls for the IOConfigError struct ``` See [the macro section](guide::the_macro) of the guide for more details. When you use `ProjectError::IOConfigError`, you're referencing the enum variant, not the struct that you need. Replacing `ProjectError::IOConfigError` with `project_error::IOConfigSnafu` fixes this problem. snafu-0.7.1/src/guide/troubleshooting.md000064400000000000000000000013170072674642500164340ustar 00000000000000# Troubleshooting common issues This section covers some commonly asked and encountered questions. If you experience a problem and spend a significant amount of time answering it, please consider [submitting a pull request][pr] to add it here. If you cannot solve your issue after reading this, please [open an issue][issue] to ask your question. [pr]: https://github.com/shepmaster/snafu/blob/master/CONTRIBUTING.md [issue]: https://github.com/shepmaster/snafu/issues ## `missing field 'source' in initializer` Do you have errors talking about the source field missing, `snafu::IntoError<_>` not being satisfied, and your project is multiple modules or files? [**answers**](troubleshooting::missing_field_source) snafu-0.7.1/src/guide/upgrading.md000064400000000000000000000212200072674642500151600ustar 00000000000000# Upgrading from previous releases - [Version 0.6 → 0.7](#version-06--07) - [Version 0.5 → 0.6](#version-05--06) - [Version 0.4 → 0.5](#version-04--05) - [Version 0.3 → 0.4](#version-03--04) - [Version 0.2 → 0.3](#version-02--03) - [Version 0.1 → 0.2](#version-01--02) ## Version 0.6 → 0.7 Upgrading should be a tedious but straightforward process. To assist upgrading your code, you can use the [snafu-upgrade-assistant], which attempts to automatically update breaking changes. [snafu-upgrade-assistant]: https://github.com/shepmaster/snafu-upgrade-assistant ### Context selector names have changed Previously, context selector names for enum errors exactly matched their corresponding enum variant names. This caused a large amount of confusion for people new to SNAFU. It was also inconsistent with context selector names for struct errors. Now, context selectors for both enum and struct errors use the `Snafu` suffix. Any existing `Error` suffix is removed before `Snafu` is added. #### Before ```rust,ignore #[derive(Debug, Snafu)] struct StructError; #[derive(Debug, Snafu)] enum EnumError { VariantError, } ensure!(false, StructContext); ensure!(false, VariantError); ``` #### After ```rust,ignore #[derive(Debug, Snafu)] struct StructError; #[derive(Debug, Snafu)] enum EnumError { VariantError, } ensure!(false, StructSnafu); ensure!(false, VariantSnafu); ``` ### `with_context` takes an argument `ResultExt::with_context`, `TryFutureExt::with_context`, and `TryStreamExt::with_context` now pass the error into the closure. #### Before ```rust,ignore some_result.with_context(|| ContextSelector); ``` #### After ```rust,ignore some_result.with_context(|_| ContextSelector); ``` ### String attribute parsing is no longer supported Previously, SNAFU allowed an alternate attribute specification format to support versions of Rust before 1.34. Since the minimum version has been increased, this format is no longer required. Use the parenthesized format instead: #### Before ```rust,ignore #[snafu(display = r#"("a format string with arguments: {}", info)"#)] ``` #### After ```rust,ignore #[snafu(display("a format string with arguments: {}", info))] ``` ### Minimum supported version of Rust is now 1.34 If you are writing a library, you will need to increase your minimum supported version of Rust to 1.34 or better. If you are writing an application, you should be able to upgrade your installed compiler by the same mechanism that you installed it. ## Version 0.5 → 0.6 ### Minimum supported version of Rust is now 1.31 If you are writing a library, you will need to increase your minimum supported version of Rust to 1.31 or better. If you are writing an application, you should be able to upgrade your installed compiler by the same mechanism that you installed it. ### Backtraces The `Backtrace` type is now always available, so it is encouraged to make liberal use of it in your errors. If you are writing an application that displays backtraces, make sure to enable the [`backtrace` feature flag](crate::guide::feature_flags) so that backtraces are populated when they are created. Implementations of `Backtrace::default` and `Backtrace::new` have been removed and replaced with `GenerateBacktrace::generate`. The `backtrace-crate` feature flag has been renamed to `backtraces-impl-backtrace-crate`. The backtrace returned by `ErrorCompat::backtrace` is now the `backtrace::Backtrace` type when this flag is enabled, so the implementation of `AsRef` has been removed. ### Futures Support for the standard library features has been stabilized, so the feature flag has been renamed from `unstable-futures` to `futures`. ## Version 0.4 → 0.5 ### `backtrace(delegate)` replaced with `backtrace` Previously, if you wanted to delegate backtrace creation to another error, you would specify `#[snafu(backtrace(delegate))]` on the source field that references the other error. Now, you specify the simpler `#[snafu(backtrace)]`. Since source fields must be error types, and backtrace fields must be `Backtrace` types, this is unambiguous and simplifies the API. #### Before ```rust,ignore #[derive(Debug, Snafu)] enum Error { MyVariant { #[snafu(backtrace(delegate))] source: OtherError, }, } ``` #### After ```rust,ignore #[derive(Debug, Snafu)] enum Error { MyVariant { #[snafu(backtrace)] source: OtherError, }, } ``` ### `source(from)` implies `source` Previously, if you had wanted to treat a field that wasn't named "source" as a source field, *and* you wanted to transform the field from another type, you had to specify both `#[snafu(source)]` and `#[snafu(source(from(...)))]`. Now, `#[snafu(source(from(...)))]` implies `#[snafu(source)]` -- it automatically treats the field as a source field regardless of its name, so you can remove the `#[snafu(source)]` attribute. #### Before ```rust,ignore #[derive(Debug, Snafu)] enum Error { CauseIsAnError { #[snafu(source)] #[snafu(source(from(Error, Box::new)))] cause: Box, }, } ``` #### After ```rust,ignore #[derive(Debug, Snafu)] enum Error { CauseIsAnError { #[snafu(source(from(Error, Box::new)))] cause: Box, }, } ``` ### New errors for attribute misuse and duplication Previously, SNAFU would ignore `#[snafu(...)]` attributes that were used in invalid locations. If attributes were duplicated, either the first or last would apply (depending on the attribute) and the rest would be ignored. One example is specifying `#[snafu(source(from(...)))]` on an enum variant instead of the source field in that variant: ```rust,ignore #[derive(Debug, Snafu)] enum Error { // This used to be ignored, and will now cause an error: #[snafu(source(from(Error, Box::new)))] MyVariant { source: Box, }, } ``` Now, compiler errors will be emitted that point to any misused or duplicated attributes. ## Version 0.3 → 0.4 ### `Context` vs. `IntoError` The `Context` type and related `From` implementations have been removed in favor of the [`IntoError`](crate::IntoError) trait. If you were making use of this for custom conversions, you will need to update your trait bounds: #### Before ```rust,ignore fn example(context: C) -> MyType where snafu::Context: Into; ``` #### After ```rust,ignore fn example(context: C) -> MyType where C: snafu::IntoError, E: std::error::Error + snafu::ErrorCompat; ``` ### `Borrow` SNAFU no longer generates `Borrow` implementations for SNAFU error types (sorry for the whiplash if you were affected by this when upgrading to 0.3). ## Version 0.2 → 0.3 Minimal changes should be required: if you previously implemented `Borrow` for a SNAFU error type, you should remove that implementation and allow SNAFU to implement it for you. ## Version 0.1 → 0.2 Support for the `snafu::display` attribute was removed as this type of attribute was [never intended to be supported][oops]. Since this required a SemVer-incompatible version, the attribute format has also been updated and normalized. 1. Attributes have been renamed - `snafu_display` and `snafu::display` became `snafu(display)`. - `snafu_visibility` became `snafu(visibility)` - `snafu_backtrace` became `snafu(backtrace)` 1. Support for `snafu_display` with individually-quoted format arguments was removed. Migrate to either the "clean" or "all one string" styles, depending on what version of Rust you are targeting. [oops]: https://github.com/rust-lang/rust/pull/58899 ### Before ```rust,ignore #[derive(Debug, Snafu)] enum DisplayUpdate { #[snafu::display("Format and {}", argument)] CleanStyle { argument: i32 }, #[snafu_display("Format and {}", "argument")] QuotedArgumentStyle { argument: i32 }, #[snafu_display = r#"("Format and {}", argument)"#] AllOneStringStyle { argument: i32 }, } ``` ```rust,ignore #[derive(Debug, Snafu)] enum VisibilityUpdate { #[snafu_visibility(pub(crate))] CleanStyle, #[snafu_visibility = "pub(crate)"] AllOneStringStyle, } ``` ### After ```rust,ignore # use snafu::Snafu; #[derive(Debug, Snafu)] enum DisplayUpdate { #[snafu(display("Format and {}", argument))] CleanStyle { argument: i32 }, #[snafu(display = r#"("Format and {}", argument)"#)] QuotedArgumentStyle { argument: i32 }, #[snafu(display = r#"("Format and {}", argument)"#)] AllOneStringStyle { argument: i32 }, } ``` ```rust,ignore # use snafu::Snafu; #[derive(Debug, Snafu)] enum VisibilityUpdate { #[snafu(visibility(pub(crate)))] CleanStyle, #[snafu(visibility = "pub(crate)")] AllOneStringStyle, } ``` snafu-0.7.1/src/guide.md000064400000000000000000000017430072674642500132100ustar 00000000000000# SNAFU user's guide Looking for quick help or answers? Please see the [troubleshooting](guide::troubleshooting) page. --- Once you've got a high-level idea of what SNAFU can do by looking at the [quick example](crate) and some [more examples](guide::examples), take a peek at [our design philosophy](guide::philosophy). For more advanced usage, take a deeper dive into [how the `Snafu` macro works](guide::the_macro), how to create [opaque error types](guide::opaque), how to create [error structs](guide::structs), how to use [generic types and lifetimes](guide::generics). For optional features of the crate, see our [list of feature flags](guide::feature_flags). If you are targeting an older release of Rust, you will be interested in [the compatibility section](guide::compatibility). For upgrading from a previous version, review the [upgrading guide](guide::upgrading). For comparisons and migration tips from another error library, see the [comparison list](guide::comparison). snafu-0.7.1/src/lib.rs000064400000000000000000001216030072674642500127030ustar 00000000000000#![deny(missing_docs)] #![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(feature = "unstable-backtraces-impl-std", feature(backtrace))] //! # SNAFU //! //! SNAFU is a library to easily generate errors and add information //! to underlying errors, especially when the same underlying error //! type can occur in different contexts. //! //! ## Features //! //! - [Turnkey errors based on strings](Whatever) //! - [Custom error types](Snafu) //! - Including a conversion path from turnkey errors //! - [Backtraces](Backtrace) //! - Extension traits for //! - [`Results`](ResultExt) //! - [`Options`](OptionExt) #![cfg_attr(feature = "futures", doc = " - [`Futures`](futures::TryFutureExt)")] #![cfg_attr(feature = "futures", doc = " - [`Streams`](futures::TryStreamExt)")] //! - Suitable for libraries and applications //! - `no_std` compatibility //! - Generic types and lifetimes //! //! For detailed information, please see the [user's guide](guide). //! //! ## Quick start //! //! If you want to report errors without hassle, start with the //! [`Whatever`][] type and the [`whatever!`][] macro: //! //! ```rust //! use snafu::{prelude::*, Whatever}; //! //! fn is_valid_id(id: u16) -> Result<(), Whatever> { //! if id < 10 { //! whatever!("ID may not be less than 10, but it was {}", id); //! } //! Ok(()) //! } //! ``` //! //! You can also use it to wrap any other error: //! //! ```rust //! use snafu::{prelude::*, Whatever}; //! //! fn read_config_file(path: &str) -> Result { //! std::fs::read_to_string(path) //! .with_whatever_context(|_| format!("Could not read file {}", path)) //! } //! ``` //! //! [`Whatever`][] allows for a short message and tracks a //! [`Backtrace`][] for every error: //! //! ```rust //! use snafu::{prelude::*, ErrorCompat, Whatever}; //! //! fn main() { //! # fn returns_an_error() -> Result<(), Whatever> { Ok(()) } //! if let Err(e) = returns_an_error() { //! eprintln!("An error occurred: {}", e); //! if let Some(bt) = ErrorCompat::backtrace(&e) { //! # #[cfg(not(feature = "backtraces-impl-backtrace-crate"))] //! eprintln!("{}", bt); //! } //! } //! } //! ``` //! //! ## Custom error types //! //! Many projects will hit limitations of the `Whatever` type. When //! that occurs, it's time to create your own error type by deriving //! [`Snafu`][]! //! //! ### Struct style //! //! SNAFU will read your error struct definition and create a *context //! selector* type (called `InvalidIdSnafu` in this example). These //! context selectors are used with the [`ensure!`][] macro to provide //! ergonomic error creation: //! //! ```rust //! use snafu::prelude::*; //! //! #[derive(Debug, Snafu)] //! #[snafu(display("ID may not be less than 10, but it was {id}"))] //! struct InvalidIdError { //! id: u16, //! } //! //! fn is_valid_id(id: u16) -> Result<(), InvalidIdError> { //! ensure!(id >= 10, InvalidIdSnafu { id }); //! Ok(()) //! } //! ``` //! //! If you add a `source` field to your error, you can then wrap an //! underlying error using the [`context`](ResultExt::context) //! extension method: //! //! ```rust //! use snafu::prelude::*; //! //! #[derive(Debug, Snafu)] //! #[snafu(display("Could not read file {path}"))] //! struct ConfigFileError { //! source: std::io::Error, //! path: String, //! } //! //! fn read_config_file(path: &str) -> Result { //! std::fs::read_to_string(path).context(ConfigFileSnafu { path }) //! } //! ``` //! //! ### Enum style //! //! While error structs are good for constrained cases, they don't //! allow for reporting multiple possible kinds of errors at one //! time. Error enums solve that problem. //! //! SNAFU will read your error enum definition and create a *context //! selector* type for each variant (called `InvalidIdSnafu` in this //! example). These context selectors are used with the [`ensure!`][] //! macro to provide ergonomic error creation: //! //! ```rust //! use snafu::prelude::*; //! //! #[derive(Debug, Snafu)] //! enum Error { //! #[snafu(display("ID may not be less than 10, but it was {id}"))] //! InvalidId { id: u16 }, //! } //! //! fn is_valid_id(id: u16) -> Result<(), Error> { //! ensure!(id >= 10, InvalidIdSnafu { id }); //! Ok(()) //! } //! ``` //! //! If you add a `source` field to a variant, you can then wrap an //! underlying error using the [`context`](ResultExt::context) //! extension method: //! //! ```rust //! use snafu::prelude::*; //! //! #[derive(Debug, Snafu)] //! enum Error { //! #[snafu(display("Could not read file {path}"))] //! ConfigFile { //! source: std::io::Error, //! path: String, //! }, //! } //! //! fn read_config_file(path: &str) -> Result { //! std::fs::read_to_string(path).context(ConfigFileSnafu { path }) //! } //! ``` //! //! You can combine the power of the [`whatever!`][] macro with an //! enum error type. This is great if you started out with //! [`Whatever`][] and are moving to a custom error type: //! //! ```rust //! use snafu::prelude::*; //! //! #[derive(Debug, Snafu)] //! enum Error { //! #[snafu(display("ID may not be less than 10, but it was {id}"))] //! InvalidId { id: u16 }, //! //! #[snafu(whatever, display("{message}"))] //! Whatever { //! message: String, //! #[snafu(source(from(Box, Some)))] //! source: Option>, //! }, //! } //! //! fn is_valid_id(id: u16) -> Result<(), Error> { //! ensure!(id >= 10, InvalidIdSnafu { id }); //! whatever!("Just kidding... this function always fails!"); //! Ok(()) //! } //! ``` //! //! You may wish to make the type `Send` and/or `Sync`, allowing //! your error type to be used in multithreaded programs, by changing //! `dyn std::error::Error` to `dyn std::error::Error + Send + Sync`. use core::fmt; pub mod prelude { //! Traits and macros used by most projects. Add `use //! snafu::prelude::*` to your code to quickly get started with //! SNAFU. pub use crate::{ensure, OptionExt as _, ResultExt as _}; // https://github.com/rust-lang/rust/issues/89020 doc_comment::doc_comment! { include_str!("Snafu.md"), pub use snafu_derive::Snafu; } #[cfg(any(feature = "std", test))] pub use crate::{ensure_whatever, whatever}; #[cfg(feature = "futures")] pub use crate::futures::{TryFutureExt as _, TryStreamExt as _}; } #[cfg(all( not(feature = "backtraces"), not(feature = "backtraces-impl-backtrace-crate"), not(feature = "unstable-backtraces-impl-std"), ))] mod backtrace_inert; #[cfg(all( not(feature = "backtraces"), not(feature = "backtraces-impl-backtrace-crate"), not(feature = "unstable-backtraces-impl-std"), ))] pub use crate::backtrace_inert::*; #[cfg(all( feature = "backtraces", not(feature = "backtraces-impl-backtrace-crate"), not(feature = "unstable-backtraces-impl-std"), ))] mod backtrace_shim; #[cfg(all( feature = "backtraces", not(feature = "backtraces-impl-backtrace-crate"), not(feature = "unstable-backtraces-impl-std"), ))] pub use crate::backtrace_shim::*; #[cfg(feature = "backtraces-impl-backtrace-crate")] pub use backtrace::Backtrace; #[cfg(feature = "unstable-backtraces-impl-std")] pub use std::backtrace::Backtrace; #[cfg(feature = "futures")] pub mod futures; #[cfg(feature = "std")] mod error_chain; #[cfg(feature = "std")] pub use crate::error_chain::*; doc_comment::doc_comment! { include_str!("Snafu.md"), pub use snafu_derive::Snafu; } macro_rules! generate_guide { (pub mod $name:ident; $($rest:tt)*) => { generate_guide!(@gen ".", pub mod $name { } $($rest)*); }; (pub mod $name:ident { $($children:tt)* } $($rest:tt)*) => { generate_guide!(@gen ".", pub mod $name { $($children)* } $($rest)*); }; (@gen $prefix:expr, ) => {}; (@gen $prefix:expr, pub mod $name:ident; $($rest:tt)*) => { generate_guide!(@gen $prefix, pub mod $name { } $($rest)*); }; (@gen $prefix:expr, @code pub mod $name:ident; $($rest:tt)*) => { #[cfg(feature = "guide")] pub mod $name; #[cfg(not(feature = "guide"))] /// Not currently built; please add the `guide` feature flag. pub mod $name {} generate_guide!(@gen $prefix, $($rest)*); }; (@gen $prefix:expr, pub mod $name:ident { $($children:tt)* } $($rest:tt)*) => { #[cfg(feature = "guide")] doc_comment::doc_comment! { include_str!(concat!($prefix, "/", stringify!($name), ".md")), pub mod $name { generate_guide!(@gen concat!($prefix, "/", stringify!($name)), $($children)*); } } #[cfg(not(feature = "guide"))] /// Not currently built; please add the `guide` feature flag. pub mod $name { generate_guide!(@gen concat!($prefix, "/", stringify!($name)), $($children)*); } generate_guide!(@gen $prefix, $($rest)*); }; } generate_guide! { pub mod guide { pub mod comparison { pub mod failure; } pub mod compatibility; pub mod feature_flags; pub mod generics; pub mod opaque; pub mod philosophy; pub mod structs; pub mod the_macro; pub mod troubleshooting { pub mod missing_field_source; } pub mod upgrading; @code pub mod examples; } } doc_comment::doctest!("../README.md", readme_tests); #[cfg(any(feature = "std", test))] #[doc(hidden)] pub use std::error::Error; #[cfg(not(any(feature = "std", test)))] mod no_std_error; #[cfg(not(any(feature = "std", test)))] #[doc(hidden)] pub use no_std_error::Error; /// Ensure a condition is true. If it is not, return from the function /// with an error. /// /// ## Examples /// /// ```rust /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// InvalidUser { user_id: i32 }, /// } /// /// fn example(user_id: i32) -> Result<(), Error> { /// ensure!(user_id > 0, InvalidUserSnafu { user_id }); /// // After this point, we know that `user_id` is positive. /// let user_id = user_id as u32; /// Ok(()) /// } /// ``` #[macro_export] macro_rules! ensure { ($predicate:expr, $context_selector:expr $(,)?) => { if !$predicate { return $context_selector .fail() .map_err(::core::convert::Into::into); } }; } /// Instantiate and return a stringly-typed error message. /// /// This can be used with the provided [`Whatever`][] type or with a /// custom error type that uses `snafu(whatever)`. /// /// # Without an underlying error /// /// Provide a format string and any optional arguments. The macro will /// unconditionally exit the calling function with an error. /// /// ## Examples /// /// ```rust /// use snafu::{Whatever, prelude::*}; /// /// type Result = std::result::Result; /// /// enum Status { /// Sleeping, /// Chilling, /// Working, /// } /// /// # fn stand_up() {} /// # fn go_downstairs() {} /// fn do_laundry(status: Status, items: u8) -> Result<()> { /// match status { /// Status::Sleeping => whatever!("Cannot launder {items} clothes when I am asleep"), /// Status::Chilling => { /// stand_up(); /// go_downstairs(); /// } /// Status::Working => { /// go_downstairs(); /// } /// } /// Ok(()) /// } /// ``` /// /// # With an underlying error /// /// Provide a `Result` as the first argument, followed by a format /// string and any optional arguments. If the `Result` is an error, /// the formatted string will be appended to the error and the macro /// will exist the calling function with an error. If the `Result` is /// not an error, the macro will evaluate to the `Ok` value of the /// `Result`. /// /// ## Examples /// /// ```rust /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// #[snafu(whatever, display("Error was: {message}"))] /// struct Error { /// message: String, /// #[snafu(source(from(Box, Some)))] /// source: Option>, /// } /// type Result = std::result::Result; /// /// fn calculate_brightness_factor() -> Result { /// let angle = calculate_angle_of_refraction(); /// let angle = whatever!(angle, "There was no angle"); /// Ok(angle * 2) /// } /// /// fn calculate_angle_of_refraction() -> Result { /// whatever!("The programmer forgot to implement this..."); /// } /// ``` #[macro_export] #[cfg(any(feature = "std", test))] macro_rules! whatever { ($fmt:literal$(, $($arg:expr),* $(,)?)?) => { return core::result::Result::Err({ $crate::FromString::without_source( format!($fmt$(, $($arg),*)*), ) }); }; ($source:expr, $fmt:literal$(, $($arg:expr),* $(,)?)*) => { match $source { core::result::Result::Ok(v) => v, core::result::Result::Err(e) => { return core::result::Result::Err({ $crate::FromString::with_source( core::convert::Into::into(e), format!($fmt$(, $($arg),*)*), ) }); } } }; } /// Ensure a condition is true. If it is not, return a stringly-typed /// error message. /// /// This can be used with the provided [`Whatever`][] type or with a /// custom error type that uses `snafu(whatever)`. /// /// ## Examples /// /// ```rust /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// #[snafu(whatever, display("Error was: {message}"))] /// struct Error { /// message: String, /// } /// type Result = std::result::Result; /// /// fn get_bank_account_balance(account_id: &str) -> Result { /// # fn moon_is_rising() -> bool { false } /// ensure_whatever!( /// moon_is_rising(), /// "We are recalibrating the dynamos for account {}, sorry", /// account_id, /// ); /// /// Ok(100) /// } /// ``` #[macro_export] #[cfg(any(feature = "std", test))] macro_rules! ensure_whatever { ($predicate:expr, $fmt:literal$(, $($arg:expr),* $(,)?)?) => { if !$predicate { $crate::whatever!($fmt$(, $($arg),*)*); } }; } /// Additions to [`Result`](std::result::Result). pub trait ResultExt: Sized { /// Extend a [`Result`]'s error with additional context-sensitive information. /// /// [`Result`]: std::result::Result /// /// ```rust /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// Authenticating { /// user_name: String, /// user_id: i32, /// source: ApiError, /// }, /// } /// /// fn example() -> Result<(), Error> { /// another_function().context(AuthenticatingSnafu { /// user_name: "admin", /// user_id: 42, /// })?; /// Ok(()) /// } /// /// # type ApiError = Box; /// fn another_function() -> Result { /// /* ... */ /// # Ok(42) /// } /// ``` /// /// Note that the context selector will call /// [`Into::into`](std::convert::Into::into) on each field, so the types /// are not required to exactly match. fn context(self, context: C) -> Result where C: IntoError, E2: Error + ErrorCompat; /// Extend a [`Result`][]'s error with lazily-generated context-sensitive information. /// /// [`Result`]: std::result::Result /// /// ```rust /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// Authenticating { /// user_name: String, /// user_id: i32, /// source: ApiError, /// }, /// } /// /// fn example() -> Result<(), Error> { /// another_function().with_context(|_| AuthenticatingSnafu { /// user_name: "admin".to_string(), /// user_id: 42, /// })?; /// Ok(()) /// } /// /// # type ApiError = std::io::Error; /// fn another_function() -> Result { /// /* ... */ /// # Ok(42) /// } /// ``` /// /// Note that this *may not* be needed in many cases because the context /// selector will call [`Into::into`](std::convert::Into::into) on each /// field. fn with_context(self, context: F) -> Result where F: FnOnce(&mut E) -> C, C: IntoError, E2: Error + ErrorCompat; /// Extend a [`Result`]'s error with information from a string. /// /// The target error type must implement [`FromString`] by using /// the /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`] type is also available. /// /// In many cases, you will want to use /// [`with_whatever_context`][Self::with_whatever_context] instead /// as it gives you access to the error and is only called in case /// of error. This method is best suited for when you have a /// string literal. /// /// ```rust /// use snafu::{prelude::*, Whatever}; /// /// fn example() -> Result<(), Whatever> { /// std::fs::read_to_string("/this/does/not/exist") /// .whatever_context("couldn't open the file")?; /// Ok(()) /// } /// /// let err = example().unwrap_err(); /// assert_eq!("couldn't open the file", err.to_string()); /// ``` #[cfg(any(feature = "std", test))] fn whatever_context(self, context: S) -> Result where S: Into, E2: FromString, E: Into; /// Extend a [`Result`]'s error with information from a /// lazily-generated string. /// /// The target error type must implement [`FromString`] by using /// the /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`] type is also available. /// /// ```rust /// use snafu::{prelude::*, Whatever}; /// /// fn example() -> Result<(), Whatever> { /// let filename = "/this/does/not/exist"; /// std::fs::read_to_string(filename) /// .with_whatever_context(|_| format!("couldn't open the file {}", filename))?; /// Ok(()) /// } /// /// let err = example().unwrap_err(); /// assert_eq!( /// "couldn't open the file /this/does/not/exist", /// err.to_string(), /// ); /// ``` /// /// The closure is not called when the `Result` is `Ok`: /// /// ```rust /// use snafu::{prelude::*, Whatever}; /// /// let value: std::io::Result = Ok(42); /// let result = value.with_whatever_context::<_, String, Whatever>(|_| { /// panic!("This block will not be evaluated"); /// }); /// /// assert!(result.is_ok()); /// ``` #[cfg(any(feature = "std", test))] fn with_whatever_context(self, context: F) -> Result where F: FnOnce(&mut E) -> S, S: Into, E2: FromString, E: Into; } impl ResultExt for Result { #[cfg_attr(feature = "rust_1_46", track_caller)] fn context(self, context: C) -> Result where C: IntoError, E2: Error + ErrorCompat, { // https://github.com/rust-lang/rust/issues/74042 match self { Ok(v) => Ok(v), Err(error) => Err(context.into_error(error)), } } #[cfg_attr(feature = "rust_1_46", track_caller)] fn with_context(self, context: F) -> Result where F: FnOnce(&mut E) -> C, C: IntoError, E2: Error + ErrorCompat, { // https://github.com/rust-lang/rust/issues/74042 match self { Ok(v) => Ok(v), Err(mut error) => { let context = context(&mut error); Err(context.into_error(error)) } } } #[cfg(any(feature = "std", test))] #[cfg_attr(feature = "rust_1_46", track_caller)] fn whatever_context(self, context: S) -> Result where S: Into, E2: FromString, E: Into, { // https://github.com/rust-lang/rust/issues/74042 match self { Ok(v) => Ok(v), Err(error) => Err(FromString::with_source(error.into(), context.into())), } } #[cfg(any(feature = "std", test))] #[cfg_attr(feature = "rust_1_46", track_caller)] fn with_whatever_context(self, context: F) -> Result where F: FnOnce(&mut E) -> S, S: Into, E2: FromString, E: Into, { // https://github.com/rust-lang/rust/issues/74042 match self { Ok(t) => Ok(t), Err(mut e) => { let context = context(&mut e); Err(FromString::with_source(e.into(), context.into())) } } } } /// A temporary error type used when converting an [`Option`][] into a /// [`Result`][] /// /// [`Option`]: std::option::Option /// [`Result`]: std::result::Result pub struct NoneError; /// Additions to [`Option`](std::option::Option). pub trait OptionExt: Sized { /// Convert an [`Option`][] into a [`Result`][] with additional /// context-sensitive information. /// /// [Option]: std::option::Option /// [Result]: std::option::Result /// /// ```rust /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// UserLookup { user_id: i32 }, /// } /// /// fn example(user_id: i32) -> Result<(), Error> { /// let name = username(user_id).context(UserLookupSnafu { user_id })?; /// println!("Username was {}", name); /// Ok(()) /// } /// /// fn username(user_id: i32) -> Option { /// /* ... */ /// # None /// } /// ``` /// /// Note that the context selector will call /// [`Into::into`](std::convert::Into::into) on each field, so the types /// are not required to exactly match. fn context(self, context: C) -> Result where C: IntoError, E: Error + ErrorCompat; /// Convert an [`Option`][] into a [`Result`][] with /// lazily-generated context-sensitive information. /// /// [`Option`]: std::option::Option /// [`Result`]: std::result::Result /// /// ``` /// use snafu::prelude::*; /// /// #[derive(Debug, Snafu)] /// enum Error { /// UserLookup { /// user_id: i32, /// previous_ids: Vec, /// }, /// } /// /// fn example(user_id: i32) -> Result<(), Error> { /// let name = username(user_id).with_context(|| UserLookupSnafu { /// user_id, /// previous_ids: Vec::new(), /// })?; /// println!("Username was {}", name); /// Ok(()) /// } /// /// fn username(user_id: i32) -> Option { /// /* ... */ /// # None /// } /// ``` /// /// Note that this *may not* be needed in many cases because the context /// selector will call [`Into::into`](std::convert::Into::into) on each /// field. fn with_context(self, context: F) -> Result where F: FnOnce() -> C, C: IntoError, E: Error + ErrorCompat; /// Convert an [`Option`] into a [`Result`] with information /// from a string. /// /// The target error type must implement [`FromString`] by using /// the /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`] type is also available. /// /// In many cases, you will want to use /// [`with_whatever_context`][Self::with_whatever_context] instead /// as it is only called in case of error. This method is best /// suited for when you have a string literal. /// /// ```rust /// use snafu::{prelude::*, Whatever}; /// /// fn example(env_var_name: &str) -> Result<(), Whatever> { /// std::env::var_os(env_var_name).whatever_context("couldn't get the environment variable")?; /// Ok(()) /// } /// /// let err = example("UNDEFINED_ENVIRONMENT_VARIABLE").unwrap_err(); /// assert_eq!("couldn't get the environment variable", err.to_string()); /// ``` #[cfg(any(feature = "std", test))] fn whatever_context(self, context: S) -> Result where S: Into, E: FromString; /// Convert an [`Option`] into a [`Result`][] with information from a /// lazily-generated string. /// /// The target error type must implement [`FromString`][] by using /// the /// [`#[snafu(whatever)]`][Snafu#controlling-stringly-typed-errors] /// attribute. The premade [`Whatever`][] type is also available. /// /// ```rust /// use snafu::{prelude::*, Whatever}; /// /// fn example(env_var_name: &str) -> Result<(), Whatever> { /// std::env::var_os(env_var_name).with_whatever_context(|| { /// format!("couldn't get the environment variable {}", env_var_name) /// })?; /// Ok(()) /// } /// /// let err = example("UNDEFINED_ENVIRONMENT_VARIABLE").unwrap_err(); /// assert_eq!( /// "couldn't get the environment variable UNDEFINED_ENVIRONMENT_VARIABLE", /// err.to_string() /// ); /// ``` /// /// The closure is not called when the `Option` is `Some`: /// /// ```rust /// use snafu::{prelude::*, Whatever}; /// /// let value = Some(42); /// let result = value.with_whatever_context::<_, String, Whatever>(|| { /// panic!("This block will not be evaluated"); /// }); /// /// assert!(result.is_ok()); /// ``` #[cfg(any(feature = "std", test))] fn with_whatever_context(self, context: F) -> Result where F: FnOnce() -> S, S: Into, E: FromString; } impl OptionExt for Option { #[cfg_attr(feature = "rust_1_46", track_caller)] fn context(self, context: C) -> Result where C: IntoError, E: Error + ErrorCompat, { // https://github.com/rust-lang/rust/issues/74042 match self { Some(v) => Ok(v), None => Err(context.into_error(NoneError)), } } #[cfg_attr(feature = "rust_1_46", track_caller)] fn with_context(self, context: F) -> Result where F: FnOnce() -> C, C: IntoError, E: Error + ErrorCompat, { // https://github.com/rust-lang/rust/issues/74042 match self { Some(v) => Ok(v), None => Err(context().into_error(NoneError)), } } #[cfg(any(feature = "std", test))] #[cfg_attr(feature = "rust_1_46", track_caller)] fn whatever_context(self, context: S) -> Result where S: Into, E: FromString, { match self { Some(v) => Ok(v), None => Err(FromString::without_source(context.into())), } } #[cfg(any(feature = "std", test))] #[cfg_attr(feature = "rust_1_46", track_caller)] fn with_whatever_context(self, context: F) -> Result where F: FnOnce() -> S, S: Into, E: FromString, { match self { Some(v) => Ok(v), None => { let context = context(); Err(FromString::without_source(context.into())) } } } } /// Backports changes to the [`Error`](std::error::Error) trait to /// versions of Rust lacking them. /// /// It is recommended to always call these methods explicitly so that /// it is easy to replace usages of this trait when you start /// supporting a newer version of Rust. /// /// ``` /// # use snafu::{prelude::*, ErrorCompat}; /// # #[derive(Debug, Snafu)] enum Example {}; /// # fn example(error: Example) { /// ErrorCompat::backtrace(&error); // Recommended /// error.backtrace(); // Discouraged /// # } /// ``` pub trait ErrorCompat { /// Returns a [`Backtrace`](Backtrace) that may be printed. fn backtrace(&self) -> Option<&Backtrace> { None } /// Returns an iterator for traversing the chain of errors, /// starting with the current error /// and continuing with recursive calls to `Error::source`. /// /// To omit the current error and only traverse its sources, /// use `skip(1)`. #[cfg(feature = "std")] fn iter_chain(&self) -> ChainCompat where Self: AsErrorSource, { ChainCompat::new(self.as_error_source()) } } impl<'a, E> ErrorCompat for &'a E where E: ErrorCompat, { fn backtrace(&self) -> Option<&Backtrace> { (**self).backtrace() } } #[cfg(any(feature = "std", test))] impl ErrorCompat for Box where E: ErrorCompat, { fn backtrace(&self) -> Option<&Backtrace> { (**self).backtrace() } } /// Converts the receiver into an [`Error`][] trait object, suitable /// for use in [`Error::source`][]. /// /// It is expected that most users of SNAFU will not directly interact /// with this trait. /// /// [`Error`]: std::error::Error /// [`Error::source`]: std::error::Error::source // // Given an error enum with multiple types of underlying causes: // // ```rust // enum Error { // BoxTraitObjectSendSync(Box), // BoxTraitObject(Box), // Boxed(Box), // Unboxed(io::Error), // } // ``` // // This trait provides the answer to what consistent expression can go // in each match arm: // // ```rust // impl error::Error for Error { // fn source(&self) -> Option<&(dyn error::Error + 'static)> { // use Error::*; // // let v = match *self { // BoxTraitObjectSendSync(ref e) => ..., // BoxTraitObject(ref e) => ..., // Boxed(ref e) => ..., // Unboxed(ref e) => ..., // }; // // Some(v) // } // } // // Existing methods like returning `e`, `&**e`, `Borrow::borrow(e)`, // `Deref::deref(e)`, and `AsRef::as_ref(e)` do not work for various // reasons. pub trait AsErrorSource { /// For maximum effectiveness, this needs to be called as a method /// to benefit from Rust's automatic dereferencing of method /// receivers. fn as_error_source(&self) -> &(dyn Error + 'static); } impl AsErrorSource for dyn Error + 'static { fn as_error_source(&self) -> &(dyn Error + 'static) { self } } impl AsErrorSource for dyn Error + Send + 'static { fn as_error_source(&self) -> &(dyn Error + 'static) { self } } impl AsErrorSource for dyn Error + Sync + 'static { fn as_error_source(&self) -> &(dyn Error + 'static) { self } } impl AsErrorSource for dyn Error + Send + Sync + 'static { fn as_error_source(&self) -> &(dyn Error + 'static) { self } } impl AsErrorSource for T where T: Error + 'static, { fn as_error_source(&self) -> &(dyn Error + 'static) { self } } /// Combines an underlying error with additional information /// about the error. /// /// It is expected that most users of SNAFU will not directly interact /// with this trait. pub trait IntoError where E: Error + ErrorCompat, { /// The underlying error type Source; /// Combine the information to produce the error fn into_error(self, source: Self::Source) -> E; } /// Takes a string message and builds the corresponding error. /// /// It is expected that most users of SNAFU will not directly interact /// with this trait. #[cfg(any(feature = "std", test))] pub trait FromString { /// The underlying error type Source; /// Create a brand new error from the given string fn without_source(message: String) -> Self; /// Wrap an existing error with the given string fn with_source(source: Self::Source, message: String) -> Self; } /// Construct data to be included as part of an error. The data must /// require no arguments to be created. pub trait GenerateImplicitData { /// Build the data. fn generate() -> Self; } /// View a backtrace-like value as an optional backtrace. pub trait AsBacktrace { /// Retrieve the optional backtrace fn as_backtrace(&self) -> Option<&Backtrace>; } /// Only create a backtrace when an environment variable is set. /// /// This looks first for the value of `RUST_LIB_BACKTRACE` then /// `RUST_BACKTRACE`. If the value is set to `1`, backtraces will be /// enabled. /// /// This value will be tested only once per program execution; /// changing the environment variable after it has been checked will /// have no effect. #[cfg(any(feature = "std", test))] impl GenerateImplicitData for Option { fn generate() -> Self { use std::env; use std::sync::{ atomic::{AtomicBool, Ordering}, Once, }; static START: Once = Once::new(); static ENABLED: AtomicBool = AtomicBool::new(false); START.call_once(|| { // TODO: What values count as "true"? let enabled = env::var_os("RUST_LIB_BACKTRACE") .or_else(|| env::var_os("RUST_BACKTRACE")) .map_or(false, |v| v == "1"); ENABLED.store(enabled, Ordering::SeqCst); }); if ENABLED.load(Ordering::SeqCst) { Some(Backtrace::generate()) } else { None } } } #[cfg(any(feature = "std", test))] impl AsBacktrace for Option { fn as_backtrace(&self) -> Option<&Backtrace> { self.as_ref() } } #[cfg(feature = "backtraces-impl-backtrace-crate")] impl GenerateImplicitData for Backtrace { fn generate() -> Self { Backtrace::new() } } #[cfg(feature = "backtraces-impl-backtrace-crate")] impl AsBacktrace for Backtrace { fn as_backtrace(&self) -> Option<&Backtrace> { Some(self) } } #[cfg(feature = "unstable-backtraces-impl-std")] impl GenerateImplicitData for Backtrace { fn generate() -> Self { Backtrace::force_capture() } } #[cfg(feature = "unstable-backtraces-impl-std")] impl AsBacktrace for Backtrace { fn as_backtrace(&self) -> Option<&Backtrace> { Some(self) } } /// The source code location where the error was reported. /// /// To use it, add a field `location: Location` to your error. This /// will automatically register it as [implicitly generated /// data][implicit]. /// /// [implicit]: Snafu#controlling-implicitly-generated-data /// /// ## Limitations /// /// ### Rust 1.46 /// /// You need to enable the [`rust_1_46` feature flag][flag] for /// implicit location capture. If you cannot enable that, you can /// still use the [`location!`] macro at the expense of more typing. /// /// [flag]: guide::compatibility#rust_1_46 /// /// ### Disabled context selectors /// /// If you have [disabled the context selector][disabled], SNAFU will /// not be able to capture an accurate location. /// /// As a workaround, re-enable the context selector. /// /// [disabled]: Snafu#disabling-the-context-selector /// /// ### Asynchronous code /// /// When using SNAFU's #[cfg_attr(feature = "futures", doc = " [`TryFutureExt`][futures::TryFutureExt]")] #[cfg_attr(not(feature = "futures"), doc = " `TryFutureExt`")] /// or #[cfg_attr(feature = "futures", doc = " [`TryStreamExt`][futures::TryStreamExt]")] #[cfg_attr(not(feature = "futures"), doc = " `TryStreamExt`")] /// extension traits, the automatically captured location will /// correspond to where the future or stream was **polled**, not where /// it was created. Additionally, many `Future` or `Stream` /// combinators do not forward the caller's location to their /// closures, causing the recorded location to be inside of the future /// combinator's library. /// /// There are two workarounds: /// 1. Use the [`location!`] macro /// 1. Use [`ResultExt`] instead /// /// ```rust /// # #[cfg(feature = "futures")] { /// # use snafu::{prelude::*, Location, location}; /// // Non-ideal: will report where `wrapped_error_future` is `.await`ed. /// # let error_future = async { AnotherSnafu.fail::<()>() }; /// let wrapped_error_future = error_future.context(ImplicitLocationSnafu); /// /// // Better: will report the location of `.context`. /// # let error_future = async { AnotherSnafu.fail::<()>() }; /// let wrapped_error_future = async { error_future.await.context(ImplicitLocationSnafu) }; /// /// // Better: Will report the location of `location!` /// # let error_future = async { AnotherSnafu.fail::<()>() }; /// let wrapped_error_future = error_future.with_context(|_| ExplicitLocationSnafu { /// location: location!(), /// }); /// /// # #[derive(Debug, Snafu)] struct AnotherError; /// #[derive(Debug, Snafu)] /// struct ImplicitLocationError { /// source: AnotherError, /// location: Location, /// } /// /// #[derive(Debug, Snafu)] /// struct ExplicitLocationError { /// source: AnotherError, /// #[snafu(implicit(false))] /// location: Location, /// } /// # } /// ``` #[derive(Debug, Copy, Clone)] pub struct Location { /// The file where the error was reported pub file: &'static str, /// The line where the error was reported pub line: u32, /// The column where the error was reported pub column: u32, // Use `#[non_exhaustive]` when we upgrade to Rust 1.40 _other: (), } impl Location { /// Constructs a `Location` using the given information pub fn new(file: &'static str, line: u32, column: u32) -> Self { Self { file, line, column, _other: (), } } } #[cfg(feature = "rust_1_46")] impl Default for Location { #[track_caller] fn default() -> Self { let loc = core::panic::Location::caller(); Self { file: loc.file(), line: loc.line(), column: loc.column(), _other: (), } } } #[cfg(feature = "rust_1_46")] impl GenerateImplicitData for Location { #[inline] #[track_caller] fn generate() -> Self { Self::default() } } impl fmt::Display for Location { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{file}:{line}:{column}", file = self.file, line = self.line, column = self.column, ) } } /// Constructs a [`Location`] using the current file, line, and column. #[macro_export] macro_rules! location { () => { Location::new(file!(), line!(), column!()) }; } /// A basic error type that you can use as a first step to better /// error handling. /// /// You can use this type in your own application as a quick way to /// create errors or add basic context to another error. This can also /// be used in a library, but consider wrapping it in an /// [opaque](guide::opaque) error to avoid putting the SNAFU crate in /// your public API. /// /// ## Examples /// /// ```rust /// use snafu::prelude::*; /// /// type Result = std::result::Result; /// /// fn subtract_numbers(a: u32, b: u32) -> Result { /// if a > b { /// Ok(a - b) /// } else { /// whatever!("Can't subtract {} - {}", a, b) /// } /// } /// /// fn complicated_math(a: u32, b: u32) -> Result { /// let val = subtract_numbers(a, b).whatever_context("Can't do the math")?; /// Ok(val * 2) /// } /// ``` /// /// See [`whatever!`][] for detailed usage instructions. /// /// ## Limitations /// /// When wrapping errors, only the backtrace from the shallowest /// function is guaranteed to be available. If you need the deepest /// possible trace, consider creating a custom error type and [using /// `#[snafu(backtrace)]` on the `source` /// field](Snafu#controlling-backtraces). If a best-effort attempt is /// sufficient, see the [`backtrace`][Self::backtrace] method. /// /// When the standard library stabilizes backtrace support, this /// behavior may change. #[derive(Debug, Snafu)] #[snafu(crate_root(crate))] #[snafu(whatever)] #[snafu(display("{message}"))] #[cfg(any(feature = "std", test))] pub struct Whatever { #[snafu(source(from(Box, Some)))] source: Option>, message: String, backtrace: Backtrace, } #[cfg(any(feature = "std", test))] impl Whatever { /// Gets the backtrace from the deepest `Whatever` error. If none /// of the underlying errors are `Whatever`, returns the backtrace /// from when this instance was created. pub fn backtrace(&self) -> Option<&Backtrace> { let mut best_backtrace = &self.backtrace; let mut source = self.source(); while let Some(s) = source { if let Some(this) = s.downcast_ref::() { best_backtrace = &this.backtrace; } source = s.source(); } Some(best_backtrace) } } snafu-0.7.1/src/no_std_error.rs000064400000000000000000000020740072674642500146340ustar 00000000000000#![allow(missing_docs)] use core::fmt::{Debug, Display}; pub trait Error: Debug + Display { fn description(&self) -> &str { "description() is deprecated; use Display" } fn cause(&self) -> Option<&dyn Error> { self.source() } fn source(&self) -> Option<&(dyn Error + 'static)> { None } } macro_rules! impl_error { ($($e:path),*) => { $( impl Error for $e {} )* } } // All errors supported by our minimum suported Rust version can be supported by // default. impl_error![ core::num::ParseFloatError, // 1.0 core::num::ParseIntError, // 1.0 core::str::ParseBoolError, // 1.0 core::str::Utf8Error, // 1.0 core::char::DecodeUtf16Error, // 1.9 core::fmt::Error, // 1.11 core::cell::BorrowError, // 1.13 core::cell::BorrowMutError, // 1.13 core::char::ParseCharError, // 1.20 core::array::TryFromSliceError, // 1.34 core::char::CharTryFromError, // 1.34 core::num::TryFromIntError // 1.34 ]; snafu-0.7.1/tests/backtrace-optional-enabled.rs000064400000000000000000000005760072674642500176470ustar 00000000000000use snafu::{prelude::*, Backtrace, ErrorCompat}; #[derive(Debug, Snafu)] enum Error { BacktraceSometimes { backtrace: Option }, } #[test] fn optional_backtrace_is_present_with_environment_variable() { std::env::set_var("RUST_LIB_BACKTRACE", "1"); let sometimes = BacktraceSometimesSnafu.build(); assert!(ErrorCompat::backtrace(&sometimes).is_some()); } snafu-0.7.1/tests/backtrace-optional.rs000064400000000000000000000021640072674642500162520ustar 00000000000000use snafu::{prelude::*, Backtrace, ErrorCompat}; #[derive(Debug, Snafu)] enum Error { BacktraceAlways { backtrace: Backtrace }, BacktraceSometimes { backtrace: Option }, } #[test] fn bare_backtrace_is_always_present() { let always = BacktraceAlwaysSnafu.build(); assert!(ErrorCompat::backtrace(&always).is_some()); } #[test] fn optional_backtrace_is_not_present_without_environment_variable() { use std::env; // The check requires RUST_BACKTRACE to be unset. Back up the // current value of the environment variable and restore it // afterwards. If we add more tests to this file that rely on // environment variables, we should introduce a mutex as // environment variables are a global resource. const BACKTRACE_ENV_NAME: &str = "RUST_BACKTRACE"; let previous_backtrace_env_value = env::var_os(BACKTRACE_ENV_NAME); env::remove_var(BACKTRACE_ENV_NAME); let sometimes = BacktraceSometimesSnafu.build(); assert!(ErrorCompat::backtrace(&sometimes).is_none()); if let Some(v) = previous_backtrace_env_value { env::set_var(BACKTRACE_ENV_NAME, v); } } snafu-0.7.1/tests/backtrace.rs000064400000000000000000000027160072674642500144320ustar 00000000000000use snafu::{prelude::*, Backtrace, ErrorCompat}; type AnotherError = Box; #[derive(Debug, Snafu)] enum Error { #[snafu(display("Invalid user {}:\n{}", user_id, backtrace))] InvalidUser { user_id: i32, backtrace: Backtrace }, WithSource { source: AnotherError, backtrace: Backtrace, }, WithSourceAndOtherInfo { user_id: i32, source: AnotherError, backtrace: Backtrace, }, } type Result = std::result::Result; fn example(user_id: i32) -> Result<()> { ensure!(user_id >= 42, InvalidUserSnafu { user_id }); Ok(()) } #[test] fn display_can_access_backtrace() { let e = example(0).unwrap_err(); let text = e.to_string(); assert!( text.contains("disabled backtrace"), "{:?} does not contain expected text", text ); } fn trigger() -> Result<(), AnotherError> { Err("boom".into()) } #[test] fn errors_with_sources_can_have_backtraces() { let e = trigger().context(WithSourceSnafu).unwrap_err(); let backtrace = ErrorCompat::backtrace(&e).unwrap(); assert!(backtrace.to_string().contains("disabled backtrace")); } #[test] fn errors_with_sources_and_other_info_can_have_backtraces() { let e = trigger() .context(WithSourceAndOtherInfoSnafu { user_id: 42 }) .unwrap_err(); let backtrace = ErrorCompat::backtrace(&e).unwrap(); assert!(backtrace.to_string().contains("disabled backtrace")); } snafu-0.7.1/tests/backtrace_attributes.rs000064400000000000000000000011450072674642500166730ustar 00000000000000use snafu::{prelude::*, Backtrace}; #[derive(Debug, Snafu)] enum Error { NoArgument { #[snafu(backtrace)] thing: Backtrace, }, ExplicitTrue { #[snafu(backtrace(true))] thing: Backtrace, }, ExplicitFalse { #[snafu(backtrace(false))] backtrace: i32, }, } fn example() -> Result<(), Error> { NoArgumentSnafu.fail()?; ExplicitTrueSnafu.fail()?; ExplicitFalseSnafu { backtrace: 42 }.fail()?; Ok(()) } #[test] fn implements_error() { fn check() {} check::(); example().unwrap_err(); } snafu-0.7.1/tests/basic.rs000064400000000000000000000024360072674642500135730ustar 00000000000000use snafu::prelude::*; use std::{ fs, io, path::{Path, PathBuf}, }; #[derive(Debug, Snafu)] enum Error { #[snafu(display("Could not open config file at {}: {}", filename.display(), source))] OpenConfig { filename: PathBuf, source: io::Error, }, #[snafu(display("Could not open config file at {}", source))] SaveConfig { source: io::Error }, #[snafu(display("User ID {} is invalid", user_id))] InvalidUser { user_id: i32 }, #[snafu(display("No user available"))] MissingUser, } type Result = std::result::Result; const CONFIG_FILENAME: &str = "/tmp/config"; fn example(root: impl AsRef, user_id: Option) -> Result<()> { let root = root.as_ref(); let filename = &root.join(CONFIG_FILENAME); let config = fs::read(filename).context(OpenConfigSnafu { filename })?; let _user_id = match user_id { None => MissingUserSnafu.fail()?, Some(user_id) if user_id != 42 => InvalidUserSnafu { user_id }.fail()?, Some(user_id) => user_id, }; fs::write(filename, config).context(SaveConfigSnafu)?; Ok(()) } #[test] fn implements_error() { fn check() {} check::(); example("/some/directory/that/does/not/exist", None).unwrap_err(); } snafu-0.7.1/tests/boxed_error_trait_object.rs000064400000000000000000000031750072674642500175560ustar 00000000000000// This test asserts that a boxed error trait object can be used as a source. use snafu::prelude::*; mod trait_object { pub type Error = Box; pub fn function() -> Result { Ok(42) } } mod trait_object_send { pub type Error = Box; pub fn function() -> Result { Ok(42) } } mod trait_object_sync { pub type Error = Box; pub fn function() -> Result { Ok(42) } } mod trait_object_send_sync { pub type Error = Box; pub fn function() -> Result { Ok(42) } } #[derive(Debug, Snafu)] enum Error { TraitObject { user_id: i32, source: trait_object::Error, }, TraitObjectSend { user_id: i32, source: trait_object_send::Error, }, TraitObjectSync { user_id: i32, source: trait_object_sync::Error, }, TraitObjectSendSync { user_id: i32, source: trait_object_send_sync::Error, }, } fn example() -> Result<(), Error> { trait_object::function().context(TraitObjectSnafu { user_id: 42 })?; trait_object_send::function().context(TraitObjectSendSnafu { user_id: 42 })?; trait_object_sync::function().context(TraitObjectSyncSnafu { user_id: 42 })?; trait_object_send_sync::function().context(TraitObjectSendSyncSnafu { user_id: 42 })?; Ok(()) } #[test] fn implements_error() { fn check() {} check::(); example().unwrap(); } snafu-0.7.1/tests/build-leaf-error.rs000064400000000000000000000006160072674642500156430ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { Mine, } type Result = std::result::Result; fn other_result() -> Result { Err(()) } fn map_result() -> Result { other_result().map_err(|_| MineSnafu.build()) } #[test] fn implements_error() { fn check() {} check::(); map_result().unwrap_err(); } snafu-0.7.1/tests/context_selector_name.rs000064400000000000000000000023740072674642500170770ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { #[snafu(context(suffix(Moo)))] Alpha, TrimsWhenEndingInError, #[snafu(context(suffix(false)))] CanOptOutOfSuffix, } fn alpha_usage() -> Result<(), Error> { AlphaMoo.fail() } fn trimming_usage() -> Result<(), Error> { TrimsWhenEndingInSnafu.fail() } fn no_suffix_usage() -> Result<(), Error> { CanOptOutOfSuffix.fail() } #[test] fn implements_error() { fn check() {} check::(); alpha_usage().unwrap_err(); trimming_usage().unwrap_err(); no_suffix_usage().unwrap_err(); } mod applied_to_enum { use super::*; #[derive(Debug, Snafu)] #[snafu(context(suffix(false)))] enum Error { Alpha, Beta, #[snafu(context(suffix(Cow)))] Gamma, } fn alpha_usage() -> Result<(), Error> { Alpha.fail() } fn beta_usage() -> Result<(), Error> { Beta.fail() } fn gamma_usage() -> Result<(), Error> { GammaCow.fail() } #[test] fn implements_error() { fn check() {} check::(); alpha_usage().unwrap_err(); beta_usage().unwrap_err(); gamma_usage().unwrap_err(); } } snafu-0.7.1/tests/default_error_display.rs000064400000000000000000000006370072674642500170750ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum InnerError { #[snafu(display("inner error"))] AnExample, } #[derive(Debug, Snafu)] enum Error { NoDisplay { source: InnerError }, } #[test] fn default_error_display() { let err: Error = AnExampleSnafu .fail::<()>() .context(NoDisplaySnafu) .unwrap_err(); assert_eq!(format!("{}", err), "NoDisplay: inner error",); } snafu-0.7.1/tests/display-shorthand.rs000064400000000000000000000051540072674642500161470ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { #[snafu(display("ID {} denied", id))] OnlyPositionalArguments { id: i32 }, #[snafu(display("Person {name} with ID {id} denied"))] OnlyShorthandArguments { id: i32, name: &'static str }, #[snafu(display("Person {} with ID {id} denied", name))] PositionalAndShorthandArguments { id: i32, name: &'static str }, #[snafu(display("Person {name:?} with ID {id:X} denied"))] ShorthandArgumentsWithSpecs { id: i32, name: &'static str }, #[snafu(display("ID {id} denied"))] FieldNotUsedAsNamedArgument { id: i32, name: &'static str }, #[snafu(display("ID {id} denied at {time}", time = 1 + 1))] AdditionalNamedArguments { id: i32 }, #[snafu(display("Person {name} with ID {id} denied", id = 99))] RedefinedNamedArguments { id: i32, name: &'static str }, /// Person {name} with ID {id} denied ShorthandArgumentsInDocComments { id: i32, name: &'static str }, } #[test] fn supports_positional_formatting() { let error = OnlyPositionalArgumentsSnafu { id: 42 }.build(); assert_eq!(error.to_string(), "ID 42 denied"); } #[test] fn supports_shorthand_formatting() { let error = OnlyShorthandArgumentsSnafu { id: 42, name: "Anna", } .build(); assert_eq!(error.to_string(), "Person Anna with ID 42 denied"); } #[test] fn supports_positional_and_shorthand_formatting() { let error = OnlyShorthandArgumentsSnafu { id: 42, name: "Anna", } .build(); assert_eq!(error.to_string(), "Person Anna with ID 42 denied"); } #[test] fn supports_format_specs() { let error = ShorthandArgumentsWithSpecsSnafu { id: 42, name: "Anna", } .build(); assert_eq!(error.to_string(), r#"Person "Anna" with ID 2A denied"#); } #[test] fn ignores_unused_fields() { let error = FieldNotUsedAsNamedArgumentSnafu { id: 42, name: "Anna", } .build(); assert_eq!(error.to_string(), "ID 42 denied"); } #[test] fn allows_additional_named_arguments() { let error = AdditionalNamedArgumentsSnafu { id: 42 }.build(); assert_eq!(error.to_string(), "ID 42 denied at 2"); } #[test] fn does_not_redefine_user_provided_named_arguments() { let error = RedefinedNamedArgumentsSnafu { id: 42, name: "Anna", } .build(); assert_eq!(error.to_string(), "Person Anna with ID 99 denied"); } #[test] fn allows_shorthand_in_doc_comments() { let error = ShorthandArgumentsInDocCommentsSnafu { id: 42, name: "Anna", } .build(); assert_eq!(error.to_string(), "Person Anna with ID 42 denied"); } snafu-0.7.1/tests/doc_comment.rs000064400000000000000000000014640072674642500150010ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { /// No user available. /// You may need to specify one. /// /// Here is a more detailed description. MissingUser, /// This is just a doc comment. #[snafu(display("This is {}", stronger))] Stronger { stronger: &'static str }, #[doc(hidden)] Hidden, } #[test] fn implements_error() { fn check() {} check::(); } #[test] fn uses_doc_comment() { assert_eq!( Error::MissingUser.to_string(), "No user available. You may need to specify one.", ); } #[test] fn display_is_stronger_than_doc_comment() { assert_eq!( Error::Stronger { stronger: "always stronger!" } .to_string(), "This is always stronger!", ); } snafu-0.7.1/tests/ensure.rs000064400000000000000000000010320072674642500140020ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { AVeryLongVariantName { a_long_piece_of_information: i32 }, } type Result = std::result::Result; #[test] fn accepts_trailing_commas() { fn example(a_long_piece_of_information: i32) -> Result<()> { ensure!( a_long_piece_of_information > a_long_piece_of_information - 1, AVeryLongVariantNameSnafu { a_long_piece_of_information, }, ); Ok(()) } let _ = example(42); } snafu-0.7.1/tests/error_chain.rs000064400000000000000000000031450072674642500150030ustar 00000000000000use snafu::prelude::*; use std::fmt::Debug; #[derive(Debug, Clone, Snafu)] enum LeafError { #[snafu(display("User ID {} is invalid", user_id))] InvalidUser { user_id: i32 }, #[snafu(display("no user available"))] MissingUser, } #[derive(Debug, Clone, Snafu)] enum MiddleError { #[snafu(display("failed to check the user"))] CheckUser { source: LeafError }, } #[derive(Debug, Clone, Snafu)] enum Error { #[snafu(display("access control failure"))] AccessControl { source: MiddleError }, } #[track_caller] fn assert_eq_debug(a: impl Debug, b: impl Debug) { assert_eq!(format!("{:?}", a), format!("{:?}", b)); } #[test] fn chain_compat_iterates() { use snafu::{ChainCompat, IntoError}; let bottom_error = InvalidUserSnafu { user_id: 12 }.build(); let middle_error = CheckUserSnafu.into_error(bottom_error.clone()); let error = AccessControlSnafu.into_error(middle_error.clone()); let errors: Vec<_> = ChainCompat::new(&error).collect(); assert_eq_debug(&errors[0], &error); assert_eq_debug(&errors[1], &middle_error); assert_eq_debug(&errors[2], &bottom_error); } #[test] fn errorcompat_chain_iterates() { use snafu::{ErrorCompat, IntoError}; let bottom_error = InvalidUserSnafu { user_id: 12 }.build(); let middle_error = CheckUserSnafu.into_error(bottom_error.clone()); let error = AccessControlSnafu.into_error(middle_error.clone()); let errors: Vec<_> = ErrorCompat::iter_chain(&error).collect(); assert_eq_debug(&errors[0], &error); assert_eq_debug(&errors[1], &middle_error); assert_eq_debug(&errors[2], &bottom_error); } snafu-0.7.1/tests/generics.rs000064400000000000000000000041220072674642500143030ustar 00000000000000use snafu::{prelude::*, Backtrace}; type BoxError = Box; #[derive(Debug, Snafu)] enum Error<'a, 'x, A, Y> { Everything { source: BoxError, name: &'a str, length: A, backtrace: Backtrace, }, Lifetime { key: &'x i32, }, Type { value: Y, }, } fn cause_error() -> Result<(), BoxError> { Ok(()) } fn example<'s, 'k, V>(name: &'s str, key: &'k i32, value: V) -> Result<(), Error<'s, 'k, usize, V>> where V: std::fmt::Debug, { let length = name.len(); cause_error().context(EverythingSnafu { name, length })?; if name == "alice" { return LifetimeSnafu { key }.fail(); } if name == "bob" { return TypeSnafu { value }.fail(); } Ok(()) } #[test] fn implements_error() { let name = String::from("hello"); let key = Box::new(42); let value = vec![false]; example(&name, &key, value).unwrap(); } mod bounds { mod inline { use snafu::prelude::*; use std::fmt::{Debug, Display}; #[derive(Debug, Snafu)] pub struct ApiError(Error); #[derive(Debug, Snafu)] enum Error { #[snafu(display("Boom: {}", value))] Boom { value: T }, } #[test] fn implements_error() { fn check_bounds() {} check_bounds::>(); check_bounds::>(); } } mod where_clause { use snafu::prelude::*; use std::fmt::{Debug, Display}; #[derive(Debug, Snafu)] pub struct ApiError(Error) where T: Debug + Display; #[derive(Debug, Snafu)] enum Error where T: Display, { #[snafu(display("Boom: {}", value))] Boom { value: T }, } #[test] fn implements_error() { fn check_bounds() {} check_bounds::>(); check_bounds::>(); } } } snafu-0.7.1/tests/generics_with_default.rs000064400000000000000000000015040072674642500170430ustar 00000000000000mod default_with_lifetime { use snafu::{prelude::*, AsErrorSource}; use std::fmt::{Debug, Display}; #[derive(Debug, Snafu)] pub struct ApiError<'a, S, T>(Error<'a, S, T>) where T: Debug + Display, S: std::error::Error + AsErrorSource; #[derive(Debug, Snafu)] enum Error<'a, S = std::io::Error, T = String> where T: Display, S: std::error::Error + AsErrorSource, { #[snafu(display("Boom: {}", value))] Boom { value: T, name: &'a str, }, WithSource { source: S, }, Empty, } #[test] fn implements_error() { fn check_bounds() {} check_bounds::>(); check_bounds::>(); } } snafu-0.7.1/tests/implicit.rs000064400000000000000000000026220072674642500143210ustar 00000000000000use snafu::prelude::*; mod basics { use super::*; use std::sync::atomic::{AtomicUsize, Ordering}; static COUNTER: AtomicUsize = AtomicUsize::new(0); #[derive(Debug)] struct OccurrenceCount(usize); impl snafu::GenerateImplicitData for OccurrenceCount { fn generate() -> Self { OccurrenceCount(COUNTER.fetch_add(1, Ordering::SeqCst)) } } #[derive(Debug, Snafu)] enum ErrorOne { Alpha { #[snafu(implicit)] occurrence: OccurrenceCount, }, } #[test] fn implicit_fields_are_constructed() { let ErrorOne::Alpha { occurrence: OccurrenceCount(o1), } = AlphaSnafu.build(); let ErrorOne::Alpha { occurrence: OccurrenceCount(o2), } = AlphaSnafu.build(); assert_eq!(o1, 0); assert_eq!(o2, 1); } } mod multiple_fields { use super::*; #[derive(Debug, PartialEq)] struct ImplicitData; impl snafu::GenerateImplicitData for ImplicitData { fn generate() -> Self { ImplicitData } } #[derive(Debug, Snafu)] struct Error { #[snafu(implicit)] one: ImplicitData, #[snafu(implicit)] two: ImplicitData, } #[test] fn multiple_implicit_fields_are_constructed() { let Error { one, two } = Snafu.build(); assert_eq!(one, two); } } snafu-0.7.1/tests/location.rs000064400000000000000000000144460072674642500143260ustar 00000000000000use snafu::{prelude::*, Location}; mod basics { use super::*; #[derive(Debug, Snafu)] enum Error { #[snafu(display("Created at {}", location))] Usage { location: Location }, } #[test] fn location_tracks_file_line_and_column() { let one = UsageSnafu.build(); let two = UsageSnafu.build(); assert_eq!(one.to_string(), "Created at tests/location.rs:14:30"); assert_eq!(two.to_string(), "Created at tests/location.rs:15:30"); } } mod opt_out { use super::*; #[derive(Debug, Snafu)] enum Error { #[snafu(display("Created at {}", location))] Usage { #[snafu(implicit(false))] location: String, }, } #[test] fn opting_out_of_automatic_implicit_data() { let error = UsageSnafu { location: "junk" }.build(); assert_eq!(error.to_string(), "Created at junk"); } } mod track_caller { use super::*; #[derive(Debug, Copy, Clone, Snafu)] struct InnerError { location: Location, } #[derive(Debug, Snafu)] struct WrapNoUserFieldsError { source: InnerError, location: Location, } #[derive(Debug, Snafu)] #[snafu(context(false))] struct WrapNoContext { source: InnerError, location: Location, } #[derive(Debug, Snafu)] #[snafu(display("{}", message))] #[snafu(whatever)] pub struct MyWhatever { #[snafu(source(from(Box, Some)))] source: Option>, message: String, location: Location, } #[test] fn track_caller_is_applied_on_build() { let base_line = line!(); let inner = InnerSnafu.build(); assert_eq!( inner.location.line, base_line + 1, "Actual location: {}", inner.location, ); } #[test] fn track_caller_is_applied_on_fail() { let base_line = line!(); let inner = InnerSnafu.fail::<()>().unwrap_err(); assert_eq!( inner.location.line, base_line + 1, "Actual location: {}", inner.location, ); } #[test] fn track_caller_is_applied_on_ensure() { let base_line = line!(); fn x() -> Result<(), InnerError> { ensure!(false, InnerSnafu); Ok(()) } let inner = x().unwrap_err(); assert_eq!( inner.location.line, base_line + 2, "Actual location: {}", inner.location, ); } #[test] fn track_caller_is_applied_on_whatever() { let base_line = line!(); fn x() -> Result<(), MyWhatever> { whatever!("bang"); } let inner = x().unwrap_err(); assert_eq!( inner.location.line, base_line + 2, "Actual location: {}", inner.location, ); } #[test] fn track_caller_is_applied_on_result_context() { let base_line = line!(); let wrap_no_user_fields = InnerSnafu .fail::<()>() .context(WrapNoUserFieldsSnafu) .unwrap_err(); assert_eq!( wrap_no_user_fields.location.line, base_line + 3, "Actual location: {}", wrap_no_user_fields.location, ); } #[test] fn track_caller_is_applied_on_result_with_context() { let base_line = line!(); let wrap_no_user_fields = InnerSnafu .fail::<()>() .with_context(|_| WrapNoUserFieldsSnafu) .unwrap_err(); assert_eq!( wrap_no_user_fields.location.line, base_line + 3, "Actual location: {}", wrap_no_user_fields.location, ); } #[test] fn track_caller_is_applied_on_result_whatever_context() { let base_line = line!(); let whatever: MyWhatever = InnerSnafu .fail::<()>() .whatever_context("bang") .unwrap_err(); assert_eq!( whatever.location.line, base_line + 3, "Actual location: {}", whatever.location, ); } #[test] fn track_caller_is_applied_on_result_with_whatever_context() { let base_line = line!(); let whatever: MyWhatever = InnerSnafu .fail::<()>() .with_whatever_context(|_| "bang") .unwrap_err(); assert_eq!( whatever.location.line, base_line + 3, "Actual location: {}", whatever.location, ); } #[test] fn track_caller_is_applied_on_option_context() { let base_line = line!(); let option_to_error_no_user_fields = None::<()>.context(InnerSnafu).unwrap_err(); assert_eq!( option_to_error_no_user_fields.location.line, base_line + 1, "Actual location: {}", option_to_error_no_user_fields.location, ); } #[test] fn track_caller_is_applied_on_option_with_context() { let base_line = line!(); let option_to_error_no_user_fields = None::<()>.with_context(|| InnerSnafu).unwrap_err(); assert_eq!( option_to_error_no_user_fields.location.line, base_line + 1, "Actual location: {}", option_to_error_no_user_fields.location, ); } #[test] fn track_caller_is_applied_on_option_whatever_context() { let base_line = line!(); let whatever: MyWhatever = None::<()>.whatever_context("bang").unwrap_err(); assert_eq!( whatever.location.line, base_line + 1, "Actual location: {}", whatever.location, ); } #[test] fn track_caller_is_applied_on_option_with_whatever_context() { let base_line = line!(); let whatever: MyWhatever = None::<()>.with_whatever_context(|| "bang").unwrap_err(); assert_eq!( whatever.location.line, base_line + 1, "Actual location: {}", whatever.location, ); } // `track_caller` not supported on the `Try` trait, so we have no // useful location for `context(false)` errors. Check back in the // future to see if there's a fix. } snafu-0.7.1/tests/mapping_result_without_try_operator.rs000064400000000000000000000010050072674642500221260ustar 00000000000000// This test asserts that `ResultExt::eager_context` can be used even // when `try!` or `?` is not used. use snafu::prelude::*; #[derive(Debug, Snafu)] enum InnerError { Alpha, } #[derive(Debug, Snafu)] enum OuterError { Beta { source: InnerError }, } fn function_1() -> Result { AlphaSnafu.fail() } fn function_2() -> Result { function_1().map(|v| v < 42).context(BetaSnafu) } #[test] fn can_be_used_without_question_mark() { function_2().unwrap_err(); } snafu-0.7.1/tests/module.rs000064400000000000000000000043540072674642500140000ustar 00000000000000pub mod inner { use snafu::Snafu; #[derive(Debug)] pub struct Dummy0; #[derive(Debug)] pub struct Dummy1; #[derive(Debug)] pub struct Dummy2; #[derive(Debug)] pub struct Dummy3; #[derive(Debug, Snafu)] #[snafu(module, visibility(pub))] pub enum PubError { Variant { v: Dummy0 }, } #[derive(Debug, Snafu)] #[snafu(module(custom_pub), visibility(pub))] pub enum PubWithCustomModError { Variant { v: Dummy1 }, } #[derive(Debug, Snafu)] #[snafu(module(custom_pub_crate), visibility(pub(crate)))] pub(crate) enum PubCrateWithCustomModError { Variant { v: Dummy2 }, } mod child { use super::Dummy3; use snafu::Snafu; #[derive(Debug, Snafu)] #[snafu(module, visibility(pub(in crate::inner)))] pub enum RestrictedError { Variant { v: Dummy3 }, } } #[test] fn can_set_module_visibility_restricted() { let _ = self::child::restricted_error::VariantSnafu { v: Dummy3 }.build(); } } use self::inner::Dummy1; use snafu::Snafu; #[derive(Debug, Snafu)] #[snafu(module)] pub enum SomeError { Variant { v: i32 }, } #[derive(Debug, Snafu)] #[snafu(module)] pub enum QualifiedError { Variant { unqualified: Dummy1, mod_struct: inner::Dummy0, self_struct: self::Dummy1, crate_struct: crate::Dummy1, boxed_trait: Box, }, } #[test] fn can_use_qualified_names_in_module() { let _ = qualified_error::VariantSnafu { unqualified: Dummy1, mod_struct: inner::Dummy0, self_struct: self::Dummy1, crate_struct: crate::Dummy1, boxed_trait: Box::new(()) as Box<_>, } .build(); } #[test] fn can_set_module() { let _ = some_error::VariantSnafu { v: 0i32 }.build(); } #[test] fn can_set_module_visibility_pub() { let _ = inner::pub_error::VariantSnafu { v: inner::Dummy0 }.build(); } #[test] fn can_set_module_visibility_pub_with_custom_name() { let _ = inner::custom_pub::VariantSnafu { v: inner::Dummy1 }.build(); } #[test] fn can_set_module_visibility_pub_crate_with_custom_name() { let _ = inner::custom_pub_crate::VariantSnafu { v: inner::Dummy2 }.build(); } snafu-0.7.1/tests/multiple_attributes.rs000064400000000000000000000015770072674642500166200ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum InnerError { InnerVariant, } mod error { use super::InnerError; use snafu::prelude::*; #[derive(Debug, Snafu)] pub(super) enum Error { // We'll test both of these attributes inside `#[snafu(...)]` #[snafu(visibility(pub(super)), display("Moo"))] Alpha { // Ensure we can have multiple field attributes as well #[snafu(source, backtrace)] cause: InnerError, }, } } // Confirm `pub(super)` is applied to the generated struct #[test] fn is_visible() { let _ = error::AlphaSnafu; } fn example() -> Result { InnerVariantSnafu.fail() } // Confirm `display("Moo")` is applied to the variant #[test] fn has_display() { let err = example().context(error::AlphaSnafu).unwrap_err(); assert_eq!(format!("{}", err), "Moo"); } snafu-0.7.1/tests/name-conflicts.rs000064400000000000000000000010620072674642500154060ustar 00000000000000use ::snafu as real_snafu; use real_snafu::{ensure, Snafu}; // Likely candidates to clash with generated code mod core {} mod std {} mod snafu {} #[derive(Debug, Snafu)] enum VariantNamedNone { #[snafu(context(suffix(false)))] None, } #[derive(Debug, Snafu)] enum VariantNamedSome { Some { value: T }, } #[derive(Debug, Snafu)] enum VariantNamedOk { Ok { value: T }, } #[derive(Debug, Snafu)] enum VariantNamedErr { Err { value: T }, } fn _using_ensure() -> Result { ensure!(false, None); Ok(0) } snafu-0.7.1/tests/no_context.rs000064400000000000000000000026230072674642500146700ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum AlphaError { AlphaDummy, } #[derive(Debug, Snafu)] enum BetaError { BetaDummy, } #[derive(Debug, Snafu)] enum Error { #[snafu(context(false))] Alpha { source: AlphaError }, #[snafu(context(false))] Beta { source: BetaError }, } fn alpha() -> Result { Ok(1) } fn beta() -> Result { Ok(2) } fn example() -> Result { let a = alpha()?; let b = beta()?; Ok(a * 10 + b) } fn check() {} #[test] fn implements_error() { check::(); assert_eq!(12, example().unwrap()); } mod with_backtraces { use super::*; use snafu::Backtrace; #[derive(Debug, Snafu)] enum Error { #[snafu(context(false))] Alpha { source: AlphaError, backtrace: Backtrace, }, } #[test] fn implements_error() { check::(); } } mod with_bounds { use super::*; use std::fmt::{Debug, Display}; #[derive(Debug, Snafu)] enum GenericError { Something { things: T, other: U }, } #[derive(Debug, Snafu)] enum Error where T: Debug + Display + Copy, { #[snafu(context(false))] Generic { source: GenericError }, } #[test] fn implements_error() { check::>(); } } snafu-0.7.1/tests/opaque.rs000064400000000000000000000025000072674642500137740ustar 00000000000000mod inner { use snafu::prelude::*; #[derive(Debug, Snafu)] pub struct Error(InnerError); pub fn api() -> Result { Ok(a()? + b()?) } pub fn not_positive(value: i32) -> Result { ensure!(value < 1, TooBigSnafu { count: value }); Ok(value) } pub fn boxed_inner(value: i32) -> Result> { ensure!(value < 1, TooBigSnafu { count: value }); Ok(value) } #[derive(Debug, Snafu)] enum InnerError { #[snafu(display("The value {} is too big", count))] TooBig { count: i32 }, } fn a() -> Result { TooBigSnafu { count: 1 }.fail() } fn b() -> Result { TooBigSnafu { count: 2 }.fail() } } #[test] fn implements_error() { fn check() {} check::(); let e = inner::api().unwrap_err(); assert!(e.to_string().contains("too big")); } #[test] fn ensure_opaque() { assert!(inner::not_positive(-1).is_ok()); let e = inner::not_positive(2).unwrap_err(); assert!(e.to_string().contains("too big")); } #[test] fn ensure_boxed() { assert!(inner::boxed_inner(-1).is_ok()); let e = inner::boxed_inner(2).unwrap_err(); assert!(e.to_string().contains("too big")); } snafu-0.7.1/tests/options.rs000064400000000000000000000015210072674642500141770ustar 00000000000000use snafu::{prelude::*, Backtrace}; use std::collections::HashMap; #[derive(Debug, Snafu)] enum Error { #[snafu(display("The left-hand argument {} was missing", "id"))] LeftHandMissing { id: i32, backtrace: Backtrace }, #[snafu(display("The right-hand argument {} was missing", "id"))] RightHandMissing { id: i32, backtrace: Backtrace }, } type Result = std::result::Result; fn example(values: &HashMap, left: i32, right: i32) -> Result { let l = values .get(&left) .context(LeftHandMissingSnafu { id: left })?; let r = values .get(&right) .context(RightHandMissingSnafu { id: right })?; Ok(l + r) } #[test] fn implements_error() { fn check() {} check::(); example(&Default::default(), 1, 2).unwrap_err(); } snafu-0.7.1/tests/premade_error.rs000064400000000000000000000054740072674642500153450ustar 00000000000000use snafu::{prelude::*, Whatever}; type Result = std::result::Result; // Using fully-qualified paths here to ensure the module's imports are // minimal #[derive(Debug, snafu::Snafu)] pub struct UnderlyingError; pub fn underlying(success: bool) -> Result { snafu::ensure!(success, UnderlyingSnafu); Ok(1) } #[test] fn implements_error() { fn check() {} check::(); } #[test] fn does_not_need_a_cause() { use std::error::Error as _; fn exercise(success: bool) -> Result { if !success { whatever!("I caused a problem {}", 42); } Ok(1) } let e = exercise(false).unwrap_err(); assert!(e.source().is_none()); } #[test] fn can_wrap_cause_with_a_formatted_string_via_macro() { use std::error::Error as _; fn exercise(success: bool) -> Result { let v = whatever!(underlying(success), "Something else happened {}", 42); Ok(v + 1) } assert!(matches!(exercise(true), Ok(2))); let e = exercise(false).unwrap_err(); assert_eq!("Something else happened 42", e.to_string()); let src = e.source().expect("Must have a source"); let src = src.downcast_ref::(); assert!(src.is_some()); } #[test] fn can_wrap_cause_with_a_formatted_string_via_trait() { use std::error::Error as _; fn exercise(success: bool) -> Result { let v = underlying(success) .with_whatever_context(|_| format!("Something else happened {}", 42))?; Ok(v + 1) } assert!(matches!(exercise(true), Ok(2))); let e = exercise(false).unwrap_err(); assert_eq!("Something else happened 42", e.to_string()); let src = e.source().expect("Must have a source"); let src = src.downcast_ref::(); assert!(src.is_some()); } #[test] fn can_be_recursive() { use std::error::Error as _; fn inner(success: bool) -> Result { if !success { whatever!("Inner error"); } Ok(1) } fn outer(success: bool) -> Result { let v = whatever!(inner(success), "Outer error"); Ok(v + 1) } assert!(matches!(outer(true), Ok(2))); let outer_error = outer(false).unwrap_err(); let inner_error = outer_error.source().expect("Must have a source"); assert!(inner_error.downcast_ref::().is_some()); assert!(inner_error.source().is_none()); } #[test] fn has_a_backtrace() { use snafu::ErrorCompat; fn exercise(success: bool) -> Result { let v = whatever!(underlying(success), "Something else happened {}", 42); Ok(v + 1) } let e = exercise(false).unwrap_err(); let bt = ErrorCompat::backtrace(&e).expect("Must have a backtrace"); assert_eq!("disabled backtrace", bt.to_string()); } snafu-0.7.1/tests/raw_idents.rs000064400000000000000000000013270072674642500146470ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { #[snafu(display("{}", r#type))] Example { r#type: String }, r#Awesome { #[snafu(source(from(Error, Box::new)))] r#mod: Box, }, } type Result = std::result::Result; #[test] fn implements_error() { fn check() {} check::(); } #[test] fn creates_context_selectors() { fn one(success: bool) -> Result<()> { ensure!(success, ExampleSnafu { r#type: "boom" }); Ok(()) } fn two(success: bool) -> Result<()> { one(success).context(r#AwesomeSnafu)?; Ok(()) } assert!(two(true).is_ok()); assert!(two(false).is_err()); } snafu-0.7.1/tests/recursive_error.rs000064400000000000000000000015340072674642500157300ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { Leaf { name: String, }, BoxedSelf { #[snafu(source(from(Error, Box::new)))] source: Box, }, BoxedPublic { #[snafu(source(from(ApiError, Box::new)))] source: Box, }, } #[derive(Debug, Snafu)] #[snafu(source(from(Error, Box::new)))] struct ApiError(Box); type Result = std::result::Result; fn lookup() -> Result<()> { LeafSnafu { name: "foo" }.fail() } fn add() -> Result<()> { lookup().context(BoxedSelfSnafu) } fn public() -> Result<(), ApiError> { add()?; Ok(()) } fn re_private() -> Result<()> { public().context(BoxedPublicSnafu) } #[test] fn implements_error() { fn check() {} check::(); re_private().unwrap_err(); } snafu-0.7.1/tests/send_between_threads.rs000064400000000000000000000017430072674642500166660ustar 00000000000000// This test asserts that errors can be used across threads. use std::thread; use snafu::prelude::*; #[derive(Debug, Snafu)] enum InnerError { Boom, } #[derive(Debug, Snafu)] enum Error { Leaf { name: String, }, Wrapper { source: InnerError, }, BoxedWrapper { source: Box, }, BoxedTraitObjectSend { source: Box, }, BoxedTraitObjectSendSync { source: Box, }, } fn example() -> Result<(), Error> { BoomSnafu.fail().context(WrapperSnafu) } #[test] fn implements_thread_safe_error() { fn check_error() {} check_error::(); check_error::(); fn check_send() {} check_send::(); check_send::(); let t = thread::spawn(move || example()); let v = t.join().expect("Thread panicked"); v.unwrap_err(); } snafu-0.7.1/tests/single_use_lifetimes_lint.rs000064400000000000000000000005600072674642500177320ustar 00000000000000#![deny(single_use_lifetimes)] use snafu::prelude::*; #[derive(Debug, Snafu)] pub enum Enum<'id> { #[snafu(display("`{}` is foo, yo", to))] Foo { to: &'id u32 }, #[snafu(display("bar `{}` frobnicated `{}`", from, to))] Bar { from: &'id String, to: &'id i8 }, } #[derive(Debug, Snafu)] pub struct Struct<'id>(Enum<'id>); #[test] fn it_compiles() {} snafu-0.7.1/tests/source_attributes.rs000064400000000000000000000046250072674642500162620ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] enum InnerError { Boom, } fn inner() -> Result<(), InnerError> { Ok(()) } mod enabling { use super::*; #[derive(Debug, Snafu)] enum Error { NoArgument { #[snafu(source)] cause: InnerError, }, ExplicitTrue { #[snafu(source(true))] cause: InnerError, }, FromImpliesTrue { #[snafu(source(from(InnerError, Box::new)))] cause: Box, }, ExplicitFalse { #[snafu(source(false))] source: i32, }, } fn example() -> Result<(), Error> { inner().context(NoArgumentSnafu)?; inner().context(ExplicitTrueSnafu)?; inner().context(FromImpliesTrueSnafu)?; ExplicitFalseSnafu { source: 42 }.fail()?; Ok(()) } #[test] fn implements_error() { fn check() {} check::(); example().unwrap_err(); } } mod transformation { use super::*; use std::io; #[derive(Debug, Snafu)] enum Error { TransformationViaClosure { #[snafu(source(from(InnerError, |e| io::Error::new(io::ErrorKind::InvalidData, e))))] source: io::Error, }, TransformationViaFunction { #[snafu(source(from(InnerError, into_io)))] source: io::Error, }, TransformationToTraitObject { #[snafu(source(from(InnerError, Box::new)))] source: Box, }, } fn into_io(e: InnerError) -> io::Error { io::Error::new(io::ErrorKind::InvalidData, e) } fn example() -> Result<(), Error> { inner().context(TransformationViaClosureSnafu)?; inner().context(TransformationViaFunctionSnafu)?; inner().context(TransformationToTraitObjectSnafu)?; Ok(()) } #[test] fn implements_error() { fn check() {} check::(); example().unwrap(); } #[derive(Debug, Snafu)] #[snafu(source(from(Error, Box::new)))] struct ApiError(Box); fn api_example() -> Result<(), ApiError> { example()?; Ok(()) } #[test] fn api_implements_error() { fn check() {} check::(); api_example().unwrap(); } } snafu-0.7.1/tests/stringly_typed.rs000064400000000000000000000174350072674642500155770ustar 00000000000000// TODO: ensure no-std still works // THOUGHT: Source must always be an `Option`. This seems to force the `from()` mod message_only { use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { #[snafu(whatever, display("{}", message))] Whatever { message: String }, } // THOUGHT: Allow identify message field? type Result = std::result::Result; #[test] fn implements_error() { fn check() {} check::(); } #[test] fn can_use_a_formatted_string() { fn exercise(success: bool) -> Result { if !success { whatever!("This is a code {} error", 42); } Ok(1) } assert!(matches!(exercise(true), Ok(1))); let e = exercise(false).unwrap_err(); assert_eq!("This is a code 42 error", e.to_string()); } } // THOUGHT: Must it be boxed trait object? // No, it *can* be a fixed type, but that's very limiting mod message_and_source { use snafu::prelude::*; #[derive(Debug, Snafu)] struct UnderlyingError; fn underlying(success: bool) -> Result { ensure!(success, UnderlyingSnafu); Ok(1) } #[derive(Debug, Snafu)] enum Error { // THOUGHT: Should display automatically do message? #[snafu(whatever, display("{}", message))] Whatever { #[snafu(source(from(Box, Some)))] source: Option>, message: String, }, } type Result = std::result::Result; #[test] fn implements_error() { fn check() {} check::(); } #[test] fn can_use_a_formatted_string_via_macro() { fn exercise(success: bool) -> Result { let v = whatever!(underlying(success), "Something else happened {}", 42); Ok(v + 1) } assert!(matches!(exercise(true), Ok(2))); let e = exercise(false).unwrap_err(); assert_eq!("Something else happened 42", e.to_string()); } #[test] fn can_use_a_formatted_string_via_trait() { fn exercise(success: bool) -> Result { let v = underlying(success) .with_whatever_context(|_| format!("Something else happened {}", 42))?; Ok(v + 1) } assert!(matches!(exercise(true), Ok(2))); let e = exercise(false).unwrap_err(); assert_eq!("Something else happened 42", e.to_string()); } #[test] fn can_access_the_cause() { use std::error::Error as _; fn exercise(success: bool) -> Result { let v = whatever!(underlying(success), "Something else happened {}", 42); Ok(v + 1) } let e = exercise(false).unwrap_err(); let src = e.source().expect("Must have a source"); let src = src.downcast_ref::(); assert!(src.is_some()); } #[test] fn does_not_need_a_cause() { use std::error::Error as _; fn exercise(success: bool) -> Result { if !success { whatever!("I caused a problem {}", 42); } Ok(1) } let e = exercise(false).unwrap_err(); assert!(e.source().is_none()); } #[test] fn can_be_recursive() { use std::error::Error as _; fn inner(success: bool) -> Result { if !success { whatever!("Inner error"); } Ok(1) } fn outer(success: bool) -> Result { let v = whatever!(inner(success), "Outer error"); Ok(v + 1) } assert!(matches!(outer(true), Ok(2))); let outer_error = outer(false).unwrap_err(); let inner_error = outer_error.source().expect("Must have a source"); assert!(inner_error.downcast_ref::().is_some()); assert!(inner_error.source().is_none()); } } mod message_source_and_backtrace { use snafu::{prelude::*, Backtrace, ErrorCompat}; #[derive(Debug, Snafu)] struct UnderlyingError; fn underlying(success: bool) -> Result { ensure!(success, UnderlyingSnafu); Ok(1) } #[derive(Debug, Snafu)] enum Error { #[snafu(whatever, display("{}", message))] Whatever { #[snafu(source(from(Box, Some)))] source: Option>, message: String, backtrace: Backtrace, }, } type Result = std::result::Result; #[test] fn implements_error() { fn check() {} check::(); } #[test] fn can_use_a_formatted_string() { fn exercise(success: bool) -> Result { let v = whatever!(underlying(success), "Something else happened {}", 42); Ok(v + 1) } assert!(matches!(exercise(true), Ok(2))); let e = exercise(false).unwrap_err(); assert_eq!("Something else happened 42", e.to_string()); } #[test] fn has_a_backtrace() { use snafu::prelude::*; fn exercise(success: bool) -> Result { let v = whatever!(underlying(success), "Something else happened {}", 42); Ok(v + 1) } let e = exercise(false).unwrap_err(); let bt = ErrorCompat::backtrace(&e).expect("Must have a backtrace"); assert_eq!("disabled backtrace", bt.to_string()); } } mod struck { mod message_source_and_backtrace { use snafu::{prelude::*, Backtrace}; #[derive(Debug, Snafu)] #[snafu(whatever, display("{}", message))] struct Error { #[snafu(source(from(Box, Some)))] source: Option>, message: String, backtrace: Backtrace, } type Result = std::result::Result; #[test] fn implements_error() { fn check() {} check::(); } #[test] fn can_use_a_formatted_string() { fn inner(success: bool) -> Result { if !success { whatever!("inner went {}", "bang"); } Ok(1) } fn outer(success: bool) -> Result { let v = whatever!(inner(success), "Something else happened {}", 42); Ok(v + 1) } assert!(matches!(outer(true), Ok(2))); let e = outer(false).unwrap_err(); assert_eq!("Something else happened 42", e.to_string()); } } } mod send_and_sync { use snafu::prelude::*; #[derive(Debug, Snafu)] enum Error { #[snafu(whatever, display("{}", message))] Whatever { #[snafu(source(from(Box, Some)))] source: Option>, message: String, }, } type Result = std::result::Result; #[test] fn implements_error() { fn check() {} check::(); } #[test] fn implements_send() { fn check() {} check::(); } #[test] fn implements_sync() { fn check() {} check::(); } #[test] fn can_be_constructed() { fn inner() -> Result<()> { whatever!("The inner case") } fn outer() -> Result<()> { whatever!(inner(), "The outer case"); Ok(()) } assert!(outer().is_err()); } } snafu-0.7.1/tests/structs/backtrace.rs000064400000000000000000000020650072674642500161360ustar 00000000000000use snafu::{prelude::*, Backtrace, ErrorCompat}; #[test] fn can_include_a_backtrace_in_leaf() { #[derive(Debug, Snafu)] struct Error { backtrace: Backtrace, } let e = Snafu.build(); let backtrace = ErrorCompat::backtrace(&e); assert!(backtrace.is_some()); } #[test] fn can_include_a_backtrace_with_source() { use snafu::IntoError; #[derive(Debug, Snafu)] struct InnerError; #[derive(Debug, Snafu)] struct Error { source: InnerError, backtrace: Backtrace, } let i = InnerSnafu.build(); let e = Snafu.into_error(i); let backtrace = ErrorCompat::backtrace(&e); assert!(backtrace.is_some()); } #[test] fn can_include_a_backtrace_with_no_context() { #[derive(Debug, Snafu)] struct InnerError; #[derive(Debug, Snafu)] #[snafu(context(false))] struct Error { source: InnerError, backtrace: Backtrace, } let i = InnerSnafu.build(); let e = Error::from(i); let backtrace = ErrorCompat::backtrace(&e); assert!(backtrace.is_some()); } snafu-0.7.1/tests/structs/backtrace_attributes.rs000064400000000000000000000011700072674642500204000ustar 00000000000000use snafu::{prelude::*, Backtrace}; #[test] fn no_argument_treated_as_backtrace() { #[derive(Debug, Snafu)] struct Error { #[snafu(backtrace)] thing: Backtrace, } let _ = Snafu.build(); } #[test] fn explicit_true_treated_as_backtrace() { #[derive(Debug, Snafu)] struct Error { #[snafu(backtrace(true))] thing: Backtrace, } let _ = Snafu.build(); } #[test] fn explicit_false_not_treated_as_backtrace() { #[derive(Debug, Snafu)] struct Error { #[snafu(backtrace(false))] backtrace: i32, } let _ = Snafu { backtrace: 42 }.build(); } snafu-0.7.1/tests/structs/context_selector_name.rs000064400000000000000000000012740072674642500206040ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(context(suffix(Moo)))] struct Alpha; fn alpha_usage() -> Result<(), Alpha> { AlphaMoo.fail() } #[test] fn alpha_implements_error() { check::(); alpha_usage().unwrap_err(); } #[derive(Debug, Snafu)] #[snafu(context(suffix(Baa)))] struct TrimsWhenEndingInError; fn trimming_usage() -> Result<(), TrimsWhenEndingInError> { TrimsWhenEndingInBaa.fail() } #[test] fn trimming_implements_error() { check::(); trimming_usage().unwrap_err(); } // `context(suffix(false))` doesn't make sense for structs because the // struct itself already has that name. fn check() {} snafu-0.7.1/tests/structs/display.rs000064400000000000000000000013500072674642500156600ustar 00000000000000use snafu::prelude::*; #[test] fn defaults_to_name_of_struct() { #[derive(Debug, Snafu)] struct Error; let e = Snafu.build(); let display = e.to_string(); assert_eq!(display, "Error"); } #[test] fn doc_comment_used_as_display() { /// This is a bad thing /// /// It's an error! #[derive(Debug, Snafu)] struct Error; let e = Snafu.build(); let display = e.to_string(); assert_eq!(display, "This is a bad thing"); } #[test] fn attribute_is_stronger_than_doc_comment() { /// This is ignored #[derive(Debug, Snafu)] #[snafu(display("In favor of this"))] struct Error; let e = Snafu.build(); let display = e.to_string(); assert_eq!(display, "In favor of this"); } snafu-0.7.1/tests/structs/from_option.rs000064400000000000000000000007760072674642500165610ustar 00000000000000use snafu::{prelude::*, Backtrace}; #[derive(Debug, Snafu)] #[snafu(display("The argument at index {} was missing", idx))] struct Error { idx: usize, backtrace: Backtrace, } type Result = std::result::Result; #[test] fn can_be_used_as_context_on_an_option() { fn example(values: &[i32], idx: usize) -> Result { values.get(idx).copied().context(Snafu { idx }) } let actual = example(&[], 42); assert!(matches!(actual, Err(Error { idx: 42, .. }))); } snafu-0.7.1/tests/structs/generics.rs000064400000000000000000000040300072674642500160100ustar 00000000000000mod lifetimes { use snafu::prelude::*; #[derive(Debug, Snafu)] struct Error<'a> { key: &'a i32, } #[test] fn are_allowed() { let key = 42; let e = Snafu { key: &key }.build(); assert_eq!(*e.key, key); } } mod types { use snafu::prelude::*; #[derive(Debug, Snafu)] struct Error { key: T, } #[test] fn are_allowed() { let key = 42; let e: Error = Snafu { key }.build(); assert_eq!(e.key, key); } mod with_defaults { use snafu::{prelude::*, AsErrorSource}; use std::{error::Error as StdError, fmt::Debug, io}; #[derive(Debug, Snafu)] struct Error where S: StdError + AsErrorSource, T: Debug, { source: S, key: T, } #[test] fn allows_non_default_types() { #[derive(Debug, Snafu)] struct AnotherError; let r = AnotherSnafu.fail::<()>(); let _e: Error<_, u8> = r.context(Snafu { key: 42 }).unwrap_err(); } } } mod bounds { mod inline { use snafu::prelude::*; use std::fmt::Display; #[derive(Debug, Snafu)] #[snafu(display("key: {}", key))] struct Error { key: T, } #[test] fn are_preserved() { let e: Error = Snafu { key: true }.build(); let display = e.to_string(); assert_eq!(display, "key: true"); } } mod where_clause { use snafu::prelude::*; use std::fmt::Display; #[derive(Debug, Snafu)] #[snafu(display("key: {}", key))] struct Error where T: Display, { key: T, } #[test] fn are_preserved() { let e: Error = Snafu { key: true }.build(); let display = e.to_string(); assert_eq!(display, "key: true"); } } } snafu-0.7.1/tests/structs/main.rs000064400000000000000000000006040072674642500151400ustar 00000000000000// Using #[path] to work around a https://github.com/rust-lang/rustfmt/issues/4404 // Once fixed and released, switch to a `mod structs { ... }` mod backtrace; mod backtrace_attributes; mod context_selector_name; mod display; mod from_option; mod generics; mod module; mod no_context; mod single_use_lifetimes; mod source_attributes; mod visibility; mod with_source; mod without_source; snafu-0.7.1/tests/structs/module.rs000064400000000000000000000034310072674642500155020ustar 00000000000000pub mod inner { use snafu::Snafu; #[derive(Debug)] pub struct Dummy0; #[derive(Debug)] pub struct Dummy1; #[derive(Debug, Snafu)] #[snafu(module, visibility(pub))] pub struct PubError; #[derive(Debug, Snafu)] #[snafu(module(custom_pub), visibility(pub))] pub struct PubWithCustomModError; #[derive(Debug, Snafu)] #[snafu(module(custom_pub_crate), visibility(pub(crate)))] pub(crate) struct PubCrateWithCustomModError; #[derive(Debug, Snafu)] #[snafu(module, visibility(pub(in crate::module)))] pub struct RestrictedError; } use self::inner::Dummy1; use snafu::Snafu; #[derive(Debug, Snafu)] #[snafu(module)] pub struct SomeError; #[derive(Debug, Snafu)] #[snafu(module)] pub struct QualifiedError { unqualified: Dummy1, mod_struct: inner::Dummy0, self_struct: self::Dummy1, crate_struct: crate::module::Dummy1, boxed_trait: Box, } #[test] fn can_use_qualified_names_in_module() { let _ = qualified_error::QualifiedSnafu { unqualified: Dummy1, mod_struct: inner::Dummy0, self_struct: self::Dummy1, crate_struct: crate::module::Dummy1, boxed_trait: Box::new(()) as Box<_>, } .build(); } #[test] fn can_set_module() { let _ = some_error::SomeSnafu.build(); } #[test] fn can_set_module_visibility_pub() { let _ = inner::pub_error::PubSnafu.build(); } #[test] fn can_set_module_visibility_restricted() { let _ = inner::restricted_error::RestrictedSnafu.build(); } #[test] fn can_set_module_visibility_pub_with_custom_name() { let _ = inner::custom_pub::PubWithCustomModSnafu.build(); } #[test] fn can_set_module_visibility_pub_crate_with_custom_name() { let _ = inner::custom_pub_crate::PubCrateWithCustomModSnafu.build(); } snafu-0.7.1/tests/structs/no_context.rs000064400000000000000000000014260072674642500163770ustar 00000000000000use snafu::prelude::*; #[derive(Debug, Snafu)] struct InnerError; fn inner() -> Result<(), InnerError> { Ok(()) } #[derive(Debug, Snafu)] #[snafu(context(false))] struct OuterError { source: InnerError, } #[test] fn does_not_need_context_method() { fn exercise() -> Result<(), OuterError> { inner()?; Ok(()) } let _ = exercise(); } mod with_source_transformation { use super::*; #[derive(Debug, Snafu)] #[snafu(context(false))] struct OuterError { #[snafu(source(from(InnerError, Box::new)))] source: Box, } #[test] fn does_not_need_context_method() { fn exercise() -> Result<(), OuterError> { inner()?; Ok(()) } let _ = exercise(); } } snafu-0.7.1/tests/structs/single_use_lifetimes.rs000064400000000000000000000003230072674642500204100ustar 00000000000000#![deny(single_use_lifetimes)] use snafu::prelude::*; #[test] fn an_error_with_generic_lifetimes_does_not_trigger_the_lint() { #[derive(Debug, Snafu)] struct Error<'id> { to: &'id u32, } } snafu-0.7.1/tests/structs/source_attributes.rs000064400000000000000000000041200072674642500177570ustar 00000000000000use snafu::prelude::*; mod enabling { use super::*; #[test] fn no_argument_treated_as_source() { #[derive(Debug, Snafu)] struct Error { #[snafu(source)] cause: InnerError, } let _ = inner().context(Snafu); } #[test] fn true_argument_treated_as_source() { #[derive(Debug, Snafu)] struct Error { #[snafu(source(true))] cause: InnerError, } let _ = inner().context(Snafu); } #[test] fn from_argument_treated_as_source() { #[derive(Debug, Snafu)] struct Error { #[snafu(source(from(InnerError, Box::new)))] cause: Box, } let _ = inner().context(Snafu); } #[test] fn false_argument_not_treated_as_source() { #[derive(Debug, Snafu)] struct Error { #[snafu(source(false))] source: i32, } let _ = Snafu { source: 42 }.build(); } } mod transformation { use super::*; use std::{error::Error as StdError, io}; #[test] fn transformation_via_closure() { #[derive(Debug, Snafu)] struct Error { #[snafu(source(from(InnerError, |e| io::Error::new(io::ErrorKind::InvalidData, e))))] source: io::Error, } let _ = inner().context(Snafu); } #[test] fn transformation_via_function() { fn into_io(e: InnerError) -> io::Error { io::Error::new(io::ErrorKind::InvalidData, e) } #[derive(Debug, Snafu)] struct Error { #[snafu(source(from(InnerError, into_io)))] source: io::Error, } let _ = inner().context(Snafu); } #[test] fn transformation_to_trait_object() { #[derive(Debug, Snafu)] struct Error { #[snafu(source(from(InnerError, Box::new)))] source: Box, } let _ = inner().context(Snafu); } } #[derive(Debug, Snafu)] struct InnerError; fn inner() -> Result<(), InnerError> { Ok(()) } snafu-0.7.1/tests/structs/visibility.rs000064400000000000000000000003650072674642500164070ustar 00000000000000// There are also sad-path tests pub mod inner { use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(visibility(pub(crate)))] pub(crate) struct Error; } #[test] fn can_set_visibility() { let _ = inner::Snafu.build(); } snafu-0.7.1/tests/structs/with_source.rs000064400000000000000000000022260072674642500165510ustar 00000000000000use snafu::prelude::*; use std::{ error::Error as StdError, fs, io, path::{Path, PathBuf}, ptr, }; #[derive(Debug, Snafu)] #[snafu(display("filename: {}, source: {}", filename.display(), source))] struct Error { filename: PathBuf, source: io::Error, } type Result = std::result::Result; fn example(filename: impl AsRef) -> Result<()> { let filename = filename.as_ref(); let _config = fs::read(filename).context(Snafu { filename })?; Ok(()) } #[test] fn implements_error() { fn check() {} check::(); let path = "/some/directory/that/does/not/exist"; let e = example(path).unwrap_err(); assert_eq!(e.filename, Path::new(path)); let source = e.source().expect("Source must be present"); let source_as_io_error = source .downcast_ref::() .expect("Source must be io::Error"); assert!(ptr::eq(source_as_io_error, &e.source)); let display = e.to_string(); assert!( display.starts_with("filename: /some/directory/that/does/not/exist, source: "), "Display string incorrect, is: {}", display, ); } snafu-0.7.1/tests/structs/without_source.rs000064400000000000000000000011560072674642500173020ustar 00000000000000use snafu::prelude::*; use std::error::Error as StdError; #[derive(Debug, Snafu)] #[snafu(display("name: [{}]", name))] struct Error { name: String, } type Result = std::result::Result; fn example(name: &str) -> Result<()> { ensure!(name.is_empty(), Snafu { name }); Ok(()) } #[test] fn implements_error() { fn check() {} check::(); let name = "must be empty"; let e = example(name).unwrap_err(); assert_eq!(e.name, name); assert!(e.source().is_none()); let display = e.to_string(); assert_eq!(display, "name: [must be empty]"); } snafu-0.7.1/tests/visibility.rs000064400000000000000000000015120072674642500146730ustar 00000000000000// There are also sad-path tests mod outer { pub mod inner { use snafu::prelude::*; #[derive(Debug, Snafu)] #[snafu(visibility(pub(crate)))] pub(crate) enum Error { PubCrate { id: i32, }, #[snafu(visibility(pub(in crate::outer)))] PubInPath { id: i32, }, #[snafu(visibility)] Private { id: i32, }, } } #[test] fn can_set_default_visibility() { let _ = self::inner::PubCrateSnafu { id: 42 }.build(); } #[test] fn can_set_visibility() { let _ = self::inner::PubInPathSnafu { id: 42 }.build(); } } #[test] fn can_set_default_visibility() { let _ = self::outer::inner::PubCrateSnafu { id: 42 }.build(); }