doxed_derive/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2
3extern 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    // Extract all the doc attributes from the input.
24    let dox = input
25        .attrs
26        .iter()
27        .filter_map(filter_attr)
28        .collect::<Result<Vec<String>, _>>()?;
29
30    // Generate the implementation.
31    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}