swc_xml_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        parse_quote!({
55            impl<W> crate::Emit<#node_type> for crate::CodeGenerator<'_, W>
56                where W: crate::writer::XmlWriter,
57            {
58                fn emit(&mut self, n: &#node_type) -> crate::Result {
59                    self.#mtd_name(n)
60                }
61            }
62
63            #block
64
65            // Emitter methods return Result<_, _>
66            // We inject this to avoid writing Ok(()) every time.
67            #[allow(unreachable_code)]
68            {
69                return Ok(());
70            }
71        })
72    };
73
74    ImplItemFn { block, ..i }
75}