tabler_dominator/
lib.rs

1use std::{collections::HashMap, path::Path, sync::Mutex};
2
3use lazy_static::lazy_static;
4use proc_macro::TokenStream;
5use quote::{quote, quote_spanned};
6
7lazy_static! {
8    static ref ICONS: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
9}
10
11#[proc_macro]
12pub fn icon(item: TokenStream) -> TokenStream {
13    icon_impl(item).unwrap_or_else(|e| e)
14}
15
16fn icon_impl(item: TokenStream) -> Result<TokenStream, TokenStream> {
17    let item = item.into_iter().next().expect("No icon specified");
18    let input = item
19        .to_string()
20        .strip_prefix('"')
21        .expect("No leading quote?")
22        .strip_suffix('"')
23        .expect("No trailing quote?")
24        .to_string();
25
26    if !ICONS.lock().unwrap().contains_key(&input) {
27        let path = Path::new(file!().strip_suffix("lib.rs").unwrap())
28            .join("../svg/")
29            .join(&format!("{input}.svg"))
30            .canonicalize()
31            .unwrap();
32
33        let file = std::fs::read(path).map_err(|_| {
34            return quote_spanned! {
35                item.span().into() =>
36                compile_error!("Invalid Tabler Icon");
37            };
38        })?;
39
40        ICONS
41            .lock()
42            .unwrap()
43            .insert(input.clone(), String::from_utf8_lossy(&file).to_string());
44    }
45
46    let icon = ICONS.lock().unwrap();
47    let icon = icon.get(&input).unwrap();
48    let exp = quote! {
49        html!("div", { .with_node!(e => {
50                .apply(|d| {
51                    e.set_inner_html(&#icon);
52                    d
53                })
54            })
55        })
56    };
57
58    Ok(exp.into())
59}