1
2extern crate proc_macro;
3use proc_macro::{TokenStream};
4use quote::{quote, ToTokens};
5use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprPath, Lit, Meta, Type, TypePath};
6
7#[proc_macro_derive(Constructor, attributes(init, new, from))]
8pub fn constructor_derive(input: TokenStream) -> TokenStream {
9 let input = parse_macro_input!(input as DeriveInput);
10 let struct_name = input.ident;
11 let generics = input.generics;
12
13 let fields = match input.data {
14 Data::Struct(data_struct) => data_struct.fields,
15 _ => panic!("Error by get struct fields")
16 };
17
18 let mut args = vec![];
19 let mut inits = vec![];
20 let mut assignments = vec![];
21
22 for field in fields.iter() {
23 let field_name = field.ident.clone().unwrap();
24 let field_type = field.ty.clone();
25
26 let mut default_value: Option<Value> = None;
27
28
29
30 for attr in &field.attrs {
31 if attr.path().is_ident("init") {
32 if let Meta::List(meta_list) = &attr.meta {
33 let inner_token_stream = TokenStream::from(meta_list.tokens.clone());
34 if let Some(lit) = get_lit_from_token_stream(inner_token_stream) {
35 default_value = Some(Value::Lit {lit})
36 }
37 if let Ok(ident) = syn::parse::<syn::Ident>(TokenStream::from(meta_list.tokens.clone())) {
38 default_value = match ident.to_string().as_str() {
39 "default" => Some(Value::TokenStream {token_stream: quote! { #field_type::default() }.into() } ),
40 "as_str" => {
41 args.push(quote! {#field_name: &str});
42 Some(Value::TokenStream {token_stream: quote! { #field_type::from(#field_name) }.into() })
43 },
44 _ => panic!("Unknown ident")
45 }
46 }
47 }
48
49 }
50 if attr.path().is_ident("new") { get_initializers(&attr.meta, &mut args, &field_type, &mut default_value, Func::New) }
51 if attr.path().is_ident("from") { get_initializers(&attr.meta, &mut args, &field_type, &mut default_value, Func::From) }
52 }
53
54 if let Some(value) = default_value {
55 match value {
56 Value::Lit { lit }=> {
57 if let Lit::Str(..) = &lit {
58 inits.push(quote! {#field_name: #lit.into()});
59 }
60 else {
61 inits.push(quote! {#field_name: #lit});
62 }
63 }
64 Value::TokenStream { token_stream } => {
65 inits.push(quote! {#field_name: #token_stream});
66 }
67 }
68
69
70 }
71 else {
72 args.push(quote! {#field_name: #field_type});
73 assignments.push(quote! {#field_name})
74 }
75 }
76
77
78 let constructor = quote! {
79 impl #generics #struct_name #generics {
80 pub fn new(#(#args,)*) -> Self {
81 Self {
82 #(#assignments,)*
83 #(#inits,)*
84 }
85 }
86 }
87 };
88 constructor.into()
89}
90
91fn get_lit_from_token_stream(token_stream: TokenStream) -> Option<Lit> {
92 if let Ok(lit) = syn::parse::<Lit>(token_stream) {
93 return Some(lit)
94 }
95 None
96}
97
98fn get_initializers(meta: &Meta, args: &mut Vec<proc_macro2::TokenStream>, field_type: &Type, default_value: &mut Option<Value>, func: Func) {
99 if let Meta::List(meta_list) = meta {
100 let mut new_args: Vec<proc_macro2::TokenStream> = vec![];
101 let inner_token_stream = TokenStream::from(meta_list.tokens.clone());
102 if let Some(lit) = get_lit_from_token_stream(inner_token_stream) {
103 if let Lit::Str(..) = &lit {
104 new_args.push(quote! { #lit.into() }.into());
105 }
106 else {
107 new_args.push(quote! { #lit }.into());
108 }
109
110 }
111 if let Ok(field) = syn::parse::<syn::FieldValue>(TokenStream::from(meta_list.tokens.clone())) {
112 let member = field.member.to_token_stream();
113
114 if let Expr::Path(ExprPath {path, ..}) = field.expr {
115 let path_seg = &path.segments[0];
116 let ident = &path_seg.ident;
117 new_args.push(quote! { #member }.into());
118 args.push(quote! { #member: #ident }.into());
119 }
120
121 }
122 if let Type::Path(TypePath{path, ..}) = &field_type {
123 let path_seg = &path.segments[0];
124 let ident = &path_seg.ident;
125 match func {
126 Func::New => *default_value = Some(Value::TokenStream {token_stream: quote! { #ident::new(#(#new_args),*) }.into() }),
127 Func::From => *default_value = Some(Value::TokenStream {token_stream: quote! { #ident::from(#(#new_args),*) }.into() }),
128
129 }
130 }
131 }
132}
133
134enum Value {
135 Lit{lit: Lit},
136 TokenStream{token_stream: proc_macro2::TokenStream}
137}
138
139enum Func {
140 New,
141 From
142}