Skip to main content

nf_icons/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::LitStr;
6use syn::parse_macro_input;
7
8include!(concat!(env!("OUT_DIR"), "/glyphs.rs"));
9
10/// Look up a Nerd Font glyph by its cheat-sheet name.
11///
12/// ```
13/// use nf_icons::nf;
14/// assert_eq!(nf!("nf-fa-xmark"), "\u{f00d}");
15/// ```
16#[proc_macro]
17pub fn nf(input: TokenStream) -> TokenStream {
18    let glyph_name = parse_macro_input!(input as LitStr).value();
19
20    if let Some(&glyph) = GLYPHS.get(glyph_name.as_str()) {
21        quote!(#glyph)
22    } else {
23        let mut message = format!("Unknown Nerd Font glyph `{glyph_name}`.");
24
25        let mut candidates =
26            GLYPHS
27                .keys()
28                .map(|&key| (strsim::jaro_winkler(&glyph_name, key), key))
29                .filter(|&(score, _)| score > 0.75)
30                .collect::<Vec<_>>();
31        candidates.sort_by(|a, b| f64::total_cmp(&b.0, &a.0));
32
33        let suggestions =
34            candidates
35                .into_iter()
36                .take(3)
37                .map(|(_, candidate)| format!("`{candidate}`"))
38                .collect::<Vec<_>>();
39
40        if !suggestions.is_empty() {
41            message.push_str(" Did you mean one of: ");
42            message.push_str(&suggestions.join(", "));
43            message.push('?');
44        }
45
46        quote!(compile_error!(#message))
47    }.into()
48}