1use proc_macro::TokenStream;
4use proc_macro2::TokenStream as TokenStream2;
5use quote::quote;
6
7#[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 syn::Data::Struct(_st) => {
27 quote! {
28 #(
29 #global_impls
30 )*
31 }.into()
32 },
33
34 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
47fn 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}