Coder Social home page Coder Social logo

failchain's Introduction

failchain

failchain is a tiny companion crate to the failure crate, which aims to recover the ergonomics of error_chain. It does this by bringing back:

  • chain_err
  • non-verbose Error, ErrorKind pairs
  • support for bail! and ensure! with custom ErrorKind-s

The failure library recommends three different patterns for errors. This library implementes the most complex one (and the most useful one) but without all the boilerplate.

What it looks like

// errors.rs
use failchain::{BoxedError, ChainErrorKind};
use failure::Fail;
use std::path::PathBuf;
use std::result::Result as StdResult;

pub type Error = BoxedError<ErrorKind>; // Use `UnboxedError` instead for
                                        // non-allocating, but bigger `Error`.
pub type Result<T> = StdResult<T, Error>;

#[derive(Clone, Eq, PartialEq, Debug, Fail)]
pub enum ErrorKind {
    #[fail(display = "Metadata I/O error {:?}.", 0)]
    MetadataIo(PathBuf),

    #[fail(display = "Corrupt metadata file: {}", 0)]
    CorruptMetadata(String),

    #[fail(display = "WAD I/O error {:?}.", 0)]
    WadIo(PathBuf),

    #[fail(display = "Corrupt WAD file: {}", 0)]
    CorruptWad(String),
}

impl ChainErrorKind for ErrorKind {
    type Error = Error;
}


// main.rs
use super::errors::{ErrorKind, Result};
use failchain::{
    bail,
    ensure,
    ResultExt, // for `chain_err`,
};
use std::fs::File;
use std::io::Read;
use std::path::Path;

fn validate_metadata(path: &Path, metadata: &str) -> Result<()> {
    // `ensure` is like `assert` (or failure's ensure), except it allows you to
    // specify the `ErrorKind`.
    ensure!(
        !metadata.is_empty(),
        ErrorKind::CorruptMetadata(format!("metadata file {:?} is empty", path))
    );

    // a special mode of `ensure` works for functions that return `ErrorKind`-s
    // and take a single string as argument:
    ensure!(
        metadata.len() > 100,
        ErrorKind::CorruptMetadata, // Any FnOnce(String) -> ErrorKind
        "metadata file {:?} is too long", // Rest of arguments are format args.
        path,
    );

    // `bail` is like `ensure`, but without the condition and always returns
    // early.
    bail!(
        ErrorKind::CorruptMetadata,
        "validation isn't actually implemented"
    );
}

fn load(wad: &Path, metadata: &Path) -> Result<()> {
    // `chain_err` stashes the original error as the `cause` of a new error.
    let wad_file = File::open(wad).chain_err(|| ErrorKind::WadIo(wad.to_owned()))?;

    let mut metadata_content = String::new();

    // `chain_inspect_err` stashes the original error as the `cause` of the new
    // error, but it first allows the callback to inspect it.
    let metadata_file = File::open(metadata)
        .and_then(|mut file| file.read_to_string(&mut metadata_content))
        .chain_inspect_err(|_io_error| ErrorKind::MetadataIo(metadata.to_owned()))?;

    validate_metadata(metadata, &metadata_content)?;

    Ok(())
}

Future Plans

This library is a proof-of-concept and was developed as part of the error_chain -> failure migration of rust-doom and inspired by @burntsushi's error handling solution in imdb-rename.

The main issue with the current library is that failchain must be leaked as a public dependency via the Error type-alias. This would be easily solved with a custom derive which implements the necessary From and Fail impl-s e.g.

#[derive(ChainError)]
pub struct Error(BoxedError<ErrorKind>);


// expanding to
impl Fail for Error { /* delegate */ }
impl From<ErrorKind> for Error { /* delegate */ }

The error handling story in Rust is a little up in the air right now, but in my opinion any eventual solution should include these pieces (as error_chain did).

Versioning

failure is a public dependency of failchain. The library versioning scheme is <failchain_major><failure_version><failchain_minor>. So 0.1015.2 would be version 0.1.2 of failchain with a dependency on version 0.1.5 of failure.

failchain's People

Contributors

cristicbz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

isgasho icodein

failchain's Issues

Story with futures

It's real nice having result.chain_err(|| ...) for your results but then when using code involving futures you end up having to go back to

future.map_err(|e| -> MyError {
    let kind = ...;
    e.context(kind).into()
})

In order to get around this, I made a variant of ResultExt that works for futures (0.1 for now):

pub trait FutureExt<S, E: Fail>: Sized + Future<Item = S, Error = E> {
    fn chain_inspect_err_fut<ErrorKindT: ChainErrorKind>(
        self,
        fn_in: impl FnOnce(&mut E) -> ErrorKindT + Send + Sync + 'static,
    ) -> MapErr<Self, Box<dyn FnOnce(E) -> ErrorKindT::Error + Send + Sync>> {
        self.map_err(Box::new(|mut e| {
            let kind = fn_in(&mut e);
            e.context(kind).into()
        }))
    }

    fn chain_err_fut<ErrorKindT: ChainErrorKind>(
        self,
        fn_in: impl FnOnce() -> ErrorKindT + Send + Sync + 'static,
    ) -> MapErr<Self, Box<dyn FnOnce(E) -> ErrorKindT::Error + Send + Sync>> {
        self.chain_inspect_err_fut(|_| fn_in())
    }
}

impl<S, E: Fail, F: Future<Item = S, Error = E>> FutureExt<S, E> for F {}

// used like:
    client
        .request(request)
        .chain_err_fut(|| ErrorKind::OtherError("foo".to_owned()))

Some immediate issues:

  • I need Send + Sync + 'static and need to enforce them, maybe some use cases don't
  • Hard coded to futures-0.1, but I think we wouldn't be able to make it more general until HKT
  • Boxing because you can't use impl Fn in traits.

Is there any interest here? I'm using it and it seems handy. Maybe there's a better discussion to be had in failure's ResultExt which could have the same pattern?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.