1extern crate proc_macro2;
2
3mod node;
4mod nodes_to_format;
5#[cfg(feature = "compile-time-svgtree")]
6mod nodes_to_svgtree;
7mod parser;
8
9use proc_macro::TokenStream;
10use proc_macro2::Span;
11use quote::quote;
12use syn::Ident;
13
14use syn::{
15 parse::{ParseStream, Parser as _},
16 Result,
17};
18
19use node::Node;
20use parser::{Parser, ParserOptions};
21
22mod punctuation {
23 use syn::custom_punctuation;
24
25 custom_punctuation!(Dash, -);
26}
27
28struct ParseOutput {
29 nodes: Vec<Node>,
30 animations: Vec<proc_macro2::TokenStream>,
31}
32
33#[cfg(feature = "compile-time-svgtree")]
34fn create_svgr_ident(
35 fframes_crate_ident: &Ident,
36 nodes: Vec<Node>,
37) -> syn::Result<proc_macro2::TokenStream> {
38 let svg_tree = crate::nodes_to_svgtree::nodes_to_svgtree(&nodes, fframes_crate_ident)?;
39 let (html_string, values) =
40 crate::nodes_to_format::prepare_svg_nodes_for_format_statement(nodes, fframes_crate_ident);
41
42 Ok(quote! {
43 #fframes_crate_ident::Svgr {
44 #[cfg(not(target_arch="wasm32"))]
45 svg_tree: #svg_tree,
46 #[cfg(target_arch="wasm32")]
47 value: format!(#html_string, #(#values),*),
48 #[cfg(target_arch="wasm32")]
49 marker: std::marker::PhantomData,
50 }
51 })
52}
53
54#[cfg(not(feature = "compile-time-svgtree"))]
55fn create_svgr_ident(
56 fframes_crate_ident: &Ident,
57 nodes: Vec<Node>,
58) -> syn::Result<proc_macro2::TokenStream> {
59 let (html_string, values) =
60 crate::nodes_to_format::prepare_svg_nodes_for_format_statement(nodes, &fframes_crate_ident);
61
62 Ok(quote! {
63 #fframes_crate_ident::Svgr {
64 value: format!(#html_string, #(#values),*),
65 marker: std::marker::PhantomData,
66 }
67 })
68}
69
70fn parse(tokens: proc_macro::TokenStream, fframes_crate_ident: &Ident) -> Result<ParseOutput> {
71 let parser = move |input: ParseStream| {
72 Parser::new(ParserOptions::default(), fframes_crate_ident).parse(input)
73 };
74
75 let (nodes, animations) = parser.parse(tokens)?;
76
77 Ok(ParseOutput { nodes, animations })
78}
79
80#[proc_macro]
81pub fn svgr(tokens: TokenStream) -> TokenStream {
82 let fframes_crate_ident = match proc_macro_crate::crate_name("fframes")
83 .expect("fframes crate must be present in Cargo.toml")
84 {
85 proc_macro_crate::FoundCrate::Itself => Ident::new("crate", Span::call_site()),
86 proc_macro_crate::FoundCrate::Name(name) => Ident::new(&name, Span::call_site()),
87 };
88
89 let parse_result =
90 parse(tokens, &fframes_crate_ident).and_then(|ParseOutput { nodes, animations }| {
91 let svgr_ident = create_svgr_ident(&fframes_crate_ident, nodes)?;
92
93 Ok(quote! {{
94 use #fframes_crate_ident::usvgr::svgtree::macro_prelude::*;
95
96 #fframes_crate_ident::lazy_static::lazy_static! { #(#animations)* }
97
98 #[allow(unused_braces)]
99 #[allow(clippy::approx_constant)]
100 #svgr_ident
101 }})
102 });
103
104 match parse_result {
105 Ok(tokens) => tokens,
106 Err(err) => err.to_compile_error(),
107 }
108 .into()
109}