Error implementation for enums

This commit is contained in:
David Tolnay 2019-10-09 07:41:33 -07:00
parent 761ff1c708
commit 8e866cde57
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
2 changed files with 92 additions and 6 deletions

View File

@ -60,6 +60,85 @@ fn struct_error(input: &DeriveInput, data: &DataStruct) -> Result<TokenStream> {
})
}
fn enum_error(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
let ident = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let sources = data
.variants
.iter()
.map(|variant| match &variant.fields {
Fields::Named(fields) => source_member(&fields.named),
Fields::Unnamed(fields) => source_member(&fields.unnamed),
Fields::Unit => Ok(None),
})
.collect::<Result<Vec<_>>>()?;
let backtraces = data
.variants
.iter()
.map(|variant| match &variant.fields {
Fields::Named(fields) => backtrace_member(&fields.named),
Fields::Unnamed(fields) => backtrace_member(&fields.unnamed),
Fields::Unit => Ok(None),
})
.collect::<Result<Vec<_>>>()?;
let source_method = if sources.iter().any(Option::is_some) {
let arms = data.variants.iter().zip(sources).map(|(variant, source)| {
let ident = &variant.ident;
match source {
Some(source) => quote! {
Self::#ident {#source: source, ..} => std::option::Option::Some(source.as_dyn_error()),
},
None => quote! {
Self::#ident {..} => std::option::Option::None,
},
}
});
Some(quote! {
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
use thiserror::private::AsDynError;
match self {
#(#arms)*
}
}
})
} else {
None
};
let backtrace_method = if backtraces.iter().any(Option::is_some) {
let arms = data.variants.iter().zip(backtraces).map(|(variant, backtrace)| {
let ident = &variant.ident;
match backtrace {
Some(backtrace) => quote! {
Self::#ident {#backtrace: backtrace, ..} => std::option::Option::Some(backtrace),
},
None => quote! {
Self::#ident {..} => std::option::Option::None,
},
}
});
Some(quote! {
fn backtrace(&self) -> std::option::Option<&std::backtrace::Backtrace> {
match self {
#(#arms)*
}
}
})
} else {
None
};
Ok(quote! {
impl #impl_generics std::error::Error for #ident #ty_generics #where_clause {
#source_method
#backtrace_method
}
})
}
fn source_member<'a>(fields: impl IntoIterator<Item = &'a Field>) -> Result<Option<Member>> {
for (i, field) in fields.into_iter().enumerate() {
if attr::is_source(field)? {
@ -94,9 +173,3 @@ fn member(i: usize, ident: &Option<Ident>) -> Member {
None => Member::Unnamed(Index::from(i)),
}
}
fn enum_error(input: &DeriveInput, data: &DataEnum) -> Result<TokenStream> {
let _ = input;
let _ = data;
unimplemented!()
}

View File

@ -1,3 +1,5 @@
#![allow(dead_code)]
use std::fmt::{self, Display};
use std::io;
use thiserror::Error;
@ -36,8 +38,19 @@ struct WithAnyhow {
cause: anyhow::Error,
}
#[derive(Error, Debug)]
enum EnumError {
Braced {
#[source]
cause: io::Error,
},
Tuple(#[source] io::Error),
Unit,
}
unimplemented_display!(BracedError);
unimplemented_display!(TupleError);
unimplemented_display!(UnitError);
unimplemented_display!(WithSource);
unimplemented_display!(WithAnyhow);
unimplemented_display!(EnumError);