97 lines
2.8 KiB
Rust
97 lines
2.8 KiB
Rust
use std::{
|
|
collections::HashMap,
|
|
fs::{exists, read_to_string, write},
|
|
sync::LazyLock,
|
|
};
|
|
use tokio::sync::RwLock;
|
|
|
|
use pathbufd::PathBufD;
|
|
|
|
/// A container for all loaded icons.
|
|
pub static ICONS: LazyLock<RwLock<HashMap<String, String>>> =
|
|
LazyLock::new(|| RwLock::new(HashMap::new()));
|
|
|
|
/// Pull an icon given its name and insert it into [`ICONS`].
|
|
pub async fn pull_icon(icon: &str, icons_dir: &str) {
|
|
let writer = &mut ICONS.write().await;
|
|
|
|
let icon_url = format!(
|
|
"https://raw.githubusercontent.com/lucide-icons/lucide/refs/heads/main/icons/{icon}.svg"
|
|
);
|
|
|
|
let file_path = PathBufD::current().extend(&[icons_dir, &format!("{icon}.svg")]);
|
|
|
|
if exists(&file_path).unwrap_or(false) {
|
|
writer.insert(icon.to_string(), read_to_string(&file_path).unwrap());
|
|
return;
|
|
}
|
|
|
|
println!("download icon: {icon}");
|
|
let svg = reqwest::get(icon_url)
|
|
.await
|
|
.unwrap()
|
|
.text()
|
|
.await
|
|
.unwrap()
|
|
.replace("\n", "");
|
|
|
|
write(&file_path, &svg).unwrap();
|
|
writer.insert(icon.to_string(), svg);
|
|
}
|
|
|
|
/// Read a string and pull all icons found within it.
|
|
pub async fn pull_icons(mut input: String, icon_dir: &str) -> String {
|
|
// icon (with class)
|
|
let icon_with_class =
|
|
regex::Regex::new("(\\{\\{)\\s*(icon)\\s*(.*?)\\s*c\\((.*?)\\)\\s*(\\}\\})").unwrap();
|
|
|
|
for cap in icon_with_class.captures_iter(&input.clone()) {
|
|
let cap_str = &cap.get(3).unwrap().as_str().replace("\"", "");
|
|
let icon = &(if cap_str.contains(" }}") {
|
|
cap_str.split(" }}").next().unwrap().to_string()
|
|
} else {
|
|
cap_str.to_string()
|
|
});
|
|
|
|
pull_icon(icon, icon_dir).await;
|
|
|
|
let reader = ICONS.read().await;
|
|
let icon_text = reader.get(icon).unwrap().replace(
|
|
"<svg",
|
|
&format!("<svg class=\"icon {}\"", cap.get(4).unwrap().as_str()),
|
|
);
|
|
|
|
input = input.replace(
|
|
&format!(
|
|
"{{{{ icon \"{cap_str}\" c({}) }}}}",
|
|
cap.get(4).unwrap().as_str()
|
|
),
|
|
&icon_text,
|
|
);
|
|
}
|
|
|
|
// icon (without class)
|
|
let icon_without_class = regex::Regex::new("(\\{\\{)\\s*(icon)\\s*(.*?)\\s*(\\}\\})").unwrap();
|
|
|
|
for cap in icon_without_class.captures_iter(&input.clone()) {
|
|
let cap_str = &cap.get(3).unwrap().as_str().replace("\"", "");
|
|
let icon = &(if cap_str.contains(" }}") {
|
|
cap_str.split(" }}").next().unwrap().to_string()
|
|
} else {
|
|
cap_str.to_string()
|
|
});
|
|
|
|
pull_icon(icon, icon_dir).await;
|
|
|
|
let reader = ICONS.read().await;
|
|
let icon_text = reader
|
|
.get(icon)
|
|
.unwrap()
|
|
.replace("<svg", "<svg class=\"icon\"");
|
|
|
|
input = input.replace(&format!("{{{{ icon \"{cap_str}\" }}}}",), &icon_text);
|
|
}
|
|
|
|
// ...
|
|
input
|
|
}
|