1use fsmentry_core::FSMGenerator;
6use quote::ToTokens as _;
7use syn::parse_macro_input;
8
9#[proc_macro]
32pub fn dsl(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
33 let generator = parse_macro_input!(item with FSMGenerator::parse_dsl);
34 let codegen = generator.codegen();
35 #[cfg(feature = "svg")]
36 let codegen = svg::attach(codegen, &generator);
37 codegen.into_token_stream().into()
38}
39
40#[proc_macro]
54pub fn dot(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
55 let generator = parse_macro_input!(item with FSMGenerator::parse_dot);
56 let codegen = generator.codegen();
57 #[cfg(feature = "svg")]
58 let codegen = svg::attach(codegen, &generator);
59 codegen.into_token_stream().into()
60}
61
62#[cfg(feature = "svg")]
63mod svg {
64 use quote::ToTokens as _;
65 use std::{
66 io::Write as _,
67 process::{Command, Stdio},
68 };
69 use syn::parse_quote;
70
71 pub fn attach(mut file: syn::File, generator: &fsmentry_core::FSMGenerator) -> syn::File {
72 let Some(syn::Item::Mod(syn::ItemMod { attrs, .. })) = file.items.first_mut() else {
73 unreachable!("the code generates a module")
74 };
75 if let Some(svg) = render_dot(generator) {
76 let svg = format!("<div>{}</div>", svg);
77 if !attrs.is_empty() {
78 attrs.push(parse_quote!(#[doc = ""]))
79 }
80 attrs.push(parse_quote!(#[doc = #svg]))
81 }
82 file
83 }
84
85 fn render_dot(generator: &fsmentry_core::FSMGenerator) -> Option<String> {
86 let mut child = Command::new("dot")
87 .arg("-Tsvg")
88 .stdin(Stdio::piped())
89 .stderr(Stdio::inherit())
90 .stdout(Stdio::piped())
91 .spawn()
92 .ok()?;
93 child
94 .stdin
95 .take()
96 .unwrap()
97 .write_all(generator.dot().to_token_stream().to_string().as_bytes())
98 .ok()?;
99 let output = child.wait_with_output().ok()?;
100 match output.status.success() {
101 true => String::from_utf8(output.stdout).ok(),
102 false => None,
103 }
104 }
105}