Support Option<$source>
This commit is contained in:
parent
c2865642b0
commit
f18a2a605d
|
@ -3,7 +3,7 @@ use crate::valid;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote, quote_spanned};
|
use quote::{format_ident, quote, quote_spanned};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::{DeriveInput, Member, Result};
|
use syn::{DeriveInput, Member, PathArguments, Result, Type};
|
||||||
|
|
||||||
pub fn derive(node: &DeriveInput) -> Result<TokenStream> {
|
pub fn derive(node: &DeriveInput) -> Result<TokenStream> {
|
||||||
let input = Input::from_syn(node)?;
|
let input = Input::from_syn(node)?;
|
||||||
|
@ -18,8 +18,14 @@ fn impl_struct(input: Struct) -> TokenStream {
|
||||||
let ty = &input.ident;
|
let ty = &input.ident;
|
||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
||||||
let source_method = input.source_member().map(|source| {
|
let source_method = input.source_field().map(|source_field| {
|
||||||
let dyn_error = quote_spanned!(source.span()=> self.#source.as_dyn_error());
|
let source = &source_field.member;
|
||||||
|
let asref = if type_is_option(source_field.ty) {
|
||||||
|
Some(quote_spanned!(source.span()=> .as_ref()?))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let dyn_error = quote_spanned!(source.span()=> self.#source #asref.as_dyn_error());
|
||||||
quote! {
|
quote! {
|
||||||
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
|
fn source(&self) -> std::option::Option<&(dyn std::error::Error + 'static)> {
|
||||||
use thiserror::private::AsDynError;
|
use thiserror::private::AsDynError;
|
||||||
|
@ -30,11 +36,20 @@ fn impl_struct(input: Struct) -> TokenStream {
|
||||||
|
|
||||||
let backtrace_method = input.backtrace_field().map(|backtrace| {
|
let backtrace_method = input.backtrace_field().map(|backtrace| {
|
||||||
let backtrace = &backtrace.member;
|
let backtrace = &backtrace.member;
|
||||||
let body = if let Some(source) = input.source_member() {
|
let body = if let Some(source_field) = input.source_field() {
|
||||||
let dyn_error = quote_spanned!(source.span()=> self.#source.as_dyn_error());
|
let source = &source_field.member;
|
||||||
|
let source_backtrace = if type_is_option(source_field.ty) {
|
||||||
|
quote_spanned! {source.span()=>
|
||||||
|
self.#source.as_ref().and_then(|source| source.as_dyn_error().backtrace())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! {source.span()=>
|
||||||
|
self.#source.as_dyn_error().backtrace()
|
||||||
|
}
|
||||||
|
};
|
||||||
quote!({
|
quote!({
|
||||||
use thiserror::private::AsDynError;
|
use thiserror::private::AsDynError;
|
||||||
#dyn_error.backtrace().unwrap_or(&self.#backtrace)
|
#source_backtrace.unwrap_or(&self.#backtrace)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -77,9 +92,15 @@ fn impl_enum(input: Enum) -> TokenStream {
|
||||||
let source_method = if input.has_source() {
|
let source_method = if input.has_source() {
|
||||||
let arms = input.variants.iter().map(|variant| {
|
let arms = input.variants.iter().map(|variant| {
|
||||||
let ident = &variant.ident;
|
let ident = &variant.ident;
|
||||||
match variant.source_member() {
|
match variant.source_field() {
|
||||||
Some(source) => {
|
Some(source_field) => {
|
||||||
let dyn_error = quote_spanned!(source.span()=> source.as_dyn_error());
|
let source = &source_field.member;
|
||||||
|
let asref = if type_is_option(source_field.ty) {
|
||||||
|
Some(quote_spanned!(source.span()=> .as_ref()?))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let dyn_error = quote_spanned!(source.span()=> source #asref.as_dyn_error());
|
||||||
quote! {
|
quote! {
|
||||||
#ty::#ident {#source: source, ..} => std::option::Option::Some(#dyn_error),
|
#ty::#ident {#source: source, ..} => std::option::Option::Some(#dyn_error),
|
||||||
}
|
}
|
||||||
|
@ -104,10 +125,19 @@ fn impl_enum(input: Enum) -> TokenStream {
|
||||||
let backtrace_method = if input.has_backtrace() {
|
let backtrace_method = if input.has_backtrace() {
|
||||||
let arms = input.variants.iter().map(|variant| {
|
let arms = input.variants.iter().map(|variant| {
|
||||||
let ident = &variant.ident;
|
let ident = &variant.ident;
|
||||||
match (variant.backtrace_field(), variant.source_member()) {
|
match (variant.backtrace_field(), variant.source_field()) {
|
||||||
(Some(backtrace), Some(source)) if backtrace.attrs.backtrace.is_none() => {
|
(Some(backtrace), Some(source_field)) if backtrace.attrs.backtrace.is_none() => {
|
||||||
let backtrace = &backtrace.member;
|
let backtrace = &backtrace.member;
|
||||||
let dyn_error = quote_spanned!(source.span()=> source.as_dyn_error());
|
let source = &source_field.member;
|
||||||
|
let source_backtrace = if type_is_option(source_field.ty) {
|
||||||
|
quote_spanned! {source.span()=>
|
||||||
|
source.as_ref().and_then(|source| source.as_dyn_error().backtrace())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! {source.span()=>
|
||||||
|
source.as_dyn_error().backtrace()
|
||||||
|
}
|
||||||
|
};
|
||||||
quote! {
|
quote! {
|
||||||
#ty::#ident {
|
#ty::#ident {
|
||||||
#backtrace: backtrace,
|
#backtrace: backtrace,
|
||||||
|
@ -115,7 +145,7 @@ fn impl_enum(input: Enum) -> TokenStream {
|
||||||
..
|
..
|
||||||
} => std::option::Option::Some({
|
} => std::option::Option::Some({
|
||||||
use thiserror::private::AsDynError;
|
use thiserror::private::AsDynError;
|
||||||
#dyn_error.backtrace().unwrap_or(backtrace)
|
#source_backtrace.unwrap_or(backtrace)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,3 +222,20 @@ fn fields_pat(fields: &[Field]) -> TokenStream {
|
||||||
None => quote!({}),
|
None => quote!({}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_is_option(ty: &Type) -> bool {
|
||||||
|
let path = match ty {
|
||||||
|
Type::Path(ty) => &ty.path,
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let last = path.segments.last().unwrap();
|
||||||
|
if last.ident != "Option" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(bracketed) => bracketed.args.len() == 1,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@ use crate::ast::{Enum, Field, Struct, Variant};
|
||||||
use syn::{Member, Type};
|
use syn::{Member, Type};
|
||||||
|
|
||||||
impl Struct<'_> {
|
impl Struct<'_> {
|
||||||
pub(crate) fn source_member(&self) -> Option<&Member> {
|
pub(crate) fn source_field(&self) -> Option<&Field> {
|
||||||
source_member(&self.fields)
|
source_field(&self.fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn backtrace_field(&self) -> Option<&Field> {
|
pub(crate) fn backtrace_field(&self) -> Option<&Field> {
|
||||||
|
@ -15,7 +15,7 @@ impl Enum<'_> {
|
||||||
pub(crate) fn has_source(&self) -> bool {
|
pub(crate) fn has_source(&self) -> bool {
|
||||||
self.variants
|
self.variants
|
||||||
.iter()
|
.iter()
|
||||||
.any(|variant| variant.source_member().is_some())
|
.any(|variant| variant.source_field().is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn has_backtrace(&self) -> bool {
|
pub(crate) fn has_backtrace(&self) -> bool {
|
||||||
|
@ -34,8 +34,8 @@ impl Enum<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Variant<'_> {
|
impl Variant<'_> {
|
||||||
pub(crate) fn source_member(&self) -> Option<&Member> {
|
pub(crate) fn source_field(&self) -> Option<&Field> {
|
||||||
source_member(&self.fields)
|
source_field(&self.fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn backtrace_field(&self) -> Option<&Field> {
|
pub(crate) fn backtrace_field(&self) -> Option<&Field> {
|
||||||
|
@ -43,15 +43,15 @@ impl Variant<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source_member<'a>(fields: &'a [Field]) -> Option<&'a Member> {
|
fn source_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> {
|
||||||
for field in fields {
|
for field in fields {
|
||||||
if field.attrs.source.is_some() {
|
if field.attrs.source.is_some() {
|
||||||
return Some(&field.member);
|
return Some(&field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for field in fields {
|
for field in fields {
|
||||||
match &field.member {
|
match &field.member {
|
||||||
Member::Named(ident) if ident == "source" => return Some(&field.member),
|
Member::Named(ident) if ident == "source" => return Some(&field),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue