A Macros 1.1 implementation of https://crates.io/crates/error-chain
The error-chain example
```ignore
mod other_error {
error_chain! {}
}
error_chain! {
types {
Error, ErrorKind, ResultExt, Result;
}
links {
Another(other_error::Error, other_error::ErrorKind) #[cfg(unix)];
}
foreign_links {
Fmt(::std::fmt::Error);
Io(::std::io::Error) #[cfg(unix)];
}
errors {
InvalidToolchainName(t: String) {
description("invalid toolchain name")
display("invalid toolchain name: '{}'", t)
}
}
}
```
becomes
```ignore
mod other_error {
#[derive(Debug, error_chain)]
pub enum ErrorKind {
Msg(String),
}
}
#[derive(Debug, error_chain)]
pub enum ErrorKind {
Msg(String),
#[cfg(unix)]
#[error_chain(link = "other_error::Error")]
Another(other_error::ErrorKind),
#[error_chain(foreign)]
Fmt(::std::fmt::Error),
#[cfg(unix)]
#[error_chain(foreign)]
Io(::std::io::Error),
#[error_chain(custom)]
#[error_chain(description = r#"|_| "invalid toolchain name""#)]
#[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)]
InvalidToolchainName(String),
}
```
So the obvious differences from `error_chain!` are:
- The ErrorKind is an enum instead of a macro invocation.
- Error links are variants of the enum instead of lines inside the macro.
- Links have explicit annotations marking them as chainable / foreign / custom instead of being grouped into corresponding sections of the macro.
- Attributes like `#[cfg]` are applied to the variants directly instead of needing special syntax.
- `description` and `display` are defined as function expressions specified as attribute values, instead of shorthands integrated into the macro syntax.
The less obvious differences are:
- The ErrorKind must explicitly implement `::std::fmt::Debug`, either automatically using `#[derive]` or manually implemented separately. `error_chain!` does this implicitly.
- The ErrorKind must have `pub` visibility. `error_chain!` does this implicitly.
- The ErrorKind must have a special `Msg(String)` member. `error_chain!` does this implicitly.
- Doc comments, since they're effectively attributes, can be applied on the enum variants without any special syntax like `error_chain!` has.
- The ErrorKind can be generic.
# Enum attributes
- `#[error_chain(error = "ErrorName")]`
Override the name of the generated `Error` struct to the given name. If not provided, the struct will be named `Error`.
- `#[error_chain(result_ext = "ResultExtName")]`
Override the name of the generated `ResultExt` trait to the given name. If not provided, the trait will be named `ResultExt`.
- `#[error_chain(result = "ResultName")]`
Override the name of the generated `Result` type alias to the given name. If not provided, the alias will be named `Result`.
If set to the empty string `""`, the alias will not be generated at all.
- `#[error_chain(backtrace = "false")]` or `#[error_chain(backtrace = false)]`
Disable backtrace functionality in the generated code. This should be kept in sync with the value of the `backtrace` feature of the `error-chain` crate.
In other words, if you set `backtrace = "false"` here, you must also specify `default-features = false` for `error-chain` in your `Cargo.toml`
# Variant definitions
- Chainable links
```ignore
#[error_chain(link = "other_error::Error")]
Another(other_error::ErrorKind),
```
A chainable link is an error and errorkind that have been generated using `error-chain` or `derive-error-chain`. The variant must have a single field
to hold the chained errorkind, and the `link` attribute must specify a path to the chained error.
- Foreign links
```ignore
#[error_chain(foreign)]
Fmt(::std::fmt::Error),
```
A foreign link is an error that implements `::std::error::Error` but otherwise does not follow `error-chain`'s conventions. The variant must have
a single field to hold the foreign error.
- Custom links
```ignore
#[error_chain(custom)]
InvalidToolchainName(String),
```
A custom link is an arbitrary variant that can hold any members.
# Variant attributes
In addition to the above attributes that identify the type of the variant's link, the below attributes can be used on all links.
- `#[error_chain(description = "some_function_expression")]`
Specifies a function expression to be used to implement `ErrorKind::description()`.
This value is also returned from the implementation of `::std::error::Error::description()` on the generated `Error`.
This can be an inline lambda:
```ignore
#[error_chain(description = r#"|_| "invalid toolchain name""#)]
InvalidToolchainName(String),
```
or it can be a separate function:
```ignore
#[error_chain(description = "invalid_toolchain_name_error_description")]
InvalidToolchainName(String),
//
fn invalid_toolchain_name_error_description(_: &str) -> &str {
"invalid toolchain name"
}
```
The function expression must have the signature `(...) -> &'static str`. It should have one parameter for each field of the variant.
The fields are passed in by reference.
Thus in the above example, since `InvalidToolchainName` had a single field of type `String`, the function expression needed to be of type
`(&str) -> &'static str`
If not specified, the default implementation behaves in this way:
- Chainable links: Forwards to the chained error kind's `description()`
- Foreign links: Forwards to the foreign error's implementation of `::std::error::Error::description()`
- Custom links: Returns the stringified name of the variant.
- `#[error_chain(display = "some_function_expression")]`
Specifies a function expression to be used to implement `::std::fmt::Display::fmt()` on the `ErrorKind` and generated `Error`
This can be an inline lambda:
```ignore
#[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)]
InvalidToolchainName(String),
```
or it can be a separate function:
```ignore
#[error_chain(display = "invalid_toolchain_name_error_display")]
InvalidToolchainName(String),
//
fn invalid_toolchain_name_error_display(f: &mut ::std::fmt::Formatter, t: &str) -> ::std::fmt::Result {
write!(f, "invalid toolchain name: '{}'", t)
}
```
The function expression must have the signature `(&mut ::std::fmt::Formatter, ...) -> ::std::fmt::Result`.
It should have one `&mut ::std::fmt::Formatter` parameter, and one parameter for each field of the variant. The fields are passed in by reference.
For brevity, closure expressions do not need the `&mut ::std::fmt::Formatter` parameter and instead capture `f` from the closure environment.
Thus in the above example, since `InvalidToolchainName` had a single field of type `String`, the function expression needed to be of type
`(&mut ::std::fmt::Formatter, &str) -> ::std::fmt::Result`
If not specified, the default implementation of `::std::fmt::Display::fmt()` behaves in this way:
- Chainable links: Forwards to the chained errorkind's implementation of `::std::fmt::Display::fmt()`
- Foreign links: Forwards to the foreign error's implementation of `::std::fmt::Display::fmt()`
- Custom links: Writes the description of the variant to the formatter.
- `#[error_chain(cause = "some_function_expression")]`
Specifies a function expression to be used to implement `::std::fmt::Error::cause()` on the generated `Error`
This can be an inline lambda:
```ignore
#[error_chain(cause = "|_, err| err")]
JSON(::std::path::PathBuf, ::serde_json::Error),
```
or it can be a separate function:
```ignore
#[error_chain(cause = "parse_json_file_error_cause")]
JSON(::std::path::PathBuf, ::serde_json::Error),
//
fn parse_json_file_error_cause<'a>(_: &::std::path::Path, err: &'a ::serde_json::Error) -> &'a ::std::error::Error {
err
}
```
The function expression must have the signature `(...) -> &::std::error::Error`. It should have one parameter for each field of the variant.
The fields are passed in by reference. The result is wrapped in `Option::Some()` for returning from `::std::error::Error::cause()`
Thus in the above example, since `JSON` had two fields of type `::std::path::PathBuf` and `::serde_json::Error`, the function expression needed to be of type
`(&::std::path::Path, &::serde_json::Error) -> &::std::error::Error`
If not specified, the default implementation of `::std::error::Error::cause()` behaves in this way:
- Chainable links: Returns `None`
- Foreign links: Forwards to the foreign error's implementation of `::std::error::Error::cause()`
- Custom links: Returns `None`
# Notes
If you want to use other macros from the `error_chain` like `bail!`, note that the following code:
```ignore
#[macro_use] extern crate derive_error_chain;
#[macro_use] extern crate error_chain;
#[derive(Debug, error_chain)]
enum ErrorKind {
Msg(String),
}
```
will fail to compile with:
```ignore
error: macro `error_chain` may not be used for derive attributes
```
This is because both crates export a macro named `error_chain` and the macro from the second crate overrides the first.
To fix this, import `error_chain` before `derive_error_chain`:
```ignore
#[macro_use] extern crate error_chain;
#[macro_use] extern crate derive_error_chain;
```
or use a fully-qualified path for the custom derive (nightly only):
```ignore
#![feature(proc_macro)]
extern crate derive_error_chain;
#[macro_use] extern crate error_chain;
#[derive(Debug, derive_error_chain::error_chain)]
enum ErrorKind {
Msg(String),
}
```