doc_macro/
example.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{Expr, PathSegment, parse_macro_input, punctuated::Punctuated, token::Colon};
4
5pub fn example_impl(input: TokenStream) -> TokenStream {
6    let derive_input = parse_macro_input!(input as Expr);
7
8    match derive_input {
9        Expr::Struct(e) => {
10            // struct/enum struct variant
11            let ident = e.path;
12            let fields = e.fields.into_iter().map(|f| {
13                let member = f.member;
14                if f.colon_token.is_some() {
15                    let expr = f.expr;
16                    quote! { #member: #expr }
17                } else {
18                    quote! { #member: Example::example() }
19                }
20            });
21            return quote! {
22                impl Example for #ident {
23                    fn example() -> Self {
24                        #ident { #(#fields),* }
25                    }
26                }
27            }
28            .into();
29        }
30        Expr::Call(e) => {
31            if let Expr::Path(p) = *e.func {
32                // tuple enum
33                let underscore: Expr = syn::parse_str("_")
34                    .expect("Failed to parse underscore literal - this should never happen");
35                let segments = p.path.segments;
36
37                // remove the trailing (:: VariantName) to get the type pathPathS
38                let len = segments.len();
39                let ty: Punctuated<PathSegment, Colon> =
40                    segments.clone().into_iter().take(len - 1).collect();
41                let args = e
42                    .args
43                    .into_iter()
44                    .map(|a| {
45                        if a == underscore {
46                            quote! { Example::example() }
47                        } else {
48                            quote! { #a }
49                        }
50                    })
51                    .collect::<Vec<_>>();
52                return quote! {
53                    impl Example for #ty {
54                        fn example() -> Self {
55                            #segments ( #(#args),* )
56                        }
57                    }
58                }
59                .into();
60            }
61        }
62        Expr::Path(e) => {
63            // unit enum
64
65            let segments = e.path.segments;
66            // remove the trailing (:: VariantName) to get the type path
67            let len = segments.len();
68            let ty: Punctuated<PathSegment, Colon> =
69                segments.clone().into_iter().take(len - 1).collect();
70
71            return quote! {
72                impl Example for #ty {
73                    fn example() -> Self {
74                        #segments
75                    }
76                }
77            }
78            .into();
79        }
80        _ => {}
81    }
82    panic!("invalid input, should be struct or enum literal");
83}