this_error_from_box/
lib.rs

1//! Procedural macro for automatic From implementation for #[from] Box<T>
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{Data, DeriveInput, Fields, parse_macro_input};
6
7#[proc_macro_attribute]
8pub fn this_error_from_box(_attr: TokenStream, item: TokenStream) -> TokenStream {
9    let input = parse_macro_input!(item as DeriveInput);
10    let enum_name = &input.ident;
11    let mut from_impls = Vec::new();
12
13    if let Data::Enum(data_enum) = &input.data {
14        for variant in &data_enum.variants {
15            if let Fields::Unnamed(fields) = &variant.fields {
16                if fields.unnamed.len() == 1 {
17                    let field = &fields.unnamed[0];
18                    let mut has_from = false;
19                    for attr in &field.attrs {
20                        if attr.path().is_ident("from") {
21                            has_from = true;
22                            break;
23                        }
24                    }
25                    if has_from {
26                        // Check if type is Box<T>
27                        if let syn::Type::Path(type_path) = &field.ty {
28                            let segments = &type_path.path.segments;
29                            // Check if the path is really std::boxed::Box or alloc::boxed::Box
30                            let is_std_box = {
31                                let segs: Vec<_> = type_path.path.segments.iter().map(|s| s.ident.to_string()).collect();
32                                (segs == ["std", "boxed", "Box"])
33                                    || (segs == ["alloc", "boxed", "Box"])
34                                    || (segs == ["Box"]) // fallback for just Box
35                            };
36                            if is_std_box {
37                                if let syn::PathArguments::AngleBracketed(args) = &segments.last().unwrap().arguments {
38                                    if args.args.len() == 1 {
39                                        if let syn::GenericArgument::Type(inner_ty) = &args.args[0] {
40                                            let variant_ident = &variant.ident;
41                                            from_impls.push(quote! {
42                                                impl ::std::convert::From<#inner_ty> for #enum_name {
43                                                    fn from(e: #inner_ty) -> Self {
44                                                        #enum_name::#variant_ident(::std::boxed::Box::new(e))
45                                                    }
46                                                }
47                                            });
48                                        }
49                                    }
50                                }
51                            }
52                        }
53                    }
54                }
55            }
56        }
57    }
58
59    let expanded = quote! {
60        #input
61        #(#from_impls)*
62    };
63    TokenStream::from(expanded)
64}