svgr_macro/
lib.rs

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}