Module snafu::guide::what_code_is_generated
source · Expand description
§What code is generated by the Snafu
macro
This procedural macro:
- produces the corresponding context selectors
- implements the
Error
trait - implements the
Display
trait - implements the
ErrorCompat
trait
§Detailed example
use snafu::{prelude::*, Backtrace};
use std::path::PathBuf;
#[derive(Debug, Snafu)]
enum Error {
#[snafu(display("Could not open config at {}", filename.display()))]
OpenConfig {
filename: PathBuf,
source: std::io::Error,
},
#[snafu(display("Could not open config"))]
SaveConfig { source: std::io::Error },
#[snafu(display("The user id {user_id} is invalid"))]
UserIdInvalid { user_id: i32, backtrace: Backtrace },
#[snafu(display("Could not validate config with key {key}: checksum was {checksum}"))]
ConfigValidationFailed {
checksum: u64,
key: String,
source: crypto::Error,
},
}
§Generated code
Note — The actual generated code may differ in exact names and details. This section is only intended to provide general guidance. Use a tool like cargo-expand to see the exact generated code for your situation.
§Context selectors
Each enum variant will generate an additional type called a context selector:
struct OpenConfigSnafu<P> {
filename: P,
}
struct SaveConfigSnafu<P>;
struct UserIdInvalidSnafu<I> {
user_id: I,
}
struct ConfigValidationFailedSnafu<I, S> {
checksum: I,
key: S,
}
Notably:
- The name of the selector is the enum variant’s name with the suffix
Snafu
added. If the name originally ended inError
, that is removed. - The
source
andbacktrace
fields have been removed; the library will automatically handle these for you. - Each remaining field’s type has been replaced with a generic type.
- If there are no fields remaining for the user to specify, the selector will not require curly braces.
If the original variant had a source
field, its context selector
will have an implementation of IntoError
:
impl<P> IntoError<Error> for OpenConfigSnafu<P>
where
P: Into<PathBuf>,
Otherwise, the context selector will have the inherent methods build
and fail
and can be used with the ensure
macro:
impl<I> UserIdInvalidSnafu<I>
where
I: Into<i32>,
{
fn build(self) -> Error { /* ... */ }
fn fail<T>(self) -> Result<T, Error> { /* ... */ }
}
If the original variant had a backtrace
field, the backtrace
will be automatically constructed when either IntoError
or
build
/fail
are called.
§Error
Error::source
will return the underlying error, if
there is one:
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Error::OpenConfig { source, .. } => Some(source),
Error::SaveConfig { source, .. } => Some(source),
Error::UserIdInvalid { .. } => None,
Error::ConfigValidationFailed { source, .. } => Some(source),
}
}
}
Error::cause
will return the same as source
. As
Error::description
is soft-deprecated, it will
return a string matching the name of the variant.
§Display
Every field of the enum variant is made available to the format string, even if they are not used:
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter ) -> fmt::Result {
match self {
Error::OpenConfig { filename, source } =>
write!(f, "Could not open config at {}", filename.display()),
Error::SaveConfig { source } =>
write!(f, "Could not open config"),
Error::UserIdInvalid { user_id, backtrace } =>
write!(f, "The user id {} is invalid", user_id = user_id),
Error::ConfigValidationFailed { key, checksum, source } =>
write!(f, "Could not validate config with key {}: checksum was {}", key = key, checksum = checksum),
}
}
}
If no display format is specified, the variant’s name will be used by default.
§ErrorCompat
Every variant that carries a backtrace will return a reference to that backtrace.
impl snafu::ErrorCompat for Error {
fn backtrace(&self) -> Option<&Backtrace> {
match self {
Error::OpenConfig { .. } => None,
Error::SaveConfig { .. } => None,
Error::UserIdInvalid { backtrace, .. } => Some(backtrace),
Error::ConfigValidationFailed { .. } => None,
}
}
}