1use std::mem;
2
3use base64::{prelude::BASE64_STANDARD as B64_ENGINE, Engine};
4use macro_railroad::{diagram, lowering, parser};
5use proc_macro::TokenStream;
6use proc_macro2::Span;
7use quote::{quote, ToTokens};
8use syn::{parse_macro_input, parse_quote, ItemMacro, LitStr};
9
10#[proc_macro_attribute]
14pub fn gen_rr_diag(args: TokenStream, macro_item: TokenStream) -> TokenStream {
15 let diag_ref_key = parse_macro_input!(args as LitStr).value();
16 let mut cifg_macro = parse_macro_input!(macro_item as ItemMacro);
17
18 let mut attrs = mem::take(&mut cifg_macro.attrs);
20
21 let mut parsed_macro = parser::parse(&cifg_macro.to_token_stream().to_string())
23 .map(lowering::MacroRules::from)
24 .expect("Failed to parse macro_rules.");
25 parsed_macro.foldcommontails();
26 parsed_macro.normalize();
27
28 const WITH_LEGEND: bool = true;
29
30 let mut macro_diag = diagram::into_diagram(parsed_macro, WITH_LEGEND);
32 macro_diag.add_default_css();
34 diagram::add_default_css(&mut macro_diag);
36
37 let base64_diag = B64_ENGINE.encode(macro_diag.to_string());
39 let base64_md_ref = format!("[{diag_ref_key}]: data:image/svg+xml;base64,{base64_diag}");
40 let doc_str = LitStr::new(&base64_md_ref, Span::mixed_site());
41
42 attrs.push(parse_quote! {
44 #[doc = #doc_str]
45 });
46 cifg_macro.attrs = attrs;
47
48 quote!(#cifg_macro).into()
49}