Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Exception and related helpers. #26

Merged
merged 1 commit into from
Nov 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 12 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,18 @@
# Der Fehler

Der Fehler is a small but very opinionated Rust error handling library.

In many ways, der Fehler is a successor to failure. However, unlike failure,
der Fehler is built around the standard library's `Error` trait, which has
adopted the necessary improvements that the `Fail` trait had provided thanks to
[RFC 2504](https://github.com/rust-lang/rfcs/blob/master/text/2504-fix-error.md).

Der Fehler provocatively and unapologetically uses the terminology of
exceptions.
Der Fehler is a library to add support for "throwing functions" to Rust through
procedural macros. Functions marked with the `throws` attribute return
`Result`, but the "Ok" path is used by default and you don't need to wrap ok
return values in `Ok`. To throw errors, use `?` or the `throws` macro.

Der Fehler provides these items:

### The `Exception` type

Exception is a polymorphic error type, essentially a trait object. It is
similar to the `failure::Error` type, but for the `Error` trait in std. There
are a few key improvements:

* Like `failure::Error` is possible to construct an ad hoc error from any type
that implements `Debug` and `Display` (such as a string). Unlike
`failure::Error`, it can be properly downcast to that type.
* Unlike `failure::Error`, `Exception` is guaranteed to be the size of a single
narrow pointer (`failure::Error` is the size of a wide pointer).

Otherwise, it is roughly the same type: an Error trait object that guarantees
the presence of a backtrace.

### The `throw!` macro

`throw!` is a macro which is equivalent to the `Err($e)?` pattern. It takes an
error type and "throws" it.

### The `#[throws]` attribute

The throws attribute modifies a function or method to make it return a
`Result`. It takes an optional typename as an argument to the attribute which
will be the error type of this function; if no typename is supplied, the error
type is `Exception`.
will be the error type of this function; if no typename is supplied, it uses
the default error type for this crate.

Within the function body, `return`s (including the implicit final return) are
automatically "Ok-wrapped." To raise errors, use `?` or the `throws!` macro.
Expand All @@ -63,19 +38,17 @@ fn bar(x: bool) -> Result<i32, i32> {
}
```

### The `error!` macro

This macro constructs an ad hoc error from format strings, similar to the
`format!` macro.
### The `throw!` macro

### The `Context` trait
`throw!` is a macro which is equivalent to the `Err($e)?` pattern. It takes an
error type and "throws" it.

This crate also defines a `Context` trait for the `Result` type, which
contains a `context` method for injecting context around an error.
One important aspect of the `throw!` macro is that it allows you to return
errors inside of functions marked with `throws`. You cannot just `return`
errors from these functions, you need to use this macro.

# TODO

* Possibly add a Display derive
* Make throws work on closures and async blocks (attributes are not allowed on
expressions on stable)

Expand Down
14 changes: 10 additions & 4 deletions examples/throwing-main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
use fehler::*;

#[throws]
#[derive(Debug)]
struct Error;

#[throws(_)]
fn do_it() -> i32 {
throw!(error!("oops, an error occurred"));
if true {
throw!(Error);
}

0
}

#[throws]
#[throws(_)]
fn main() {
do_it().context("do it failed")?;
do_it()?;
}
80 changes: 0 additions & 80 deletions fehler-macros/src/error.rs

This file was deleted.

10 changes: 0 additions & 10 deletions fehler-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
extern crate proc_macro;

mod error;
mod throws;

use proc_macro::*;

/*
#[proc_macro_derive(Error)]
pub fn derive_error(input: TokenStream) -> TokenStream {
crate::error::entry(input)
}
*/

synstructure::decl_derive!([Error, attributes(error)] => crate::error::entry);

#[proc_macro_attribute]
pub fn throws(args: TokenStream, input: TokenStream) -> TokenStream {
crate::throws::entry(args, input)
Expand Down
29 changes: 15 additions & 14 deletions fehler-macros/src/throws.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ struct Throws {
}

pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
let ty = if args.is_empty() {
syn::parse2(quote::quote!(::fehler::Exception)).unwrap()
} else {
syn::parse(args).unwrap_or_else(|_| {
panic!("argument to #[throws] attribute must be a type")
})
};
let mut ty = syn::parse(args).unwrap_or_else(|_| {
panic!("argument to #[throws] attribute must be a type")
});

if let syn::Type::Infer(_) = ty {
ty = syn::parse2(quote::quote!(crate::Error)).unwrap();
}

let mut throws = Throws { ty, outer_fn: true };

if let Ok(item_fn) = syn::parse(input.clone()) {
Expand Down Expand Up @@ -93,19 +94,19 @@ impl Fold for Throws {
let error = &self.ty;
match i {
syn::ReturnType::Default => {
syn::parse2(quote::quote!(-> ::std::result::Result<(), #error>)).unwrap()
syn::parse2(quote::quote!(-> ::core::result::Result<(), #error>)).unwrap()
}
syn::ReturnType::Type(arrow, ty) => {
let result = syn::parse2(quote::quote!(::std::result::Result<#ty, #error>)).unwrap();
let result = syn::parse2(quote::quote!(::core::result::Result<#ty, #error>)).unwrap();
syn::ReturnType::Type(arrow, result)
}
}
}

fn fold_expr_return(&mut self, i: syn::ExprReturn) -> syn::ExprReturn {
let ok = match &i.expr {
Some(expr) => syn::parse2(quote::quote!(::std::result::Result::Ok(#expr))).unwrap(),
None => syn::parse2(quote::quote!(::std::result::Result::Ok(()))).unwrap(),
Some(expr) => syn::parse2(quote::quote!(::core::result::Result::Ok(#expr))).unwrap(),
None => syn::parse2(quote::quote!(::core::result::Result::Ok(()))).unwrap(),
};
syn::ExprReturn { expr: Some(Box::new(ok)), ..i }
}
Expand All @@ -120,13 +121,13 @@ fn modify_tail(is_unit_fn: bool, stmts: &mut Vec<syn::Stmt>) {
let new = syn::parse2(quote::quote!(#e;)).unwrap();
stmts.pop();
stmts.push(new);
stmts.push(syn::Stmt::Expr(syn::parse2(quote::quote!(::std::result::Result::Ok(()))).unwrap()));
stmts.push(syn::Stmt::Expr(syn::parse2(quote::quote!(::core::result::Result::Ok(()))).unwrap()));
}
Some(syn::Stmt::Expr(e)) => {
*e = syn::parse2(quote::quote!(::std::result::Result::Ok(#e))).unwrap();
*e = syn::parse2(quote::quote!(::core::result::Result::Ok(#e))).unwrap();
}
_ if is_unit_fn => {
stmts.push(syn::Stmt::Expr(syn::parse2(quote::quote!(::std::result::Result::Ok(()))).unwrap()));
stmts.push(syn::Stmt::Expr(syn::parse2(quote::quote!(::core::result::Result::Ok(()))).unwrap()));
}
_ => { }
}
Expand Down
27 changes: 0 additions & 27 deletions src/as_error.rs

This file was deleted.

87 changes: 0 additions & 87 deletions src/context.rs

This file was deleted.

Loading