diff --git a/impl/src/expand.rs b/impl/src/expand.rs index 94b5592..b71e7c4 100644 --- a/impl/src/expand.rs +++ b/impl/src/expand.rs @@ -90,12 +90,13 @@ fn impl_struct(input: Struct) -> TokenStream { }); let from_impl = input.from_field().map(|from_field| { - let member = &from_field.member; + let backtrace_field = input.backtrace_field(); let from = from_field.ty; + let body = from_initializer(from_field, backtrace_field); quote! { impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { fn from(source: #from) -> Self { - #ty { #member: source } + #ty #body } } } @@ -243,13 +244,14 @@ fn impl_enum(input: Enum) -> TokenStream { let from_impls = input.variants.iter().filter_map(|variant| { let from_field = variant.from_field()?; + let backtrace_field = variant.backtrace_field(); let variant = &variant.ident; - let member = &from_field.member; let from = from_field.ty; + let body = from_initializer(from_field, backtrace_field); Some(quote! { impl #impl_generics std::convert::From<#from> for #ty #ty_generics #where_clause { fn from(source: #from) -> Self { - #ty::#variant { #member: source } + #ty::#variant #body } } }) @@ -280,6 +282,26 @@ fn fields_pat(fields: &[Field]) -> TokenStream { } } +fn from_initializer(from_field: &Field, backtrace_field: Option<&Field>) -> TokenStream { + let from_member = &from_field.member; + let backtrace = backtrace_field.map(|backtrace_field| { + let backtrace_member = &backtrace_field.member; + if type_is_option(backtrace_field.ty) { + quote! { + #backtrace_member: std::option::Option::Some(std::backtrace::Backtrace::capture()), + } + } else { + quote! { + #backtrace_member: std::backtrace::Backtrace::capture(), + } + } + }); + quote!({ + #from_member: source, + #backtrace + }) +} + fn type_is_option(ty: &Type) -> bool { let path = match ty { Type::Path(ty) => &ty.path, diff --git a/impl/src/prop.rs b/impl/src/prop.rs index 3e40539..940b4f8 100644 --- a/impl/src/prop.rs +++ b/impl/src/prop.rs @@ -51,6 +51,12 @@ impl Variant<'_> { } } +impl Field<'_> { + pub(crate) fn is_backtrace(&self) -> bool { + type_is_backtrace(self.ty) + } +} + fn from_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { for field in fields { if field.attrs.from.is_some() { @@ -82,7 +88,7 @@ fn backtrace_field<'a, 'b>(fields: &'a [Field<'b>]) -> Option<&'a Field<'b>> { } } for field in fields { - if type_is_backtrace(field.ty) { + if field.is_backtrace() { return Some(&field); } } diff --git a/impl/src/valid.rs b/impl/src/valid.rs index d52882d..7b6750c 100644 --- a/impl/src/valid.rs +++ b/impl/src/valid.rs @@ -104,6 +104,7 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> { let mut from_field = None; let mut source_field = None; let mut backtrace_field = None; + let mut has_backtrace = false; for field in fields { if let Some(from) = field.attrs.from { if from_field.is_some() { @@ -125,7 +126,9 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> { )); } backtrace_field = Some(field); + has_backtrace = true; } + has_backtrace |= field.is_backtrace(); } if let (Some(from_field), Some(source_field)) = (from_field, source_field) { if !same_member(from_field, source_field) { @@ -136,10 +139,10 @@ fn check_field_attrs(fields: &[Field]) -> Result<()> { } } if let Some(from_field) = from_field { - if fields.len() > 1 { + if fields.len() > 1 + has_backtrace as usize { return Err(Error::new_spanned( from_field.attrs.from, - "deriving From requires no fields other than source", + "deriving From requires no fields other than source and backtrace", )); } }