doc_macro/example.rs
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");
}