The arti-client crate is our primary point of entry. It exposes a mostly-opaque Error type, and an ErrorKind type derived from its inner (hidden) data.
The stable, supported ways to get information from arti_client::Error are:
The ErrorKind enum contains a number of possible error types. Each is designed for high-level consumption: its documentation should explain why such an error might occur, and what the application might want to do about it. (If we cannot write such documentation for an ErrorKind, it is probably wrong.)
There is additionally an unstable way to get more detailed information from an arti_client::Error. If you have enabled the "error-details" feature, you can call a detail() method, to access an inner ErrorDetail enum with a more specific breakdown of where the error occurred, how, and why.
Using the "error-details" feature breaks your semver guarantees on the arti-client crate. If you ever need to do so, then possible one of us is mistaken: maybe we have made a mistake in designing Error or ErrorKind, or maybe there is a better way to do what you're trying to achieve. Please get in touch so that we can help figure out the best solution.
We are making the arti_client::Error type opaque so that we have freedom to radically refactor ErrorDetail, as well as the Error types from other crates. We are exposing it behind a feature because we recognize that we will inevitably make some mistakes in our designs, and it's important to allow escape hatches for users with tricky needs.
Everywhere else besides arti_error::Error, we try to make our error types follow these general patterns:
* All should implement Clone, Display, Error, Debug, Send, Sync, 'static.
* When we wrap an inner error, we always include context information describing what was happening when the inner error occurred. This means that we should usually not `impl From<TheirInner> for MyError`.
* Whenever appropriate, we have a `pub fn kind(&self) -> ErrorKind` function.
* When a public function can fail for a number of reasons that are much more limited than the crate's entire Error type, we should consider give that function its own Error type.
* We allow more instability in these types than we allow in arti_client: these types should be inaccessible from the arti_client when "error-details" is not enabled.
If the build fails, then someone is responsible for putting the `OwnedChatTarget` (or some subset of the information in it) into the error structure.
This should be the *calling* crate. Otherwise the same context information must be duplicated for every variant of the lower crate error. In the upper crate, it can be in only one variant.
For example, when tor-circmgr calls `build_channel`, it is tor-circmgr which is responsible for putting the context in the outer error variant, ie
When a problem is reported, different error types should generally produce different messages. Where should this be done ?
Answer: the place where the type is embedded. For example:
```
enum tor_circmgr::Error {
/// Problem with channel
#[error("Problem with channel to {peer}")]
ChanFailed {
cause: tor_chanmgr::Error,
enum tor_chanmgr::Error {
/// Network IO error or TLS error
#[error("Network IO error, or TLS error, talking to {peer}")]
Io {
/// Who we were talking to
peer: SocketAddr,
```
So the channel error does not say that it *is* a channel error, just as an `io::Error` doesn't say that it is an IO error. `tor_chanmgr::Error::Io` says that it is an IO error; when that is found inside eg a `tor_circmgr::Error`, the circmgr error is responsible for saying it's about a channel (and describing relevant channel properties).
Matching on internal errors (ie bugs) is not going to be useful for callers. It might want to have a backtrace in it as well as something resembling an assertion message.