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
Errortrait - implements the
Displaytrait - implements the
ErrorCompattrait
§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
Snafuadded. If the name originally ended inError, that is removed. - The
sourceandbacktracefields 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,
}
}
}