use std::borrow::Cow;
use gpui::{AssetSource, Result, SharedString, Styled as _, Svg, svg};
macro_rules! icon_assets {
($(($const_name:ident, $path:literal)),+ $(,)?) => {
$(pub const $const_name: &str = concat!("icons/", $path, ".svg");)+
pub struct Assets;
impl AssetSource for Assets {
fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
Ok(match path {
$(concat!("icons/", $path, ".svg") => Some(Cow::Borrowed(
include_bytes!(concat!("../assets/icons/", $path, ".svg")).as_slice(),
)),)+
_ => None,
})
}
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
let all = [$(concat!("icons/", $path, ".svg")),+];
Ok(all
.iter()
.filter(|p| p.starts_with(path))
.map(|p| SharedString::from(*p))
.collect())
}
}
};
}
icon_assets![
(MONITOR, "monitor"),
(LAPTOP, "laptop"),
(PEN_NEW_SQUARE, "pen-new-square"),
(SORT_VERTICAL, "sort-vertical"),
(LIST, "list"),
(FOLDER_WITH_FILES, "folder-with-files"),
(FOLDER, "folder"),
(GIT_BRANCH, "git-branch"),
(CLOUD, "cloud"),
(TAG, "tag"),
(SIDEBAR_MINIMALISTIC, "sidebar-minimalistic"),
(SIDEBAR_MINIMALISTIC_LEFT, "sidebar-minimalistic-left"),
(KEY_MINIMALISTIC, "key-minimalistic"),
(KEYBOARD, "keyboard"),
(ARROW_LEFT, "arrow-left"),
(ARROW_RIGHT, "arrow-right"),
(ARROW_UP, "arrow-up"),
(ARROW_DOWN, "arrow-down"),
(RETURN, "return"),
(ALT_ARROW_DOWN, "alt-arrow-down"),
(EXPAND_ARROWS, "expand-arrows"),
(FOLD_VERTICAL, "fold-vertical"),
(ALT_ARROW_LEFT, "alt-arrow-left"),
(ALT_ARROW_RIGHT, "alt-arrow-right"),
(SMARTPHONE, "smartphone"),
(ARCHIVE_UP_MINIMALISTIC, "archive-up-minimalistic"),
(REFRESH, "refresh"),
(RESTART, "restart"),
(ADD_CIRCLE, "add-circle"),
(TUNING, "tuning"),
(PAPERCLIP, "paperclip"),
(PEN, "pen"),
(ARCHIVE_MINIMALISTIC, "archive-minimalistic"),
(TRASH_BIN_MINIMALISTIC, "trash-bin-minimalistic"),
(SETTINGS_MINIMALISTIC, "settings-minimalistic"),
(LOGOUT_2, "logout-2"),
(MAGNIFER, "magnifer"),
(COMMAND, "command"),
(DOCUMENT, "document"),
(DOCUMENT_ADD, "document-add"),
(GLOBAL, "global"),
(CHECKLIST, "checklist"),
(WIDGET, "widget"),
(WIFI_OFF, "wifi-off"),
(CLOSE_CIRCLE, "close-circle"),
(INFO_CIRCLE, "info-circle"),
(DANGER_TRIANGLE, "danger-triangle"),
(CHAT_ROUND_LINE, "chat-round-line"),
(BELL, "bell"),
(VOLUME_LOUD, "volume-loud"),
(TERMINAL, "terminal"),
(PLUS, "plus"),
(CLOSE, "close"),
(STOP, "stop"),
(CHECK, "check"),
(COPY, "copy"),
(STAR, "star"),
(STAR_BOLD, "star-bold"),
];
pub fn icon(path: &'static str) -> Svg {
svg().path(path).flex_none()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_registered_icon_loads_and_parses() {
let assets = Assets;
for path in assets.list("icons/").unwrap() {
let bytes = assets
.load(&path)
.unwrap()
.unwrap_or_else(|| panic!("missing asset {path}"));
let text = std::str::from_utf8(&bytes).expect("icon svg is utf-8");
assert!(text.contains("<svg"), "{path} is not an svg");
assert!(text.contains("viewBox"), "{path} lacks a viewBox");
}
}
#[test]
fn unknown_paths_are_none() {
assert!(Assets.load("icons/nope.svg").unwrap().is_none());
}
#[test]
fn list_filters_by_prefix() {
assert!(!Assets.list("icons/").unwrap().is_empty());
assert!(Assets.list("fonts/").unwrap().is_empty());
}
}