1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
//! A wrapper around `anyhow` but with a "primary" error type.
//!
//! ## Motivation
//!
//! This library aims to be the glue between `anyhow` and `thiserror`.
//! It allows you to define a primary error type for variants that the caller
//! should match on, while still capturing any other errors that may have
//! occurred along the way.
//!
//! ### Documenting the error type of a function
//!
//! If you simply return an `anyhow::Error`, the caller has no idea what
//! kind of error to expect. They would need to read your code to determine
//! what the possible error types are.
//!
//! By using `TError`, you can specify the primary error type that the caller
//! should match on. This has the effect of documenting the primary error type
//! for your function.
//!
//! ```ignore
//! fn my_fallible_function() -> typederror::Result<(), MyError> {
//! // Do something that might fail.
//! let s = std::fs::read_to_string("file.txt").map_err(|e| MyError::IoError(e))?;
//! // NOTE: if `MyError` implements `From<std::io::Error>`,
//! // you can do `std::fs::read_to_string("file.txt").terror()?` instead.
//! some_operation(s)?; // An error we don't need to match on.
//! Ok(())
//! }
//! ```
//!
//! The primary error type could be an enum that derives
//! `thiserror::Error`, where only the meaningful errors are captured by
//! the enum and any other errors are captured by the `anyhow::Error`
//! underneath.
//!
//! You can also implement `DefaultError` so that all other errors are
//! captured in a special "catch-all" variant of the primary error type.
//!
//! ```ignore
//! #[derive(Debug, thiserror::Error)]
//! enum MyError {
//! #[error("IO error: {0}")]
//! IoError(#[from] std::io::Error),
//! #[error("{0}")]
//! Misc(typederror::anyhow::Error)
//! }
//!
//! impl DefaultError for MyError {
//! fn from_anyhow(err: typederror::anyhow::Error) -> Self {
//! Self::Misc(err)
//! }
//! }
//! ```
//!
//! ### Downcasting to the primary error type
//!
//! Since `TError` already knows the primary error type, it can provide
//! convenience methods for downcasting to that type. This allows you to
//! more easily work with errors of a single type without needing to match
//! on several different error types.
//!
//! ```ignore
//! if let Err(err) = my_fallible_function() { // returns Result<T, TError<MyError>>
//! match err.get() {
//! MyError::IoError(e) => { // e is of type `std::io::Error`
//! // Handle the error.
//! }
//! MyError::Misc(e) => { // e is of type `anyhow::Error`
//! // Handle the error.
//! }
//! }
//! }
//! ```
//!
//! You can also downcast to other types if needed, the same as you
//! would with `anyhow`.
//!
//! ```ignore
//! match err.downcast_ref::<serde::Error>() {
//! Ok(e) => {
//! // Handle serde error.
//! }
//! Err(e) => {
//! // Handle other error.
//! }
//! }
//! ```
//!
//! ### Start simple and add error variants later
//!
//! To get you started, you can use `TError<()>` as the primary error type.
//! Or use `typederror::Result<T>` as the return type of your function.
//! This will effectively work the same as `anyhow`, allowing you to
//! write your code and worry about error types later.
//!
//! ```ignore
//! fn do_something() -> typederror::Result<()> {
//! // Do something.
//! my_fallible_function()?;
//! Ok(())
//! }
//! ```
//!
//! Later, when you want to create specific variants for your function
//! for easier matching by the caller, you can create an enum,
//! derive `thiserror::Error`, and use that as the primary error type instead.
//! You will need to add any necessary conversions, but you only need to add
//! the variants you want to match on.
//!
//! All other errors will still be captured as per `anyhow` behaviour, or
//! they can be captured in a special "catch-all" variant of your enum by
//! implementing the `DefaultError` trait on the enum.
//!
//! ## Caveats
//!
//! Unfortunately the `?` operator cannot automatically convert error types
//! to your primary error type.
//!
//! For example:
//! ```ignore
//! #[derive(Debug, thiserror::Error)]
//! enum MyError {
//! #[error("IO error: {0}")]
//! IoError(#[from] std::io::Error),
//! #[error("{0}")]
//! Misc(anyhow::Error)
//! }
//!
//! impl DefaultError for MyError {
//! fn from_anyhow(err: anyhow::Error) -> Self {
//! Self::Misc(err)
//! }
//! }
//!
//! fn my_fallible_function() -> typederror::Result<(), MyError> {
//! let s = std::fs::read_to_string("file.txt")?;
//! // Do something else with s.
//! Ok(())
//! }
//!
//! fn main() {
//! if let Err(e) = my_fallible_function() {
//! match e.get() {
//! // ...
//! }
//! }
//! }
//! ```
//!
//! In the above example, the `?` operator will not automatically convert the
//! `std::io::Error` to `MyError::IoError`, as it would if you had used
//! `MyError` as the error type directly. The error would instead match as
//! `MyError::Misc` in the call to `e.get()`.
//!
//! To capture the `IoError` correctly, change the first line of the function to
//! ```ignore
//! let s = std::fs::read_to_string("file.txt").terror()?;
//! ```
//!
pub use *;
/// Re-export of anyhow macros.