Skip to content

Commit 1dba591

Browse files
author
Gaëtan Schwartz
committed
feat: add From trait implementations for boxed types
1 parent 470e6a6 commit 1dba591

3 files changed

Lines changed: 79 additions & 3 deletions

File tree

impl/src/expand.rs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,23 @@ fn impl_struct(input: Struct) -> TokenStream {
174174
#ty #body
175175
}
176176
};
177-
let from_impl = quote_spanned! {span=>
177+
let mut from_impl = quote_spanned! {span=>
178178
#[automatically_derived]
179179
impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
180180
#from_function
181181
}
182182
};
183+
if let Some(from) = type_parameter_of_box(from_field.ty) {
184+
let body = from_some_source(from_field, backtrace_field, quote!(::thiserror::__private::Box::new(#source_var)));
185+
from_impl.extend(quote_spanned! {span=>
186+
#[automatically_derived]
187+
impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
188+
fn from(#source_var: #from) -> Self {
189+
#ty #body
190+
}
191+
}
192+
});
193+
}
183194
Some(quote! {
184195
#[allow(
185196
deprecated,
@@ -449,12 +460,23 @@ fn impl_enum(input: Enum) -> TokenStream {
449460
#ty::#variant #body
450461
}
451462
};
452-
let from_impl = quote_spanned! {span=>
463+
let mut from_impl = quote_spanned! {span=>
453464
#[automatically_derived]
454465
impl #impl_generics ::core::convert::From<#from> for #ty #ty_generics #where_clause {
455466
#from_function
456467
}
457468
};
469+
if let Some(boxed) = type_parameter_of_box(from_field.ty) {
470+
let body = from_some_source(from_field, backtrace_field, quote!(::thiserror::__private::Box::new(#source_var)));
471+
from_impl.extend(quote_spanned! {span=>
472+
#[automatically_derived]
473+
impl #impl_generics ::core::convert::From<#boxed> for #ty #ty_generics #where_clause {
474+
fn from(#source_var: #boxed) -> Self {
475+
#ty::#variant #body
476+
}
477+
}
478+
});
479+
}
458480
Some(quote! {
459481
#[allow(
460482
deprecated,
@@ -523,12 +545,20 @@ fn from_initializer(
523545
backtrace_field: Option<&Field>,
524546
source_var: &Ident,
525547
) -> TokenStream {
526-
let from_member = &from_field.member;
527548
let some_source = if type_is_option(from_field.ty) {
528549
quote!(::core::option::Option::Some(#source_var))
529550
} else {
530551
quote!(#source_var)
531552
};
553+
from_some_source(from_field, backtrace_field, some_source)
554+
}
555+
556+
fn from_some_source(
557+
from_field: &Field,
558+
backtrace_field: Option<&Field<'_>>,
559+
some_source: TokenStream,
560+
) -> TokenStream {
561+
let from_member = &from_field.member;
532562
let backtrace = backtrace_field.map(|backtrace_field| {
533563
let backtrace_member = &backtrace_field.member;
534564
if type_is_option(backtrace_field.ty) {
@@ -581,3 +611,29 @@ fn type_parameter_of_option(ty: &Type) -> Option<&Type> {
581611
_ => None,
582612
}
583613
}
614+
615+
fn type_parameter_of_box(ty: &Type) -> Option<&Type> {
616+
let path = match ty {
617+
Type::Path(ty) => &ty.path,
618+
_ => return None,
619+
};
620+
621+
let last = path.segments.last().unwrap();
622+
if last.ident != "Box" {
623+
return None;
624+
}
625+
626+
let bracketed = match &last.arguments {
627+
PathArguments::AngleBracketed(bracketed) => bracketed,
628+
_ => return None,
629+
};
630+
631+
if bracketed.args.len() != 1 {
632+
return None;
633+
}
634+
635+
match &bracketed.args[0] {
636+
GenericArgument::Type(arg) => Some(arg),
637+
_ => None,
638+
}
639+
}

src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@
273273
#[cfg(all(thiserror_nightly_testing, not(error_generic_member_access)))]
274274
compile_error!("Build script probe failed to compile.");
275275

276+
#[cfg(not(feature = "std"))]
277+
extern crate alloc;
276278
#[cfg(feature = "std")]
277279
extern crate std;
278280
#[cfg(feature = "std")]
@@ -299,8 +301,14 @@ pub mod __private {
299301
#[doc(hidden)]
300302
pub use crate::var::Var;
301303
#[doc(hidden)]
304+
#[cfg(not(feature = "std"))]
305+
pub use alloc::boxed::Box;
306+
#[doc(hidden)]
302307
pub use core::error::Error;
303308
#[cfg(all(feature = "std", not(thiserror_no_backtrace_type)))]
304309
#[doc(hidden)]
305310
pub use std::backtrace::Backtrace;
311+
#[doc(hidden)]
312+
#[cfg(feature = "std")]
313+
pub use std::boxed::Box;
306314
}

tests/test_from.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ pub enum ErrorEnumOptional {
4343
},
4444
}
4545

46+
#[derive(Error, Debug)]
47+
#[error("...")]
48+
pub enum ErrorEnumBox {
49+
Test {
50+
#[from]
51+
source: Box<io::Error>,
52+
},
53+
}
54+
4655
#[derive(Error, Debug)]
4756
#[error("...")]
4857
pub enum Many {
@@ -51,6 +60,7 @@ pub enum Many {
5160
}
5261

5362
fn assert_impl<T: From<io::Error>>() {}
63+
fn assert_impl_box<T: From<Box<io::Error>>>() {}
5464

5565
#[test]
5666
fn test_from() {
@@ -60,5 +70,7 @@ fn test_from() {
6070
assert_impl::<ErrorTupleOptional>();
6171
assert_impl::<ErrorEnum>();
6272
assert_impl::<ErrorEnumOptional>();
73+
assert_impl::<ErrorEnumBox>();
74+
assert_impl_box::<ErrorEnumBox>();
6375
assert_impl::<Many>();
6476
}

0 commit comments

Comments
 (0)