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("_").unwrap();
34                let segments = p.path.segments;
35
36                // remove the trailing (:: VariantName) to get the type pathPathS
37                let len = segments.len();
38                let ty: Punctuated<PathSegment, Colon> =
39                    segments.clone().into_iter().take(len - 1).collect();
40                let args = e
41                    .args
42                    .into_iter()
43                    .map(|a| {
44                        if a == underscore {
45                            quote! { Example::example() }
46                        } else {
47                            quote! { #a }
48                        }
49                    })
50                    .collect::<Vec<_>>();
51                return quote! {
52                    impl Example for #ty {
53                        fn example() -> Self {
54                            #segments ( #(#args),* )
55                        }
56                    }
57                }
58                .into();
59            }
60        }
61        Expr::Path(e) => {
62            // unit enum
63
64            let segments = e.path.segments;
65            // remove the trailing (:: VariantName) to get the type path
66            let len = segments.len();
67            let ty: Punctuated<PathSegment, Colon> =
68                segments.clone().into_iter().take(len - 1).collect();
69
70            return quote! {
71                impl Example for #ty {
72                    fn example() -> Self {
73                        #segments
74                    }
75                }
76            }
77            .into();
78        }
79        _ => {}
80    }
81    panic!("invalid input, should be struct or enum literal");
82}