1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use proc_macro::TokenStream;
use quote::quote;
use syn::Ident;
use syn::{parse_macro_input, DeriveInput};

fn _print_type_of<T>(_: &T) {
    println!("{}", std::any::type_name::<T>())
}

#[proc_macro_derive(IntoDefault, attributes(IntoStruct, IntoType))]
pub fn into_default(item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as DeriveInput);
    let attributes = &ast.attrs;
    let base_type = &ast.ident;
    let mut code: TokenStream = TokenStream::from(quote! {});
    for attribute in attributes {
        if attribute.path.is_ident("IntoStruct") {
            let type_struct = attribute.parse_args::<Ident>().unwrap();
            if let syn::Data::Struct(d) = &ast.data {
                if let syn::Fields::Named(f) = &d.fields {
                    let fields = &f.named;
                    let mut fields_name = Vec::new();
                    let mut fields_value = Vec::new();
                    for field in fields {
                        let type_temp = &field.ident;
                        let mut quote_temp = quote! {item.#type_temp.into()};
                        fields_name.push(&field.ident);
                        if let syn::Type::Path(path) = &field.ty {
                            for segment in &path.path.segments {
                                if segment.ident == "Option" {
                                    quote_temp = quote! {match item.#type_temp {
                                        Some(value) => value.into(),
                                        None => Default::default()
                                    }};
                                } else if segment.ident == "Vec" {
                                    quote_temp = quote! {item.#type_temp.into_iter().map(|v| v.into()).collect::<Vec<_>>()};
                                }
                            }
                        }
                        fields_value.push(quote_temp);
                    }
                    code = TokenStream::from(quote! {
                        impl From<Option<#base_type>> for #type_struct {
                            fn from(item: Option<#base_type>) -> Self {
                               match item {
                                   Some(v) => v.into(),
                                   None => Default::default()
                               }
                            }
                        }

                        impl Into<Option<#type_struct>> for #base_type {
                            fn into(self) -> Option<#type_struct> {
                                Some(self.into())
                            }
                        }

                        impl Into<Vec<#type_struct>> for #base_type {
                            fn into(self) -> Vec<#type_struct> {
                                vec! {self.into()}
                            }
                        }

                        impl From<#base_type> for #type_struct {
                          fn from(item: #base_type) -> Self {
                             #type_struct {
                                #(#fields_name: #fields_value ,)*
                             }
                          }
                        }

                    });
                }
            }
        } else {
            TokenStream::from(quote! {});
        }
    }
    code
}