swc_css_codegen_macros/
lib.rs

1#![deny(clippy::all)]
2
3extern crate proc_macro;
4
5use quote::ToTokens;
6use syn::{parse_quote, FnArg, ImplItemFn, Type, TypeReference};
7
8#[proc_macro_attribute]
9pub fn emitter(
10    _attr: proc_macro::TokenStream,
11    item: proc_macro::TokenStream,
12) -> proc_macro::TokenStream {
13    let item: ImplItemFn = syn::parse(item).expect("failed to parse input as an item");
14    let item = expand(item);
15
16    item.into_token_stream().into()
17}
18
19fn expand(i: ImplItemFn) -> ImplItemFn {
20    let mtd_name = i.sig.ident.clone();
21    assert!(
22        format!("{}", i.sig.ident).starts_with("emit_"),
23        "#[emitter] methods should start with `emit_`"
24    );
25    let block = {
26        let node_type = {
27            i.sig
28                .inputs
29                .clone()
30                .into_iter()
31                .nth(1)
32                .and_then(|arg| match arg {
33                    FnArg::Typed(ty) => Some(ty.ty),
34                    _ => None,
35                })
36                .map(|ty| {
37                    // &Ident -> Ident
38                    match *ty {
39                        Type::Reference(TypeReference { elem, .. }) => *elem,
40                        _ => panic!(
41                            "Type of node parameter should be reference but got {}",
42                            ty.into_token_stream()
43                        ),
44                    }
45                })
46                .expect(
47                    "#[emitter] methods should have signature of
48fn (&mut self, node: Node) -> Result;
49    ",
50                )
51        };
52
53        let block = &i.block;
54
55        parse_quote!({
56            impl<W> crate::Emit<#node_type> for crate::CodeGenerator<W>
57            where
58                W: crate::writer::CssWriter,
59            {
60                fn emit(&mut self, n: &#node_type) -> crate::Result {
61                    self.#mtd_name(n)
62                }
63            }
64
65            #block
66
67            // Emitter methods return Result<_, _>
68            // We inject this to avoid writing Ok(()) every time.
69            #[allow(unreachable_code)]
70            {
71                return Ok(());
72            }
73        })
74    };
75
76    ImplItemFn { block, ..i }
77}