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}