macron_impl_into/
lib.rs

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