From 5ba86a4c09009386c29129157bca16bd6c342eba Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 29 Dec 2018 23:19:00 +0100 Subject: [PATCH 1/5] Added basic failure name --- examples/simple.rs | 4 ++-- failure_derive/src/lib.rs | 7 +++++++ src/error/mod.rs | 5 +++++ src/lib.rs | 8 ++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 35d25e1..fc39601 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -16,7 +16,7 @@ fn bad_function() -> Result<(), WrappingError> { } fn main() { - for cause in Fail::iter_causes(&bad_function().unwrap_err()) { - println!("{}", cause); + for cause in Fail::iter_chain(&bad_function().unwrap_err()) { + println!("{}: {}", cause.name().unwrap_or("Error"), cause); } } diff --git a/failure_derive/src/lib.rs b/failure_derive/src/lib.rs index 4c223eb..7df10d1 100644 --- a/failure_derive/src/lib.rs +++ b/failure_derive/src/lib.rs @@ -7,6 +7,7 @@ extern crate synstructure; extern crate quote; use proc_macro2::{TokenStream, Span}; +use syn::LitStr; use syn::spanned::Spanned; #[derive(Debug)] @@ -40,6 +41,8 @@ fn fail_derive_impl(s: synstructure::Structure) -> Result { quote! { & } }; + let ty_name = LitStr::new(&s.ast().ident.to_string(), Span::call_site()); + let cause_body = s.each_variant(|v| { if let Some(cause) = v.bindings().iter().find(is_cause) { quote!(return Some(::failure::AsFail::as_fail(#cause))) @@ -59,6 +62,10 @@ fn fail_derive_impl(s: synstructure::Structure) -> Result { let fail = s.unbound_impl( quote!(::failure::Fail), quote! { + fn name(&self) -> Option<&str> { + Some(concat!(module_path!(), "::", #ty_name)) + } + #[allow(unreachable_code)] fn cause(&self) -> ::failure::_core::option::Option<#make_dyn(::failure::Fail)> { match *self { #cause_body } diff --git a/src/error/mod.rs b/src/error/mod.rs index 30e69cc..152807b 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -70,6 +70,11 @@ impl Error { self.imp.failure() } + /// Returns the name of the underlying fail. + pub fn name(&self) -> Option<&str> { + self.as_fail().name() + } + /// Returns a reference to the underlying cause of this `Error`. Unlike the /// method on `Fail`, this does not return an `Option`. The `Error` type /// always has an underlying failure. diff --git a/src/lib.rs b/src/lib.rs index 498b814..a5d7068 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,14 @@ with_std! { /// `std::error::Error`, and are also `Send`, `Sync`, and `'static`, implement /// `Fail` by a blanket impl. pub trait Fail: Display + Debug + Send + Sync + 'static { + /// Returns the "name" of the error. + /// + /// This is typically the type name. Not all errors will implement + /// this. + fn name(&self) -> Option<&str> { + None + } + /// Returns a reference to the underlying cause of this failure, if it /// is an error that wraps other errors. /// From 62b45ef79708ba94714116b8fb60760ddb5122cc Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 29 Dec 2018 23:36:51 +0100 Subject: [PATCH 2/5] Updated name property --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a5d7068..e5b637b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -109,7 +109,8 @@ pub trait Fail: Display + Debug + Send + Sync + 'static { /// Returns the "name" of the error. /// /// This is typically the type name. Not all errors will implement - /// this. + /// this. It's expected to be most useful in situations where errors + /// need to be reported to external systems such as crash reporters. fn name(&self) -> Option<&str> { None } From f3b00d204f76de42e01390ca15e768c59f129182 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Sat, 29 Dec 2018 17:44:15 -0500 Subject: [PATCH 3/5] Phrasing nit --- src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e5b637b..02c239f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,9 +108,10 @@ with_std! { pub trait Fail: Display + Debug + Send + Sync + 'static { /// Returns the "name" of the error. /// - /// This is typically the type name. Not all errors will implement - /// this. It's expected to be most useful in situations where errors - /// need to be reported to external systems such as crash reporters. + /// This is typically the type name. Not all errors will implement + /// this. This method is expected to be most useful in situations + /// where errors need to be reported to external instrumentation systems + /// such as crash reporters. fn name(&self) -> Option<&str> { None } From d5b7bb55affc3c85b98c34948e6cb1deb0d81727 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 29 Dec 2018 23:45:32 +0100 Subject: [PATCH 4/5] Added name() to context --- src/context.rs | 4 ++++ src/error_message.rs | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/context.rs b/src/context.rs index 3316b76..9ab0cc4 100644 --- a/src/context.rs +++ b/src/context.rs @@ -108,6 +108,10 @@ with_std! { } impl Fail for Context { + fn name(&self) -> Option<&str> { + self.failure.as_cause().and_then(|x| x.name()) + } + fn cause(&self) -> Option<&Fail> { self.failure.as_cause() } diff --git a/src/error_message.rs b/src/error_message.rs index 01ff1ae..560d317 100644 --- a/src/error_message.rs +++ b/src/error_message.rs @@ -19,7 +19,11 @@ struct ErrorMessage { msg: D, } -impl Fail for ErrorMessage { } +impl Fail for ErrorMessage { + fn name(&self) -> Option<&str> { + Some("failure::ErrorMessage") + } +} impl Display for ErrorMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { From c88f3b8e1772defce556020162ade1a5174edca7 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 29 Dec 2018 23:54:04 +0100 Subject: [PATCH 5/5] Added basic test for names of failure --- tests/basic_fail.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/basic_fail.rs diff --git a/tests/basic_fail.rs b/tests/basic_fail.rs new file mode 100644 index 0000000..574886d --- /dev/null +++ b/tests/basic_fail.rs @@ -0,0 +1,21 @@ +#[macro_use] +extern crate failure; + +use failure::Fail; + +#[test] +fn test_name() { + #[derive(Fail, Debug)] + #[fail(display = "my error")] + struct MyError; + + let err = MyError; + + assert_eq!(err.to_string(), "my error"); + assert_eq!(err.name(), Some("basic_fail::MyError")); + + let ctx = err.context("whatever"); + + assert_eq!(ctx.to_string(), "whatever"); + assert_eq!(ctx.name(), Some("basic_fail::MyError")); +} \ No newline at end of file