├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── flex-error-demo-full ├── Cargo.toml └── src │ └── main.rs └── flex-error ├── Cargo.toml ├── LICENSE └── src ├── lib.rs ├── macros.rs ├── source.rs ├── tracer.rs └── tracer_impl ├── anyhow.rs ├── eyre.rs ├── mod.rs └── string.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /flex-error/target/ 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.4.4 4 | 5 | - Use `E: Display + Debug + Send + Sync + 'static` trait bound for `ErrorTracer` 6 | for eyre and anyhow tracers. The original `E: std::error::Error + Send + Sync + 'static` 7 | trait bound do not work well when individual crates have `std` features disabled, 8 | but had `flex-error`'s `std` feature enabled somehow. 9 | 10 | ## v0.4.3 11 | 12 | - Add `into_detail` and `into_trace` methods to error types defined by `define_error!`. 13 | - Export local alias to `std::error::Error` so that `std` do not need to be available in 14 | user crate when `define_error!` is called. 15 | 16 | ## v0.4.2 17 | 18 | - Make the `Display` implementation of error types use `Debug` to display error traces 19 | from `eyre` and other error tracers. 20 | 21 | ## v0.4.1 22 | 23 | Breaking changes: 24 | 25 | - The `impl` definition for `std::error::Error` is now generated based on whether 26 | the `std` feature is enabled on the `flex-error` crate, instead of the user 27 | crate that calls `define_error`. 28 | 29 | ## v0.4.0 30 | 31 | Breaking changes: 32 | 33 | - Define main error types in the form of `struct Error(ErrorDetail, Trace)` instead of 34 | the type alias `type Error = ErrorReport`. 35 | 36 | - Remove auto derive of common traits such as `Clone` and `Eq` for the main error type. 37 | Users can instead implement the traits explicitly since it is no longer a type alias. 38 | 39 | - Change the error constructor convention from `error::constructor_error()` to 40 | `error::Error::constructor()`. 41 | 42 | - Allow multiple attributes to be given to main and sub errors in `define_error!`. 43 | 44 | ## v0.3.0 45 | 46 | Breaking changes: 47 | 48 | - Update structure of `ErrorReport` from `ErrorReport { detail, trace }` to `ErrorReport(detail, trace)` 49 | to allow simpler pattern matching. 50 | 51 | - Auto derive common traits such as `Clone` and `Eq` for `ErrorReport`. 52 | 53 | - Allow recursive error source using `Self`. 54 | 55 | ## v0.2.0 56 | 57 | Breaking changes: 58 | 59 | - Update define syntax from `define_error!{ Error; ... }` to `define_error!{ Error { ... } }` 60 | 61 | - Allow custom derive attributes in the form of `define_error!{ #[derive(Debug, ...)] Error { ... } }`. 62 | 63 | ## v0.1.0 64 | 65 | - Initial draft release for flex-error. 66 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ "flex-error", "flex-error-demo-full" ] 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flex-error 2 | 3 | [![Crate][crate-image]][crate-link] 4 | [![Docs][docs-image]][docs-link] 5 | [![Apache 2.0 Licensed][license-image]][license-link] 6 | 7 | `flex-error` is a lightweight Rust library that uses macros and traits to switch between different error tracing implementations and no_std. The library currently supports 3 modes via Cargo feature flags: `eyre_tracer` (default), `anyhow_tracer`, and `string_tracer` (no_std). 8 | 9 | The library separates out several concepts as traits: `ErrorDetail`, `ErrorTrace`, and `ErrorSource`. 10 | - The `ErrorDetail` is responsible to structured metadata information about a specific error. 11 | - The `ErrorTracer` is responsible for tracing error chains and backtraces. 12 | - The `ErrorSource` allows generic conversion of external error types into an ErrorDetail with optional ErrorTrace. 13 | - An application error is of type `ErrorReport`, which holds both the error details and trace. 14 | 15 | With the separation of concerns, `flex-error` allows applications to easily switch between different error reporting implementations, such as `eyre` and `anyhow`, by implementing `ErrorTracer` for the respective reporters. 16 | 17 | `flex-error` defines a `define_error!` macro that define custom `Detail` types and error types as alias to `ErrorReport`. The `DefaultTracer` type is set globally by the feature flag, so that application error types do not have to be over-generalized. The trade off is that it is not possible to use multiple `ErrorTracer` implementations at the same time across different crates that use `flex-error`. 18 | 19 | ## Demo 20 | 21 | The [flex-error-demo-full](./flex-error-demo-full) directory contains a [demo](./flex-error-demo-full/src/main.rs) program that reports error using `eyre`. When run with `RUST_BACKTRACE=1`, it should produce something like follows: 22 | 23 | ```bash 24 | $ RUST_BACKTRACE=1 cargo run flex-error-demo-full 25 | 26 | Error: 27 | 0: error caused by foo: Foo has failed 28 | 1: system error 29 | 2: error1 30 | 31 | Location: 32 | flex-error/flex-error/src/tracer_impl/eyre.rs:25 33 | 34 | ---------------------------------- BACKTRACE ----------------------------------- 35 | ⋮ 5 frames hidden ⋮ 36 | 6: flex_error::tracer_impl::eyre:: for eyre::Report>::new_trace::h471dd777954521dc 37 | at flex-error/flex-error/src/tracer_impl/eyre.rs:25 38 | 7: flex_error::tracer:: for flex_error::source::StdError>::error_details::hba2719390b7121af 39 | at flex-error/flex-error/src/tracer.rs:26 40 | 8: flex_error::report::ErrorReport::trace_from::h39963733bcaf4442 41 | at flex-error/flex-error/src/report.rs:32 42 | 9: flex_error_demo_full::foo::system_error::h9d131e83ad96a9bb 43 | at flex-error/flex-error/src/macros.rs:111 44 | 10: flex_error_demo_full::main::hb0f615a97c840949 45 | at flex-error/flex-error-demo-full/src/main.rs:50 46 | ``` 47 | 48 | ## License 49 | 50 | Copyright © 2021 Informal Systems 51 | 52 | Licensed under the Apache License, Version 2.0 (the "License"); 53 | you may not use the files in this repository except in compliance with the License. 54 | You may obtain a copy of the License at 55 | 56 | https://www.apache.org/licenses/LICENSE-2.0 57 | 58 | Unless required by applicable law or agreed to in writing, software 59 | distributed under the License is distributed on an "AS IS" BASIS, 60 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 61 | See the License for the specific language governing permissions and 62 | limitations under the License. 63 | 64 | [crate-image]: https://img.shields.io/crates/v/flex-error.svg 65 | [crate-link]: https://crates.io/crates/flex-error 66 | [docs-image]: https://docs.rs/flex-error/badge.svg 67 | [docs-link]: https://docs.rs/flex-error/ 68 | [license-image]: https://img.shields.io/badge/license-Apache2.0-blue.svg 69 | [license-link]: https://github.com/informalsystems/flex-error/blob/master/LICENSE 70 | -------------------------------------------------------------------------------- /flex-error-demo-full/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flex-error-demo-full" 3 | version = "0.1.0" 4 | authors = ["Soares Chen "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | thiserror = "1.0.24" 11 | color-eyre = "0.5" 12 | flex-error = { path = "../flex-error" } 13 | -------------------------------------------------------------------------------- /flex-error-demo-full/src/main.rs: -------------------------------------------------------------------------------- 1 | pub mod test { 2 | use flex_error::*; 3 | use thiserror::Error; 4 | #[derive(Debug, Error, Eq, PartialEq, Clone)] 5 | #[error("external")] 6 | pub struct ExternalError; 7 | 8 | define_error! { 9 | /// This is documentation for foo error 10 | 11 | #[derive(Debug, Clone)] 12 | #[derive(Eq, PartialEq)] 13 | FooError { 14 | /// This is documentation for bar error 15 | Bar 16 | { code: u32 } 17 | [ DisplayError ] 18 | | e | { format_args!("Bar error with code {}", e.code) }, 19 | 20 | /// This is documentation for baz error 21 | #[derive(Ord, PartialOrd)] 22 | Baz 23 | { extra: String } 24 | | e | { format_args!("General Baz error with extra detail: {}", e.extra) }, 25 | } 26 | } 27 | } 28 | 29 | pub mod foo { 30 | use flex_error::*; 31 | 32 | use thiserror::Error; 33 | 34 | #[derive(Debug, Clone, Eq, PartialEq)] 35 | pub struct PrimitiveError; 36 | 37 | #[derive(Debug, Clone, Error, PartialEq)] 38 | pub enum SystemError { 39 | #[error("error1")] 40 | Error1, 41 | #[error("error2")] 42 | Error2, 43 | } 44 | 45 | define_error! { 46 | #[derive(Debug, Clone, PartialEq, Eq)] 47 | FooError { 48 | Foo 49 | { foo_val: String } 50 | [ DetailOnly ] 51 | | err | { format_args!("foo error: {}", err.foo_val) }, 52 | System 53 | [ TraceError ] 54 | | _ | { "system error" }, 55 | Unknown 56 | | _ | { "unknown error" }, 57 | 58 | Nested 59 | [ Self ] 60 | | _ | { format_args!("nested foo error") } 61 | } 62 | } 63 | } 64 | 65 | pub mod bar { 66 | use super::foo; 67 | use flex_error::*; 68 | 69 | define_error! { 70 | #[derive(Debug, Clone, PartialEq, Eq)] 71 | BarError { 72 | Bar 73 | { bar: String } 74 | | err | { format_args!("bar error {}", err.bar) }, 75 | Foo 76 | { detail: String } 77 | [ foo::FooError ] 78 | | err | { format_args!("error caused by foo: {}", err.detail) }, 79 | } 80 | } 81 | } 82 | 83 | fn main() -> Result<(), bar::BarError> { 84 | color_eyre::install().unwrap(); 85 | 86 | let err1 = foo::FooError::system(foo::SystemError::Error1); 87 | let err2 = foo::FooError::nested(err1); 88 | let err3 = bar::BarError::foo("Foo has failed".into(), err2); 89 | 90 | println!("error: {:?}", err3); 91 | 92 | // Err(err3) 93 | Ok(()) 94 | } 95 | -------------------------------------------------------------------------------- /flex-error/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flex-error" 3 | version = "0.4.4" 4 | edition = "2018" 5 | license = "Apache-2.0" 6 | repository = "https://github.com/informalsystems/flex-error" 7 | authors = ["Informal Systems "] 8 | description = "Flexible error definitions using macros and traits" 9 | readme = "../README.md" 10 | 11 | [dependencies] 12 | paste = "1.0.5" 13 | eyre = { version = "0.6.5", optional = true } 14 | anyhow = { version = "1.0.40", optional = true } 15 | 16 | [features] 17 | default = ["full"] 18 | std = [] 19 | eyre_tracer = ["eyre", "std"] 20 | anyhow_tracer = ["anyhow", "std"] 21 | full = ["std", "eyre_tracer", "anyhow_tracer"] 22 | -------------------------------------------------------------------------------- /flex-error/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2020 Informal Systems 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /flex-error/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | /*! 4 | `flex-error` is a lightweight Rust library that uses macros and traits 5 | to switch between different error tracing implementations and no_std. The library currently supports 3 modes via Cargo feature flags: `eyre_tracer` (default), `anyhow_tracer`, and `string_tracer` (no_std). 6 | 7 | The library separates out several concepts as traits: 8 | `ErrorDetail`, [`ErrorTracer`], and [`ErrorSource`]. 9 | 10 | - `ErrorDetail` is responsible to structured metadata information about a specific error. 11 | 12 | - `ErrorTracer` is responsible for tracing error chains and backtraces. 13 | 14 | - `ErrorSource` allows generic conversion of external error types into an ErrorDetail with optional ErrorTrace. 15 | 16 | With the separation of concerns, `flex-error` allows applications to easily 17 | switch between different error reporting implementations, 18 | such as [`eyre`] and [`anyhow`], by implementing 19 | [`ErrorTracer`] for the respective reporters. 20 | 21 | `flex-error` defines a [`define_error!`] macro that define custom `Detail` 22 | types and error types implementing `ErrorSource`. 23 | The [`DefaultTracer`] type is set globally by the feature flag, so that 24 | application error types do not have to be over-generalized. 25 | The trade off is that it is not possible to use multiple 26 | [`ErrorTracer`] implementations at the same time across different crates that 27 | use `flex-error`. 28 | 29 | !*/ 30 | 31 | #[cfg(feature = "std")] 32 | extern crate std; 33 | 34 | pub extern crate alloc; 35 | 36 | #[cfg(feature = "std")] 37 | pub use std::error::Error as StdError; 38 | 39 | pub mod macros; 40 | mod source; 41 | mod tracer; 42 | pub mod tracer_impl; 43 | 44 | pub use source::*; 45 | pub use tracer::*; 46 | 47 | /// The `DefaultTracer` type alias is used when defining error types 48 | /// using [`define_error!`]. With the default Cargo features, or when 49 | /// the `eyre_tracer` feature is set, this is configured to use the 50 | /// [EyreTracer](tracer_impl::eyre::EyreTracer). Otherwise, it will 51 | /// be set to [AnyhowTracer](tracer_impl::anyhow::AnyhowTracer) if 52 | /// the `anyhow_tracer` feature is set. If neither `eyre_tracer` 53 | /// nor `anyhow_tracer` is set, then `DefaultTracer` is set to 54 | /// [StringTracer](tracer_impl::string::StringTracer). 55 | /// 56 | /// We hard code globally the default error tracer to be used in 57 | /// [`define_error!`], to avoid making the error types overly generic. 58 | 59 | // If `eyre_tracer` feature is active, it is the default error tracer 60 | #[cfg(feature = "eyre_tracer")] 61 | pub type DefaultTracer = tracer_impl::eyre::EyreTracer; 62 | 63 | // Otherwise, if `eyre_tracer` feature is active, it is the default error tracer 64 | #[cfg(all(feature = "anyhow_tracer", not(feature = "eyre_tracer")))] 65 | pub type DefaultTracer = tracer_impl::anyhow::AnyhowTracer; 66 | 67 | // Otherwise, if `string_tracer` feature is active, it is the default error tracer 68 | #[cfg(all(not(feature = "eyre_tracer"), not(feature = "anyhow_tracer")))] 69 | pub type DefaultTracer = tracer_impl::string::StringTracer; 70 | -------------------------------------------------------------------------------- /flex-error/src/macros.rs: -------------------------------------------------------------------------------- 1 | pub use paste::paste; 2 | 3 | /** 4 | `define_error!` is the main macro that implements a mini DSL to 5 | define error types using `flex-error`. The DSL syntax 6 | is as follows: 7 | 8 | ```ignore 9 | define_error! { 10 | ErrorName { 11 | SubErrorWithFieldsAndErrorSource 12 | { field1: Type1, field2: Type2, ... } 13 | [ ErrorSource ] 14 | | e | { format_args!( 15 | "format error message with field1: {}, field2: {}, source: {}", 16 | e.field1, e.field2, e.source) 17 | }, 18 | SubErrorWithFieldsOnly 19 | { field1: Type1, field2: Type2, ... } 20 | | e | { format_args!( 21 | "format error message with field1: {}, field2: {}", 22 | e.field1, e.field2) 23 | }, 24 | SubErrorWithSourceOnly 25 | [ ErrorSource ] 26 | | e | { format_args!( 27 | "format error message with source: {}", 28 | e.source) 29 | }, 30 | SubError 31 | | e | { format_args!( 32 | "only suberror message") 33 | }, 34 | } 35 | } 36 | ``` 37 | 38 | ## Macro Expansion 39 | 40 | Behind the scene, for an error named `MyError`, `define_error!` 41 | does the following: 42 | 43 | - Define a struct in the form 44 | 45 | ```ignore 46 | pub struct MyError(pub MyErrorDetail, pub flex_error::DefaultTracer) 47 | ``` 48 | 49 | - Define an enum in the form 50 | 51 | ```ignore 52 | pub enum MyError { ... } 53 | ``` 54 | 55 | For each suberror named `MySubError`, does the following: 56 | 57 | - Define a variant in `MyError` in the form 58 | ```ignore 59 | pub enum MyError { ..., MySubError(MySubErrorSubdetail) ,... } 60 | ``` 61 | 62 | - Implement [`core::fmt::Debug`] and [`core::fmt::Display`] 63 | for `MyError`. 64 | 65 | - If the `"std"` feature is enabled on the `flex-error` crate, 66 | it will generate an `impl` block for [`std::error::Error`]. 67 | 68 | - Implement [`ErrorSource`](crate::ErrorSource) 69 | for `MyError`, with `MyErrorDetail` being the `Detail` type, 70 | and `MyError` being the `Source` type. 71 | 72 | - Implement the following helper methods in `impl MyError {...}`: 73 | 74 | - `pub fn detail(&self) -> &MyErrorDetail` 75 | 76 | - `pub fn trace(&self) -> flex_error::DefaultTracer` 77 | 78 | - `pub fn add_trace(self, e: &E) -> MyError` 79 | 80 | - Define a struct in the form 81 | 82 | ```ignore 83 | pub struct MySubErrorSubdetail { ... } 84 | ``` 85 | 86 | - For each field named `my_field: MyFieldType`, define a struct 87 | field in the form 88 | 89 | ```ignore 90 | struct MySubErrorSubdetail { ..., pub my_field: MyFieldType, ... } 91 | ``` 92 | 93 | - If the sub-error has an error source `MySource` implementing 94 | [`ErrorSource`](crate::ErrorSource), define a `source` field 95 | in the form 96 | 97 | ```ignore 98 | struct MySubErrorSubdetail { ..., pub source: MySource::Detail } 99 | ``` 100 | 101 | - Implement [`core::fmt::Display`] in the form 102 | 103 | ```ignore 104 | impl Display for MySubErrorSubdetail { ... } 105 | ``` 106 | 107 | - Define a snake-cased error constructor method in `MyError` in the form 108 | 109 | ```ignore 110 | impl MyError { pub fn my_sub_error(...) -> MyError { ... } } 111 | ``` 112 | 113 | - For each field named `my_field: MyFieldType`, define a 114 | function argument in the form 115 | 116 | ```ignore 117 | fn my_sub_error(..., my_field: MyFieldType, ...) 118 | ``` 119 | 120 | - If the sub-error has an error source `MySource` implementing 121 | [`ErrorSource`](crate::ErrorSource), define a `source` field in the form 122 | 123 | ```ignore 124 | fn my_sub_error(..., source: MySource::Detail) 125 | ``` 126 | 127 | ## Formatter 128 | 129 | For each sub-error definition, a formatter needs to be provided using the 130 | closure syntax. For example, the following definition: 131 | 132 | 133 | ```ignore 134 | MyError { 135 | MySubError 136 | { code: u32 } 137 | [ MySource ] 138 | | e | { format_args!("error with code {}", e.code) }, 139 | ... 140 | } 141 | ``` 142 | 143 | will include the following expansion: 144 | 145 | ``` 146 | impl ::core::fmt::Display for MySubErrorSubdetail { 147 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 148 | let e = self; 149 | write!(f, "{}", format_args!("error with code {}", e.code)) 150 | } 151 | } 152 | ``` 153 | 154 | Note that there is no need to manually display the error source, as the 155 | source is already automatically traced by the error tracer. 156 | 157 | If a sub-error do not have any field, we can write a simpler form of the 158 | formatter like: 159 | 160 | ```ignore 161 | MyError { 162 | MySubError 163 | | _ | { "something went wrong" }, 164 | ... 165 | } 166 | ``` 167 | 168 | ## Example Definition 169 | 170 | We can demonstrate the macro expansion of `define_error!` with the following example: 171 | 172 | ```ignore 173 | // An external error type implementing Display 174 | use external_crate::ExternalError; 175 | 176 | define_error! { 177 | FooError { 178 | Bar 179 | { code: u32 } 180 | [ DisplayError ] 181 | | e | { format_args!("Bar error with code {}", e.code) }, 182 | Baz 183 | { extra: String } 184 | | e | { format_args!("General Baz error with extra detail: {}", e.extra) } 185 | } 186 | } 187 | ``` 188 | 189 | The above code will be expanded into something like follows: 190 | 191 | ```ignore 192 | pub struct FooError(pub FooErrorDetail, pub flex_error::DefaultTracer); 193 | 194 | #[derive(Debug)] 195 | pub enum FooErrorDetail { 196 | Bar(BarSubdetail), 197 | Baz(BazSubdetail), 198 | } 199 | 200 | #[derive(Debug)] 201 | pub struct BarSubdetail { 202 | pub code: u32, 203 | pub source: ExternalError 204 | } 205 | 206 | #[derive(Debug)] 207 | pub struct BazSubdetail { 208 | pub extra: String 209 | } 210 | 211 | fn bar_error(code: u32, source: ExternalError) -> FooError { ... } 212 | fn baz_error(extra: String) -> FooError { ... } 213 | 214 | impl ::core::fmt::Display for BarSubdetail { 215 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 216 | let e = self; 217 | write!(f, "{}", format_args!("Bar error with code {}", e.code)) 218 | } 219 | } 220 | 221 | impl ::core::fmt::Display for BazSubdetail { 222 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 223 | let e = self; 224 | write!(f, "{}", format_args!("General Baz error with extra detail: {}", e.code)) 225 | } 226 | } 227 | 228 | impl Display for FooErrorDetail { ... } 229 | ``` 230 | 231 | For the detailed macro expansion, you can use [cargo-expand](https://github.com/dtolnay/cargo-expand) 232 | to expand the Rust module that uses `define_error!` to see how the error definition 233 | gets expanded. 234 | 235 | Since `FooError` implements [`ErrorSource`](crate::ErrorSource), it can be used 236 | directly as a error source in other error definitions. For example: 237 | 238 | ```ignore 239 | define_error! { 240 | QuuxError { 241 | Foo 242 | { action: String } 243 | [ FooError ] 244 | | e | { format_args!("error arised from Foo when performing action {}", e.action) }, 245 | ... 246 | } 247 | } 248 | ``` 249 | 250 | Would be expanded to include the following definitions: 251 | 252 | ```ignore 253 | pub struct FooSubdetail { 254 | pub action: String, 255 | pub source: FooErrorDetail 256 | } 257 | 258 | pub fn foo_error(action: String, source: FooError) { ... } 259 | ``` 260 | 261 | In the formatter for `QuuxErrorDetail::Foo`, we can also see that it does not 262 | need to include the error string from `FooError`. This is because the error 263 | tracer already takes care of the source error trace, so the full trace is 264 | automatically tracked inside `foo_error`. The outer error only need to 265 | add additional detail about what caused the source error to be raised. 266 | 267 | ## Attributes 268 | 269 | `define_error!` supports adding attributes to the generated error types. 270 | 271 | ### First `doc` Attribute 272 | 273 | If the first attribute is a [`doc`](https://doc.rust-lang.org/rustdoc/the-doc-attribute.html) 274 | attribute, it is attached to the main error struct. For example: 275 | 276 | ```ignore 277 | define_error! { 278 | /// Documentation for MyError 279 | MyError { ... } 280 | } 281 | ``` 282 | 283 | will include the following expansion: 284 | 285 | ```ignore 286 | #[doc = "Documentation for MyError"] 287 | pub struct MyError(pub MyErrorDetail, pub flex_error::DefaultTracer); 288 | ``` 289 | 290 | ## Common Attributes 291 | 292 | For all other attributes defined on the main error type, 293 | they are defined on the _error detail_ and _sub-errors type. For example: 294 | 295 | 296 | ```ignore 297 | define_error! { 298 | #[derive(Debug, Clone)] 299 | MyError { 300 | Foo 301 | { ... } 302 | | _ | { "foo error" }, 303 | 304 | Bar 305 | { ... } 306 | | _ | { "bar error" }, 307 | } 308 | } 309 | ``` 310 | 311 | will include the following expansion: 312 | 313 | ```ignore 314 | pub struct MyError(pub MyErrorDetail, pub flex_error::DefaultTracer); 315 | 316 | #[derive(Debug, Clone)] 317 | pub enum MyErrorDetail { ... } 318 | 319 | #[derive(Debug, Clone)] 320 | pub struct FooSubdetail { ... } 321 | 322 | #[derive(Debug, Clone)] 323 | pub struct BarSubdetail { ... } 324 | ``` 325 | 326 | Note that we do not add the `derive` attribute to the main error struct 327 | `MyError`. This is because the [`DefaultTracer`](crate::DefaultTracer) 328 | type is opaque, and auto deriving traits like `Clone` on it is 329 | generally not supported. 330 | 331 | If you need the main error type to implement certain traits, 332 | you can instead define your own custom `impl` definition for it. 333 | 334 | ## Sub Attributes 335 | 336 | We can also define custom attributes for only the sub-error. 337 | In that case, the attribute is given to the sub-detail type. 338 | For example: 339 | 340 | ```ignore 341 | define_error! { 342 | MyError { 343 | /// Documentation for Foo 344 | #[derive(Clone)] 345 | Foo 346 | { ... } 347 | | _ | { "foo error" }, 348 | 349 | ... 350 | } 351 | } 352 | ``` 353 | 354 | will include the following expansion: 355 | 356 | ```ignore 357 | #[doc = "Documentation for Foo"] 358 | #[derive(Clone)] 359 | pub struct FooSubdetail { ... } 360 | ``` 361 | 362 | Note that if no attribute is given to the main error, 363 | the `#[derive(Debug)]` trait is added by default. 364 | So there is no need to derive it again in the 365 | sub-errors. 366 | 367 | **/ 368 | 369 | #[macro_export] 370 | macro_rules! define_error { 371 | ( $name:ident 372 | { $($suberrors:tt)* } 373 | ) => { 374 | $crate::define_error_with_tracer![ 375 | @tracer( $crate::DefaultTracer ), 376 | @attr[ derive(Debug) ], 377 | @name( $name ), 378 | @suberrors{ $($suberrors)* } 379 | ]; 380 | }; 381 | ( #[doc = $doc:literal] $( #[$attr:meta] )* 382 | $name:ident 383 | { $($suberrors:tt)* } 384 | ) => { 385 | $crate::define_error_with_tracer![ 386 | @tracer( $crate::DefaultTracer ), 387 | @doc( $doc ), 388 | @attr[ $( $attr ),* ], 389 | @name( $name ), 390 | @suberrors{ $($suberrors)* } 391 | ]; 392 | }; 393 | ( $( #[$attr:meta] )* 394 | $name:ident 395 | { $($suberrors:tt)* } 396 | ) => { 397 | $crate::define_error_with_tracer![ 398 | @tracer( $crate::DefaultTracer ), 399 | @attr[ $( $attr ),* ], 400 | @name( $name ), 401 | @suberrors{ $($suberrors)* } 402 | ]; 403 | }; 404 | ( @with_tracer[ $tracer:ty ] 405 | $name:ident, 406 | { $($suberrors:tt)* } 407 | ) => { 408 | $crate::define_error_with_tracer![ 409 | @tracer( $tracer ), 410 | @attr[ derive(Debug) ], 411 | @name( $name ), 412 | @suberrors{ $($suberrors)* } 413 | ]; 414 | }; 415 | ( @with_tracer[ $tracer:ty ] 416 | $( #[$attr:meta] )* 417 | $name:ident, 418 | @suberrors{ $($suberrors:tt)* } 419 | ) => { 420 | $crate::define_error_with_tracer![ 421 | @tracer( $tracer ), 422 | @attr[ $( $attr ),* ], 423 | @name( $name ), 424 | @suberrors{ $($suberrors)* } 425 | ]; 426 | }; 427 | } 428 | 429 | /// This macro allows error types to be defined with custom error tracer types 430 | /// other than [`DefaultTracer`](crate::DefaultTracer). Behind the scene, 431 | /// a macro call to `define_error!{ ... } really expands to 432 | /// `define_error_with_tracer!{ flex_error::DefaultTracer; ... }` 433 | #[macro_export] 434 | #[doc(hidden)] 435 | macro_rules! define_error_with_tracer { 436 | ( @tracer( $tracer:ty ), 437 | $( @doc($doc:literal), )? 438 | @attr[ $( $attr:meta ),* ], 439 | @name($name:ident), 440 | @suberrors{ $($suberrors:tt)* } $(,)? 441 | ) => { 442 | $crate::macros::paste![ 443 | $crate::define_main_error!( 444 | @tracer( $tracer ), 445 | $( @doc( $doc ), )? 446 | @name( $name ) 447 | ); 448 | 449 | $crate::define_error_detail!( 450 | @attr[ $( $attr ),* ] , 451 | @name( $name ), 452 | @suberrors{ $($suberrors)* }); 453 | 454 | $crate::define_suberrors! { 455 | @tracer($tracer), 456 | @attr[ $( $attr ),* ], 457 | @name($name), 458 | { $( $suberrors )* } 459 | } 460 | ]; 461 | }; 462 | } 463 | 464 | #[macro_export] 465 | #[doc(hidden)] 466 | macro_rules! define_main_error { 467 | ( @tracer( $tracer:ty ), 468 | $( @doc( $doc:literal ), )? 469 | @name( $name:ident ) $(,)? 470 | ) => { 471 | $crate::macros::paste![ 472 | $crate::define_main_error_struct!( 473 | @tracer( $tracer ), 474 | $( @doc($doc), )? 475 | @name( $name ) 476 | ); 477 | 478 | impl $crate::ErrorSource<$tracer> for $name { 479 | type Source = Self; 480 | type Detail = [< $name Detail >]; 481 | 482 | fn error_details($name(detail, trace): Self) -> ([< $name Detail >], Option<$tracer>) { 483 | (detail, Some(trace)) 484 | } 485 | } 486 | 487 | impl ::core::fmt::Debug for $name 488 | where 489 | $tracer: ::core::fmt::Debug, 490 | { 491 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 492 | ::core::fmt::Debug::fmt(self.trace(), f) 493 | } 494 | } 495 | 496 | impl ::core::fmt::Display for $name 497 | where 498 | $tracer: ::core::fmt::Debug, 499 | { 500 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) 501 | -> ::core::fmt::Result 502 | { 503 | // Always use `Debug` to format error traces, as eyre do not 504 | // include full back trace information in normal Display mode. 505 | ::core::fmt::Debug::fmt(self.trace(), f) 506 | } 507 | } 508 | 509 | $crate::define_std_err_impl!( 510 | @tracer( $tracer ), 511 | @name( $name ) 512 | ); 513 | 514 | impl $name { 515 | pub fn detail(&self) -> &[< $name Detail >] { 516 | &self.0 517 | } 518 | 519 | 520 | pub fn into_detail(self) -> [< $name Detail >] { 521 | self.0 522 | } 523 | 524 | pub fn trace(&self) -> &$tracer { 525 | &self.1 526 | } 527 | 528 | pub fn into_trace(self) -> $tracer { 529 | self.1 530 | } 531 | 532 | pub fn add_trace(self, message: &E) -> Self 533 | where 534 | $tracer: $crate::ErrorMessageTracer, 535 | { 536 | let detail = self.0; 537 | let trace = $crate::ErrorMessageTracer::add_message(self.1, message); 538 | $name(detail, trace) 539 | } 540 | 541 | pub fn trace_from(source: E::Source, cont: Cont) -> Self 542 | where 543 | E: $crate::ErrorSource<$tracer>, 544 | $tracer: $crate::ErrorMessageTracer, 545 | Cont: FnOnce(E::Detail) -> [< $name Detail >], 546 | { 547 | let (detail1, m_trace1) = E::error_details(source); 548 | let detail2 = cont(detail1); 549 | match m_trace1 { 550 | Some(trace1) => { 551 | let trace2 = $crate::ErrorMessageTracer::add_message(trace1, &detail2); 552 | $name(detail2, trace2) 553 | } 554 | None => { 555 | let trace2 = $crate::ErrorMessageTracer::new_message(&detail2); 556 | $name(detail2, trace2) 557 | } 558 | } 559 | } 560 | } 561 | ]; 562 | } 563 | } 564 | 565 | // define the impl for `std::error::Error` only in std mode 566 | #[cfg(feature = "std")] 567 | #[macro_export] 568 | #[doc(hidden)] 569 | macro_rules! define_std_err_impl { 570 | ( @tracer( $tracer:ty ), 571 | @name( $name:ident ) $(,)? 572 | ) => { 573 | $crate::macros::paste![ 574 | impl $crate::StdError for $name 575 | where 576 | [< $name Detail >]: ::core::fmt::Display, 577 | $tracer: ::core::fmt::Debug + ::core::fmt::Display, 578 | $tracer: $crate::ErrorMessageTracer, 579 | { 580 | fn source(&self) -> ::core::option::Option<&(dyn $crate::StdError + 'static)> { 581 | $crate::ErrorMessageTracer::as_error(self.trace()) 582 | } 583 | } 584 | ]; 585 | } 586 | } 587 | 588 | // do not define the impl for `std::error::Error` when in no_std mode 589 | #[cfg(not(feature = "std"))] 590 | #[macro_export] 591 | #[doc(hidden)] 592 | macro_rules! define_std_err_impl { 593 | ( @tracer( $tracer:ty ), 594 | @name( $name:ident ) $(,)? 595 | ) => {}; 596 | } 597 | 598 | #[macro_export] 599 | #[doc(hidden)] 600 | macro_rules! define_main_error_struct { 601 | ( @tracer( $tracer:ty ), 602 | $( @doc( $doc:literal ), )? 603 | @name( $name:ident ) $(,)? 604 | ) => { 605 | $crate::macros::paste![ 606 | $( #[doc = $doc] )? 607 | pub struct $name (pub [< $name Detail >], pub $tracer); 608 | ]; 609 | } 610 | } 611 | 612 | #[macro_export] 613 | #[doc(hidden)] 614 | macro_rules! with_suberrors { 615 | ( @cont($cont:path), 616 | @ctx[ $($args:tt)* ], 617 | @suberrors{ 618 | $( 619 | $( #[$sub_attr:meta] )* 620 | $suberror:ident 621 | $( { $( $arg_name:ident : $arg_type:ty ),* $(,)? } )? 622 | $( [ $source:ty ] )? 623 | | $formatter_arg:pat | $formatter:expr 624 | ),* $(,)? 625 | } $(,)? 626 | ) => { 627 | $cont!( @ctx[ $( $args )* ], @suberrors{ $( $suberror ),* } ); 628 | } 629 | } 630 | 631 | #[macro_export] 632 | #[doc(hidden)] 633 | macro_rules! define_error_detail { 634 | ( @attr[ $( $attr:meta ),* ], 635 | @name( $name:ident ), 636 | @suberrors{ $($suberrors:tt)* } $(,)? 637 | ) => { 638 | $crate::with_suberrors!( 639 | @cont($crate::define_error_detail_enum), 640 | @ctx[ 641 | @attr[ $( $attr ),* ], 642 | @name($name) 643 | ], 644 | @suberrors{ $( $suberrors )* } 645 | ); 646 | 647 | $crate::with_suberrors!( 648 | @cont($crate::define_error_detail_display), 649 | @ctx[ 650 | @name($name) 651 | ], 652 | @suberrors{ $( $suberrors )* } 653 | ); 654 | } 655 | } 656 | 657 | #[macro_export] 658 | #[doc(hidden)] 659 | macro_rules! define_error_detail_enum { 660 | ( @ctx[ 661 | @attr[ $( $attr:meta ),* ], 662 | @name($name:ident) 663 | ], 664 | @suberrors{ $( $suberror:ident ),* } $(,)? 665 | ) => { 666 | $crate::macros::paste! [ 667 | $( #[$attr] )* 668 | pub enum [< $name Detail >] { 669 | $( 670 | $suberror ( 671 | [< $suberror Subdetail >] 672 | ) 673 | ),* 674 | } 675 | ]; 676 | } 677 | } 678 | 679 | #[macro_export] 680 | #[doc(hidden)] 681 | macro_rules! define_error_detail_display { 682 | ( @ctx[ 683 | @name( $name:ident ) 684 | ], 685 | @suberrors{ $( $suberror:ident ),* } $(,)? 686 | ) => { 687 | $crate::macros::paste! [ 688 | impl ::core::fmt::Display for [< $name Detail >] { 689 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) 690 | -> ::core::fmt::Result 691 | { 692 | match self { 693 | $( 694 | Self::$suberror( suberror ) => { 695 | ::core::write!( f, "{}", suberror ) 696 | } 697 | ),* 698 | } 699 | } 700 | } 701 | ]; 702 | } 703 | } 704 | 705 | #[macro_export] 706 | #[doc(hidden)] 707 | macro_rules! define_suberrors { 708 | ( @tracer($tracer:ty), 709 | @attr[ $( $attr:meta ),* ], 710 | @name($name:ident), 711 | {} $(,)? 712 | ) => { }; 713 | ( @tracer($tracer:ty), 714 | @attr[ $( $attr:meta ),* ], 715 | @name($name:ident), 716 | { 717 | $( #[$sub_attr:meta] )* 718 | $suberror:ident 719 | $( { $( $arg_name:ident : $arg_type:ty ),* $(,)? } )? 720 | $( [ $source:ty ] )? 721 | | $formatter_arg:pat | $formatter:expr 722 | 723 | $( , $($tail:tt)* )? 724 | } 725 | ) => { 726 | $crate::macros::paste![ 727 | $crate::define_suberror! { 728 | @tracer( $tracer ), 729 | @attr[ $( $attr ),* ], 730 | @sub_attr[ $( $sub_attr ),* ], 731 | @name( $name ), 732 | @suberror( $suberror ), 733 | @args( $( $( $arg_name : $arg_type ),* )? ) 734 | $( @source[ $source ] )? 735 | } 736 | 737 | impl ::core::fmt::Display for [< $suberror Subdetail >] { 738 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 739 | use ::core::format_args; 740 | let $formatter_arg = self; 741 | ::core::write!(f, "{}", $formatter) 742 | } 743 | } 744 | 745 | impl $name { 746 | $crate::define_error_constructor! { 747 | @tracer( $tracer ), 748 | @name( $name ), 749 | @suberror( $suberror ), 750 | @args( $( $( $arg_name : $arg_type ),* )? ) 751 | $( @source[ $source ] )? 752 | } 753 | } 754 | ]; 755 | 756 | $crate::define_suberrors! { 757 | @tracer($tracer), 758 | @attr[ $( $attr ),* ], 759 | @name($name), 760 | { $( $( $tail )* )? } 761 | } 762 | }; 763 | } 764 | 765 | /// Internal macro used to define suberror structs 766 | #[macro_export] 767 | #[doc(hidden)] 768 | macro_rules! define_suberror { 769 | ( @tracer( $tracer:ty ), 770 | @attr[ $( $attr:meta ),* ], 771 | @sub_attr[ $( $sub_attr:meta ),* ], 772 | @name( $name:ident ), 773 | @suberror( $suberror:ident ), 774 | @args( $( $arg_name:ident: $arg_type:ty ),* ) 775 | @source[ Self ] 776 | ) => { 777 | $crate::macros::paste! [ 778 | $( #[ $attr ] )* 779 | $( #[ $sub_attr ] )* 780 | pub struct [< $suberror Subdetail >] { 781 | $( pub $arg_name: $arg_type, )* 782 | pub source: $crate::alloc::boxed::Box< [< $name Detail >] > 783 | } 784 | ]; 785 | }; 786 | ( @tracer( $tracer:ty ), 787 | @attr[ $( $attr:meta ),* ], 788 | @sub_attr[ $( $sub_attr:meta ),* ], 789 | @name( $name:ident ), 790 | @suberror( $suberror:ident ), 791 | @args( $( $arg_name:ident: $arg_type:ty ),* ) 792 | $( @source[ $source:ty ] )? 793 | ) => { 794 | $crate::macros::paste! [ 795 | $( #[ $attr ] )* 796 | $( #[ $sub_attr ] )* 797 | pub struct [< $suberror Subdetail >] { 798 | $( pub $arg_name: $arg_type, )* 799 | $( pub source: $crate::AsErrorDetail<$source, $tracer> )? 800 | } 801 | ]; 802 | }; 803 | } 804 | 805 | /// Internal macro used to define suberror constructor functions 806 | #[macro_export] 807 | #[doc(hidden)] 808 | macro_rules! define_error_constructor { 809 | ( @tracer( $tracer:ty ), 810 | @name( $name:ident ), 811 | @suberror( $suberror:ident ), 812 | @args( $( $arg_name:ident: $arg_type:ty ),* ) $(,)? 813 | ) => { 814 | $crate::macros::paste! [ 815 | pub fn [< $suberror:snake >]( 816 | $( $arg_name: $arg_type, )* 817 | ) -> $name 818 | { 819 | let detail = [< $name Detail >]::$suberror([< $suberror Subdetail >] { 820 | $( $arg_name, )* 821 | }); 822 | 823 | let trace = < $tracer as $crate::ErrorMessageTracer >::new_message(&detail); 824 | $name(detail, trace) 825 | } 826 | ]; 827 | }; 828 | ( @tracer( $tracer:ty ), 829 | @name( $name:ident ), 830 | @suberror( $suberror:ident ), 831 | @args( $( $arg_name:ident: $arg_type:ty ),* ) 832 | @source[ Self ] 833 | ) => { 834 | $crate::macros::paste! [ 835 | pub fn [< $suberror:snake >]( 836 | $( $arg_name: $arg_type, )* 837 | source: $name 838 | ) -> $name 839 | { 840 | let detail = [< $name Detail >]::$suberror([< $suberror Subdetail >] { 841 | $( $arg_name, )* 842 | source: Box::new(source.0), 843 | }); 844 | 845 | let trace = source.1.add_message(&detail); 846 | 847 | $name(detail, trace) 848 | } 849 | ]; 850 | }; 851 | ( @tracer( $tracer:ty ), 852 | @name( $name:ident ), 853 | @suberror( $suberror:ident ), 854 | @args( $( $arg_name:ident: $arg_type:ty ),* ) 855 | @source[ $source:ty ] 856 | ) => { 857 | $crate::macros::paste! [ 858 | pub fn [< $suberror:snake >]( 859 | $( $arg_name: $arg_type, )* 860 | source: $crate::AsErrorSource< $source, $tracer > 861 | ) -> $name 862 | { 863 | $name::trace_from::<$source, _>(source, 864 | | source_detail | { 865 | [< $name Detail >]::$suberror([< $suberror Subdetail >] { 866 | $( $arg_name, )* 867 | source: source_detail, 868 | }) 869 | }) 870 | } 871 | ]; 872 | }; 873 | } 874 | -------------------------------------------------------------------------------- /flex-error/src/source.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Display; 2 | use core::marker::PhantomData; 3 | 4 | use crate::tracer::{ErrorMessageTracer, ErrorTracer}; 5 | 6 | /** 7 | A type implementing `ErrorSource` is a proxy type that provides the 8 | capability of extracting from an error source of type `Self::Source`, 9 | returning error detail of type `Self::Detail`, and an optional error 10 | tracer of type `Tracer`. 11 | 12 | The proxy type `Self` is not used anywhere. We separate out `Self` 13 | and `Self::Source` so that there can be different generic implementations 14 | of error sources, such as for all `E: Display` or for all `E: Error`. 15 | 16 | There are currently 4 types of error sources: 17 | - [`NoSource`] - Indicating the lack of any error source 18 | - [`DisplayError`] - An error source that implements [`Display`](std::fmt::Display) 19 | to be used for tracing, and also be stored as detail. 20 | - [`DisplayOnly`] - An error source that implements [`Display`](std::fmt::Display) 21 | to be used for tracing, and discarded instead of being stored as detail. 22 | - [`DetailOnly`] - An error source that is used as detail and do not contain any error trace. 23 | - [`TraceError`] - An error source that implements [`Error`](std::error::Error) 24 | and used only for tracing. 25 | - [`TraceClone`] - An error source that implements [`Error`](std::error::Error) and 26 | have a cloned copy as detail. 27 | **/ 28 | 29 | pub trait ErrorSource { 30 | /// The type of the error source. 31 | type Source; 32 | 33 | /// The type of the error detail that can be extracted from the error source 34 | type Detail; 35 | 36 | /// Extracts the error details out from the error source, together with 37 | /// an optional error trace. 38 | fn error_details(source: Self::Source) -> (Self::Detail, Option); 39 | } 40 | 41 | /// Type alias to `>::Detail` 42 | pub type AsErrorDetail = >::Detail; 43 | 44 | /// Type alias to `>::Source` 45 | pub type AsErrorSource = >::Source; 46 | 47 | /// An [`ErrorSource`] that can be used to represent to lack of any error source. 48 | /// Both its `Source` and `Detail` types are `()`. This can be used for primitive errors 49 | /// that are not caused by any error source. 50 | /// 51 | /// In practice, it is also possible to omit specifying any error source inside 52 | /// [`define_error!`](crate::define_error), which has similar effect as using 53 | /// `NoSource` but with the `source` field omitted entirely. 54 | pub struct NoSource; 55 | 56 | /// An [`ErrorSource`] that implements [`Display`](std::fmt::Display) and 57 | /// can be traced by error tracers implementing [`ErrorMessageTracer`](crate::tracer::ErrorMessageTracer). 58 | /// 59 | /// Both its `Source` and `Detail` types are `E`. When extraced, it also provides 60 | /// an error trace that is traced from its string representation. 61 | pub struct DisplayError(PhantomData); 62 | 63 | pub struct DisplayOnly(PhantomData); 64 | 65 | /// An [`ErrorSource`] that should implement [`Error`](std::error::Error) and 66 | /// other constraints such as `Send`, `Sync`, `'static`, so that it can be traced 67 | /// by error tracing libraries such as [`eyre`] and [`anyhow`]. Because these libraries 68 | /// take ownership of the source error object, the error cannot be extracted as detail 69 | /// at the same time. 70 | pub struct TraceError(PhantomData); 71 | 72 | pub struct TraceClone(PhantomData); 73 | 74 | /// An [`ErrorSource`] that contains only the error trace with no detail. 75 | /// This can for example be used for upstream functions that return tracers like 76 | /// [`eyre::Report`] directly. 77 | pub struct TraceOnly(PhantomData); 78 | 79 | /// An [`ErrorSource`] that only provides error details but do not provide any trace. 80 | /// This can typically comes from primitive error types that do not implement 81 | /// [`Error`](std::error::Error). The `Detail` type is the error and the returned 82 | /// trace is `None`. 83 | /// 84 | /// It is also possible to omit specifying the error as an error source, and instead 85 | /// place it as a field in the error variant. However specifying it as a `DetailOnly` 86 | /// source may give stronger hint to the reader that the particular error variant 87 | /// is caused by other underlying errors. 88 | pub struct DetailOnly(PhantomData); 89 | 90 | pub struct BoxDetail(PhantomData); 91 | 92 | impl ErrorSource for DetailOnly { 93 | type Detail = Detail; 94 | type Source = Detail; 95 | 96 | fn error_details(source: Self::Source) -> (Self::Detail, Option) { 97 | (source, None) 98 | } 99 | } 100 | 101 | impl ErrorSource for NoSource { 102 | type Detail = (); 103 | type Source = (); 104 | 105 | fn error_details(_: Self::Source) -> (Self::Detail, Option) { 106 | ((), None) 107 | } 108 | } 109 | 110 | impl ErrorSource for TraceOnly { 111 | type Detail = (); 112 | type Source = Trace; 113 | 114 | fn error_details(source: Self::Source) -> (Self::Detail, Option) { 115 | ((), Some(source)) 116 | } 117 | } 118 | 119 | impl ErrorSource for DisplayError 120 | where 121 | E: Display, 122 | Tracer: ErrorMessageTracer, 123 | { 124 | type Detail = E; 125 | type Source = E; 126 | 127 | fn error_details(source: Self::Source) -> (Self::Detail, Option) { 128 | let trace = Tracer::new_message(&source); 129 | (source, Some(trace)) 130 | } 131 | } 132 | 133 | impl ErrorSource for DisplayOnly 134 | where 135 | E: Display, 136 | Tracer: ErrorMessageTracer, 137 | { 138 | type Detail = (); 139 | type Source = E; 140 | 141 | fn error_details(source: Self::Source) -> (Self::Detail, Option) { 142 | let trace = Tracer::new_message(&source); 143 | ((), Some(trace)) 144 | } 145 | } 146 | 147 | impl ErrorSource for TraceClone 148 | where 149 | E: Clone, 150 | Tracer: ErrorTracer, 151 | { 152 | type Detail = E; 153 | type Source = E; 154 | 155 | fn error_details(source: Self::Source) -> (Self::Detail, Option) { 156 | let detail = source.clone(); 157 | let trace = Tracer::new_trace(source); 158 | (detail, Some(trace)) 159 | } 160 | } 161 | 162 | impl ErrorSource for TraceError 163 | where 164 | Tracer: ErrorTracer, 165 | { 166 | type Detail = (); 167 | type Source = E; 168 | 169 | fn error_details(source: Self::Source) -> (Self::Detail, Option) { 170 | let trace = Tracer::new_trace(source); 171 | ((), Some(trace)) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /flex-error/src/tracer.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Display; 2 | 3 | /// An `ErrorMessageTracer` can be used to generically trace 4 | /// any error detail that implements [`Display`](std::fmt::Display). 5 | /// 6 | /// The error tracer may add backtrace information when the tracing 7 | /// methods are called. However since the error detail is required 8 | /// to only implement `Display`, any existing error trace may be 9 | /// lost even if the error detail implements `Error` and contains 10 | /// backtrace, unless the backtrace is serialized in `Display`. 11 | pub trait ErrorMessageTracer { 12 | /// Creates a new error trace, starting from a source error 13 | /// detail that implements [`Display`](std::fmt::Display). 14 | fn new_message(message: &E) -> Self; 15 | 16 | /// Adds new error detail to an existing trace. 17 | fn add_message(self, message: &E) -> Self; 18 | 19 | /// If the `std` feature is enabled, the error tracer 20 | /// also provides method to optionally converts itself 21 | /// to a `dyn` [`Error`](std::error::Error). 22 | #[cfg(feature = "std")] 23 | fn as_error(&self) -> Option<&(dyn std::error::Error + 'static)>; 24 | } 25 | 26 | /// An error tracer implements `ErrorTracer` if it supports 27 | /// more sophisticated error tracing for an error type `E`. 28 | /// The contraint for `E` depends on the specific error tracer 29 | /// implementation. 30 | /// 31 | /// For example, [`EyreTracer`](crate::tracer_impl::eyre::EyreTracer) 32 | /// and [`AnyhowTracer`](crate::tracer_impl::anyhow::AnyhowTracer) requires 33 | /// an error type to satisfy `E: Error + Send + Sync + 'static`. 34 | /// 35 | /// The error tracer also requires ownership of the source error to be 36 | /// transferred to the error tracer. Because of this, it may not be possible 37 | /// to extract a source error type to be used as both error detail and 38 | /// error trace. We also should not expect `E` to implement `Clone`, as 39 | /// error types such as [`eyre::Report`] do not implement `Clone`. 40 | pub trait ErrorTracer: ErrorMessageTracer { 41 | /// Create a new error trace from `E`, also taking ownership of it. 42 | /// 43 | /// This calls the underlying methods such as [`eyre::Report::new`] 44 | /// and [`anyhow::Error::new`]. 45 | fn new_trace(err: E) -> Self; 46 | 47 | /// Add a new error trace from `E`. In the current underlying implementation, 48 | /// this is effectively still has the same behavior as 49 | /// [`ErrorMessageTracer::add_message`]. This is because [`eyre`] and 50 | /// [`anyhow`] do not support adding new set of backtraces to an existing 51 | /// trace. So effectively, currently the error tracers can track at most 52 | /// one backtrace coming from the original error source. 53 | fn add_trace(self, err: E) -> Self; 54 | } 55 | -------------------------------------------------------------------------------- /flex-error/src/tracer_impl/anyhow.rs: -------------------------------------------------------------------------------- 1 | use crate::tracer::{ErrorMessageTracer, ErrorTracer}; 2 | use core::fmt::{Debug, Display}; 3 | 4 | /// Type alias to [`anyhow::Error`] 5 | pub type AnyhowTracer = anyhow::Error; 6 | 7 | impl ErrorMessageTracer for AnyhowTracer { 8 | fn new_message(err: &E) -> Self { 9 | let message = alloc::format!("{}", err); 10 | AnyhowTracer::msg(message) 11 | } 12 | 13 | fn add_message(self, err: &E) -> Self { 14 | let message = alloc::format!("{}", err); 15 | self.context(message) 16 | } 17 | 18 | #[cfg(feature = "std")] 19 | fn as_error(&self) -> Option<&(dyn std::error::Error + 'static)> { 20 | use core::ops::Deref; 21 | Some(self.deref()) 22 | } 23 | } 24 | 25 | impl ErrorTracer for AnyhowTracer 26 | where 27 | E: Display + Debug + Send + Sync + 'static, 28 | { 29 | fn new_trace(err: E) -> Self { 30 | AnyhowTracer::msg(err) 31 | } 32 | 33 | fn add_trace(self, err: E) -> Self { 34 | let message = alloc::format!("{}", err); 35 | self.context(message) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /flex-error/src/tracer_impl/eyre.rs: -------------------------------------------------------------------------------- 1 | use crate::tracer::{ErrorMessageTracer, ErrorTracer}; 2 | use core::fmt::{Debug, Display}; 3 | 4 | /// Type alias to [`eyre::Report`]. 5 | pub type EyreTracer = eyre::Report; 6 | 7 | impl ErrorMessageTracer for EyreTracer { 8 | fn new_message(err: &E) -> Self { 9 | let message = alloc::format!("{}", err); 10 | EyreTracer::msg(message) 11 | } 12 | 13 | fn add_message(self, err: &E) -> Self { 14 | let message = alloc::format!("{}", err); 15 | self.wrap_err(message) 16 | } 17 | 18 | #[cfg(feature = "std")] 19 | fn as_error(&self) -> Option<&(dyn std::error::Error + 'static)> { 20 | use core::ops::Deref; 21 | Some(self.deref()) 22 | } 23 | } 24 | 25 | impl ErrorTracer for EyreTracer 26 | where 27 | E: Display + Debug + Send + Sync + 'static, 28 | { 29 | fn new_trace(err: E) -> Self { 30 | EyreTracer::msg(err) 31 | } 32 | 33 | fn add_trace(self, err: E) -> Self { 34 | let message = alloc::format!("{}", err); 35 | self.wrap_err(message) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /flex-error/src/tracer_impl/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod string; 2 | 3 | #[cfg(feature = "anyhow_tracer")] 4 | pub mod anyhow; 5 | 6 | #[cfg(feature = "eyre_tracer")] 7 | pub mod eyre; 8 | -------------------------------------------------------------------------------- /flex-error/src/tracer_impl/string.rs: -------------------------------------------------------------------------------- 1 | use crate::tracer::{ErrorMessageTracer, ErrorTracer}; 2 | use alloc::string::String; 3 | use core::fmt::{Debug, Display, Formatter}; 4 | 5 | /// A naive string tracer serializes error messages into 6 | /// string and simply concatenate them together. 7 | /// This can be used for example in `no_std` environment, 8 | /// which may not support more complex error tracers. 9 | pub struct StringTracer(pub String); 10 | 11 | impl ErrorMessageTracer for StringTracer { 12 | fn new_message(err: &E) -> Self { 13 | StringTracer(alloc::format!("{}", err)) 14 | } 15 | 16 | fn add_message(self, err: &E) -> Self { 17 | StringTracer(alloc::format!("{0}: {1}", err, self.0)) 18 | } 19 | 20 | #[cfg(feature = "std")] 21 | fn as_error(&self) -> Option<&(dyn std::error::Error + 'static)> { 22 | None 23 | } 24 | } 25 | 26 | impl ErrorTracer for StringTracer { 27 | fn new_trace(err: E) -> Self { 28 | StringTracer(alloc::format!("{}", err)) 29 | } 30 | 31 | fn add_trace(self, err: E) -> Self { 32 | StringTracer(alloc::format!("{0}: {1}", err, self.0)) 33 | } 34 | } 35 | 36 | impl Debug for StringTracer { 37 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 38 | write!(f, "StringTracer: {0}", self.0) 39 | } 40 | } 41 | 42 | impl Display for StringTracer { 43 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 44 | write!(f, "{0}", self.0) 45 | } 46 | } 47 | --------------------------------------------------------------------------------