leftwm_macros/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5
6use quote::quote;
7use syn::{parse_macro_input, Data, DeriveInput, Error};
8
9macro_rules! derive_error {
10    ($string: tt) => {
11        Error::new(Span::call_site(), $string)
12            .to_compile_error()
13            .into()
14    };
15}
16
17fn parse_enum_doc_comment(attrs: &[syn::Attribute]) -> String {
18    let mut ret = String::new();
19    for attr in attrs {
20        let meta = &attr.meta;
21        if let syn::Meta::NameValue(meta) = meta {
22            if let syn::Expr::Lit(syn::ExprLit {
23                lit: syn::Lit::Str(l),
24                ..
25            }) = &meta.value
26            {
27                ret.push_str(&format!("\n    {}", l.value().trim()));
28            }
29        }
30    }
31
32    ret
33}
34
35#[proc_macro_derive(EnumDocs)]
36/// Returns a const str for the Enum to which it applies.
37///
38/// # Example:
39/// ```
40/// #[derive(leftwm_macros::EnumDocs)]
41/// enum LeftWm {
42///   One,
43///   /// Doc comment
44///   Two
45/// }
46///
47/// assert_eq!(LeftWm::documentation(), "\nOne\nTwo\n    Doc comment");
48/// ```
49///
50/// The purpose of this macro is for serializing options of the `BaseCommand` for `leftwm-command`
51pub fn derive_enum_docs(input: TokenStream) -> TokenStream {
52    let input: DeriveInput = parse_macro_input!(input);
53
54    match &input.data {
55        // Only if data is an enum, we do parsing
56        Data::Enum(data_enum) => {
57            // data_enum is of type syn::DataEnum
58            // https://doc.servo.org/syn/struct.DataEnum.html
59
60            let mut names = String::new();
61
62            // For each variant, push its name onto `names`
63            for variant in &data_enum.variants {
64                let doc = parse_enum_doc_comment(&variant.attrs);
65
66                names.push_str(&format!("\n{}{}", variant.ident, doc));
67            }
68
69            // The enum's name
70            let name = &input.ident;
71            let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
72            quote! {
73                impl #impl_generics #name #ty_generics #where_clause {
74                    pub const fn documentation() -> &'static str {
75                        #names
76                    }
77                }
78            }
79            .into()
80        }
81        _ => derive_error!("EnumDocs can only be implemented for enums"),
82    }
83}