macron_impl_from/
lib.rs

1//! See the documentation here [macron documentation](https://docs.rs/macron)
2
3use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6
7/// The implementation of trait From
8#[proc_macro_derive(From, attributes(from))]
9pub fn impl_from(input: TokenStream) -> TokenStream {
10    let syn::DeriveInput { ident, data, attrs, .. } = syn::parse_macro_input!(input as syn::DeriveInput);
11    
12    let global_impls = read_attr_values(&attrs, None)
13        .into_iter()
14        .map(|AttrValue { ty, expr }| {
15            quote! {
16                impl ::std::convert::From<#ty> for #ident {
17                    fn from(value: #ty) -> Self {
18                        #expr
19                    }
20                }
21            }
22        });
23    
24    match data {
25        // impl Struct:
26        syn::Data::Struct(_st) => {
27            quote! {
28                #(
29                    #global_impls
30                )*
31            }.into()
32        },
33
34        // impl Enum:
35        syn::Data::Enum(en) => {
36            let var_impls = en.variants
37                .iter()
38                .map(|syn::Variant { ident: var_ident, attrs, fields, .. }| {
39                    let var_impls = read_attr_values(&attrs, Some(&fields))
40                        .into_iter()
41                        .map(|AttrValue { ty, expr }| {
42                            let output = match &fields {
43                                syn::Fields::Named(_) => quote! { Self::#var_ident { #expr } },
44                                syn::Fields::Unnamed(_) => quote! { Self::#var_ident(#expr) },
45                                syn::Fields::Unit => quote! { Self::#var_ident }
46                            };
47                            
48                            quote! {
49                                impl ::std::convert::From<#ty> for #ident {
50                                    fn from(value: #ty) -> Self {
51                                        #output
52                                    }
53                                }
54                            }
55                        });
56
57                    quote! { #(#var_impls)* }
58                });
59            
60            quote! {
61                #(
62                    #global_impls
63                )*
64
65                #(
66                    #var_impls
67                )*
68            }.into()
69        },
70
71        _ => panic!("Expected a 'struct' or 'enum'")
72    }
73}
74
75// Reads an attribute values
76fn read_attr_values(attrs: &[syn::Attribute], fields: Option<&syn::Fields>) -> Vec<AttrValue> {
77    attrs
78        .iter()
79        .filter(|attr| attr.path().is_ident("from"))
80        .map(|attr| {
81            match &attr.meta {
82                syn::Meta::List(list) => {
83                    list
84                        .parse_args()
85                        .expect("Expected the attribute format like this '#[from(Type, \"a code..\")]'")
86                },
87
88                syn::Meta::Path(_) if fields.is_some() => {
89                    let fields = fields.unwrap();
90                    if fields.len() != 1 { panic!("Expected the one variant argument for the short attribute '#[from]'") }
91
92                    let field = fields.iter().next().unwrap();
93                    
94                    AttrValue {
95                        ty: field.ty.clone(),
96                        expr: syn::parse_str( &if let Some(ident) = &field.ident { format!("{ident}: value") }else{ format!("value")} ).unwrap()
97                    }
98                },
99                
100                _ => panic!("Expected the attribute format like this '#[from(Type, \"a code..\")]'")
101            }
102        })
103        .collect::<Vec<_>>()
104}
105
106struct AttrValue {
107    pub ty: syn::Type,
108    pub expr: TokenStream2
109}
110
111impl syn::parse::Parse for AttrValue {
112    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
113        let ty: syn::Type = input.parse()?;
114        
115        input.parse::<syn::token::Comma>()?;
116        
117        let expr_s: syn::LitStr = input.parse()?;
118        let expr: TokenStream2 = syn::parse_str(&expr_s.value())?;
119        
120        Ok(AttrValue {
121            ty,
122            expr,
123        })
124    }
125}