macron_impl_into/
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 Into
8#[proc_macro_derive(Into, attributes(into))]
9pub fn impl_into(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::Into<#ty> for #ident {
17                    fn into(self) -> #ty {
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            quote! {
37                #(
38                    #global_impls
39                )*
40            }.into()
41        },
42
43        _ => panic!("Expected a 'struct' or 'enum'")
44    }
45}
46
47// Reads an attribute values
48fn read_attr_values(attrs: &[syn::Attribute], fields: Option<&syn::Fields>) -> Vec<AttrValue> {
49    attrs
50        .iter()
51        .filter(|attr| attr.path().is_ident("into"))
52        .map(|attr| {
53            match &attr.meta {
54                syn::Meta::List(list) => {
55                    list
56                        .parse_args()
57                        .expect("Expected the attribute format like this '#[into(Type, \"a code..\")]'")
58                },
59
60                syn::Meta::Path(_) if fields.is_some() => {
61                    let fields = fields.unwrap();
62                    if fields.len() != 1 { panic!("Expected the one variant argument for the short attribute '#[into]'") }
63
64                    let field = fields.iter().next().unwrap();
65                    
66                    AttrValue {
67                        ty: field.ty.clone(),
68                        expr: syn::parse_str( &if let Some(ident) = &field.ident { format!("self.{ident}") }else{ format!("arg0")} ).unwrap()
69                    }
70                },
71                
72                _ => panic!("Expected the attribute format like this '#[into(Type, \"a code..\")]'")
73            }
74        })
75        .collect::<Vec<_>>()
76}
77
78struct AttrValue {
79    pub ty: syn::Type,
80    pub expr: TokenStream2
81}
82
83impl syn::parse::Parse for AttrValue {
84    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
85        let ty: syn::Type = input.parse()?;
86        
87        input.parse::<syn::token::Comma>()?;
88        
89        let expr_s: syn::LitStr = input.parse()?;
90        let expr: TokenStream2 = syn::parse_str(&expr_s.value())?;
91        
92        Ok(AttrValue {
93            ty,
94            expr,
95        })
96    }
97}