[][src]Crate snafu

SNAFU

Design philosophy

SNAFU believes that it should be easy to bin one underlying error type (such as io::Error) into multiple domain-specific errors while also optionally adding contextual information.

SNAFU is designed to be used in libraries, not just end-user applications.

Quick example

This example mimics a (very poor) authentication process that opens a file, writes to a file, and checks the user's ID. While two of our operations involve an io::Error, these are different conceptual errors to us.

SNAFU creates context selectors mirroring each error variant. These are used with the context method to provide ergonomic error handling.

use snafu::{Snafu, ResultExt};
use std::{fs, path::{Path, PathBuf}};

#[derive(Debug, Snafu)]
enum Error {
    #[snafu_display("Could not open config from {}: {}", "filename.display()", "source")]
    OpenConfig { filename: PathBuf, source: std::io::Error },
    #[snafu_display("Could not save config to {}: {}", "filename.display()", "source")]
    SaveConfig { filename: PathBuf, source: std::io::Error },
    #[snafu_display("The user id {} is invalid", "user_id")]
    UserIdInvalid { user_id: i32 },
}

type Result<T, E = Error> = std::result::Result<T, E>;

fn log_in_user<P>(config_root: P, user_id: i32) -> Result<()>
where
    P: AsRef<Path>,
{
    let config_root = config_root.as_ref();
    let filename = &config_root.join("config.toml");

    let config = fs::read(filename).context(OpenConfig { filename })?;
    // Perform updates to config
    fs::write(filename, config).context(SaveConfig { filename })?;

    if user_id != 42 {
        return Err(Error::UserIdInvalid { user_id });
    }

    Ok(())
}

The Snafu macro

This procedural macro implements the Error trait and produces the corresponding context selectors.

Detailed example

use snafu::Snafu;
use std::path::PathBuf;

#[derive(Debug, Snafu)]
enum Error {
    #[snafu_display("Could not open config at {}: {}", "filename.display()", "source")]
    OpenConfig { filename: PathBuf, source: std::io::Error },
    #[snafu_display("Could not open config: {}", "source")]
    SaveConfig { source: std::io::Error },
}

Generated code

This will generate two additional types called context selectors:

This example is not tested
struct OpenConfig<P> { filename: P }
struct SaveConfig<P> { filename: P }

Notably:

  1. One struct is created for each enum variant.
  2. The name of the struct is the same as the enum variant's name.
  3. The source field has been removed; the library will automatically handle this for you.
  4. Each field's type has been replaced with a generic type.

Each context selector will have an implementation of From for a snafu::Context:

This example is not tested
impl<P> From<Context<Error, OpenConfig<T0>>> for Error
where
    P: Into<PathBuf>,

Re-exports

pub use snafu_derive::Snafu;

Structs

Context

A combination of an underlying error and additional information about the error. It is not expected for users of this crate to interact with this type.

Traits

ResultExt

Additions to Result.