Macro flex_error::define_error [−][src]
macro_rules! define_error { ($name : ident $expr : tt) => { ... }; (# $derive : tt $name : ident $expr : tt) => { ... }; (@ with_tracer [$tracer : ty] $name : ident $expr : tt) => { ... }; (@ with_tracer [$tracer : ty] # $derive : tt $name : ident $expr : tt) => { ... }; }
Expand description
define_error! is the main macro that implements a mini DSL to
define error types using flex-error. The DSL syntax
is as follows:
define_error! { ErrorName { SubErrorWithFieldsAndErrorSource { field1: Type1, field2: Type2, ... } [ ErrorSource ] | e | { format_args!( "format error message with field1: {}, field2: {}, source: {}", e.field1, e.field2, e.source) }, SubErrorWithFieldsOnly { field1: Type1, field2: Type2, ... } | e | { format_args!( "format error message with field1: {}, field2: {}", e.field1, e.field2) }, SubErrorWithSourceOnly [ ErrorSource ] | e | { format_args!( "format error message with source: {}", e.source) }, SubError | e | { format_args!( "only suberror message") }, } }
Behind the scene, define_error! does the following:
-
Define an enum with the postfix
Detail, e.g. an error namedFooErrorwould have the enumFooErrorDetaildefined. -
Define the error name as a type alias to
ErrorReport<ErrorNameDetail, DefaultTracer>. e.g.type FooError = ErrorReport<FooErrorDetail, DefaultTracer>;. -
For each suberror, does the following:
-
Define a variant with the suberror name in the detail enum. e.g. a
Barsuberror inFooErrorbecomes aBarvariant inFooErrorDetail. -
Define a struct with the
Subdetailpostfix. e.g.Barwould have aBarSubdetailstruct.-
The struct contains all named fields if specified.
-
If an error source is specified, a
sourcefield is also defined with the typeAsErrorDetail<ErrorSource>. e.g. a suberror withDisplayError<SourceError>would have the fieldsource: SourceError. Because of this, the field namesourceis reserved and should not be present in other detail fields.
-
-
Implement
Displayfor the suberror using the provided formatter to format the arguments. The argument type of the formatter is the suberror subdetail struct. -
Define a suberror constructor function in snake case with the postfix
_error. e.g.Barwould have the constructor functionbar_error.-
The function accepts arguments according to the named fields specified.
-
If an error source is specified, the constructor function also accepts a last argument of type
AsErrorSource<ErrorSource>. e.g. a suberror withDisplayError<SourceError>would have the last argument of typeSourceErrorin the constructor function. -
The function returns the main error type. e.g.
FooError, which is alias toErrorReport<FooErrorDetail, DefaultTrace>.
-
-
We can demonstrate the macro expansion of define_error! with the following example:
// An external error type implementing Display use external_crate::ExternalError; define_error! { FooError { Bar { code: u32 } [ DisplayError<ExternalError> ] | e | { format_args!("Bar error with code {}", e.code) }, Baz { extra: String } | e | { format_args!("General Baz error with extra detail: {}", e.extra) } } }
The above code will be expanded into something like follows:
pub type FooError = Report<FooErrorDetail, DefaultTracer>; #[derive(Debug)] pub enum FooErrorDetail { Bar(BarSubdetail), Baz(BazSubdetail), } #[derive(Debug)] pub struct BarSubdetail { pub code: u32, pub source: ExternalError } #[derive(Debug)] pub struct BazSubdetail { pub extra: String } fn bar_error(code: u32, source: ExternalError) -> FooError { ... } fn baz_error(extra: String) -> FooError { ... } impl Display for BarSubdetail { fn fmt(&self, f: &mut Formatter<'_>) -> Result { let e = self; write!(f, "{}", format_args!("Bar error with code {}", e.code)) } } impl Display for BazSubdetail { fn fmt(&self, f: &mut Formatter<'_>) -> Result { let e = self; write!(f, "{}", format_args!("General Baz error with extra detail: {}", e.code)) } } impl Display for FooErrorDetail { ... }
For the detailed macro expansion, you can use cargo-expand
to expand the Rust module that uses define_error! to see how the error definition
gets expanded.
Because FooError is defined as an alias to ErrorReport,
it automatically implements ErrorSource and can be used
as a source in other error definitions. For example:
define_error! { QuuxError { Foo { action: String } [ FooError ] | e | { format_args!("error arised from Foo when performing action {}", e.action) }, ... } }
Would be expanded to include the following definitions:
pub struct FooSubdetail { pub action: String, pub source: FooErrorDetail } pub fn foo_error(action: String, source: FooError) { ... }
In the formatter for QuuxErrorDetail::Foo, we can also see that it does not
need to include the error string from FooError. This is because the error
tracer already takes care of the source error trace, so the full trace is
automatically tracked inside foo_error. The outer error only need to
add additional detail about what caused the source error to be raised.