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
81
82
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, punctuated::Punctuated, token::Colon, Expr, PathSegment};

pub fn example_impl(input: TokenStream) -> TokenStream {
    let derive_input = parse_macro_input!(input as Expr);

    match derive_input {
        Expr::Struct(e) => {
            // struct/enum struct variant
            let ident = e.path;
            let fields = e.fields.into_iter().map(|f| {
                let member = f.member;
                if f.colon_token.is_some() {
                    let expr = f.expr;
                    quote! { #member: #expr }
                } else {
                    quote! { #member: Example::example() }
                }
            });
            return quote! {
                impl Example for #ident {
                    fn example() -> Self {
                        #ident { #(#fields),* }
                    }
                }
            }
            .into();
        }
        Expr::Call(e) => {
            if let Expr::Path(p) = *e.func {
                // tuple enum
                let underscore: Expr = syn::parse_str("_").unwrap();
                let segments = p.path.segments;

                // remove the trailing (:: VariantName) to get the type pathPathS
                let len = segments.len();
                let ty: Punctuated<PathSegment, Colon> =
                    segments.clone().into_iter().take(len - 1).collect();
                let args = e
                    .args
                    .into_iter()
                    .map(|a| {
                        if a == underscore {
                            quote! { Example::example() }
                        } else {
                            quote! { #a }
                        }
                    })
                    .collect::<Vec<_>>();
                return quote! {
                    impl Example for #ty {
                        fn example() -> Self {
                            #segments ( #(#args),* )
                        }
                    }
                }
                .into();
            }
        }
        Expr::Path(e) => {
            // unit enum

            let segments = e.path.segments;
            // remove the trailing (:: VariantName) to get the type path
            let len = segments.len();
            let ty: Punctuated<PathSegment, Colon> =
                segments.clone().into_iter().take(len - 1).collect();

            return quote! {
                impl Example for #ty {
                    fn example() -> Self {
                        #segments
                    }
                }
            }
            .into();
        }
        _ => {}
    }
    panic!("invalid input, should be struct or enum literal");
}