1extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use syn::{Attribute, DeriveInput, Error, Expr, ExprLit, Lit, Meta};
7
8const UNSUPPORTED: &str = r#"only `/// ...` or `#[doc = "..."]` are supported"#;
9
10fn filter_attr(attr: &Attribute) -> Option<Result<String, Error>> {
11 match &attr.meta {
12 Meta::NameValue(nv) if nv.path.is_ident("doc") => match &nv.value {
13 Expr::Lit(ExprLit {
14 lit: Lit::Str(s), ..
15 }) => Some(Ok(s.value())),
16 _ => Some(Err(Error::new_spanned(attr, UNSUPPORTED))),
17 },
18 _ => None,
19 }
20}
21
22fn inner(input: DeriveInput) -> Result<TokenStream, Error> {
23 let dox = input
25 .attrs
26 .iter()
27 .filter_map(filter_attr)
28 .collect::<Result<Vec<String>, _>>()?;
29
30 let name = &input.ident;
32 let generics = &input.generics;
33 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
34 Ok(From::from(quote::quote! {
35 impl #impl_generics Doxed for #name #ty_generics #where_clause {
36 const DOX: &'static [&'static str] = &[#(#dox),*];
37 }
38 }))
39}
40
41#[proc_macro_derive(Doxed)]
42pub fn derive_dox(input: TokenStream) -> TokenStream {
43 inner(syn::parse_macro_input!(input as DeriveInput))
44 .unwrap_or_else(|err| err.to_compile_error().into())
45}