1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
//! The `icon_map` module provides functionality for mapping `IconKind` enum variants to their corresponding icons and colors.
//! It contains a `Lazy` static `ICON_MAP` which is a thread-safe `HashMap` that maps `IconKind` enum variants to a tuple of an icon and a color.
//! The `ICON_MAP` is used by the `Whisper` struct to look up the icon and color based on the `IconKind`.
//! The `ICON_MAP` is lazily initialized and contains mappings for both `NerdFont` and Unicode icons.
//!
//! The `IconKind` enum represents different kinds of icons for formatting messages. It supports both Unicode or Nerd Font icons if you have a Nerd Font installed.
//!
use enum_iterator::Sequence;
use once_cell::sync::Lazy;
use std::fmt;
use std::{collections::HashMap, sync::Mutex};
/// `IconKind` is an enum representing different kinds of icons for formatting messages.
///
/// # Examples
/// ```
/// use murmur::{Whisper, IconKind};
///
/// Whisper::new()
/// .icon(IconKind::NfFaTimes)
/// .message("This is a message with a Nerd Font icon.")
/// .whisper()
/// .unwrap();
/// ```
///
/// You must have [NerdFonts](https://www.nerdfonts.com/) installed to use the `Nf` variants.
/// - [Nerfonts github](https://github.com/ryanoasis/nerd-fonts?tab=readme-ov-files)
/// - [NerdFonts cheat-sheet](https://www.nerdfonts.com/cheat-sheet)
#[derive(Debug, Clone, Eq, PartialEq, Hash, Sequence)]
pub enum IconKind {
NfFaTimes,
NfFaCheck,
NfFaInfoCircle,
NfFaRefresh,
NfFaWarning,
NfFaBug,
NfOctDotFill,
UnicodeCrossMark,
UnicodeCheckMark,
UnicodeInformationSource,
UnicodeGear,
UnicodeWarningSign,
UnicodeBug,
}
/// Implement the `Display` trait for `IconKind`.
impl fmt::Display for IconKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
/// A static `ICON_MAP` that maps `IconKind` to a tuple of icon and color.
///
/// This map is lazily initialized and thread-safe. It contains mappings for both `NerdFont` and Unicode icons.
/// Each `IconKind` is mapped to a tuple, where the first element is the icon character and the second element is the color.
///
/// The `ICON_MAP` is used by the `Whisper` struct to look up the icon and color based on the `IconKind`.
///
/// If the `tracing` feature is enabled, an informational message will be logged when the `ICON_MAP` is initialized.
pub static ICON_MAP: Lazy<Mutex<HashMap<IconKind, (&'static str, &'static str)>>> =
Lazy::new(|| {
let mut icon_map = HashMap::new();
// Nerd Font icons
icon_map.insert(IconKind::NfFaTimes, ("\u{f00d} ", "red")); //
icon_map.insert(IconKind::NfFaCheck, ("\u{f00c} ", "green")); //
icon_map.insert(IconKind::NfFaInfoCircle, ("\u{f05a} ", "white")); //
icon_map.insert(IconKind::NfFaRefresh, ("\u{f021} ", "cyan")); //
icon_map.insert(IconKind::NfFaWarning, ("\u{f071} ", "yellow")); //
icon_map.insert(IconKind::NfFaBug, ("\u{f188} ", "red")); //
icon_map.insert(IconKind::NfOctDotFill, ("\u{f444} ", "white")); //
// Unicode icons
icon_map.insert(IconKind::UnicodeCrossMark, ("\u{274C} ", "red")); // ❌
icon_map.insert(IconKind::UnicodeCheckMark, ("\u{2714}\u{FE0F} ", "green")); // ✔️
icon_map.insert(
IconKind::UnicodeInformationSource,
("\u{2139}\u{FE0F} ", "white"),
); // ℹ️
icon_map.insert(IconKind::UnicodeGear, ("\u{2699}\u{FE0F} ", "cyan")); // ⚙️
icon_map.insert(
IconKind::UnicodeWarningSign,
("\u{26A0}\u{FE0F} ", "yellow"),
); // ⚠️
icon_map.insert(IconKind::UnicodeBug, ("\u{1F41B} ", "red")); // 🐛
Mutex::new(icon_map)
});
#[cfg(test)]
mod icon_map_tests {
use super::*;
use crate::Whisper;
use color_eyre::Report;
use enum_iterator::all;
#[test]
fn color_eyre_install_setup() -> Result<(), Report> {
color_eyre::install()?;
Whisper::new().message("color_eyre installed").whisper()?;
Ok(())
}
/// Test function to print all icons associated with different kinds of messages.
///
/// The function begins by calling `all::<IconKind>().collect::<Vec<_>>()`.
/// This line of code uses the `all` function from the `enum_iterator` crate
/// to create an iterator over all variants of the `IconKind` enum.
/// The `collect::<Vec<_>>()` function is then used to collect these variants into a vector.
///
/// Next, the function calls `iter()` on the vector to create an iterator,
/// and then uses `for_each` to apply a closure to each `IconKind` variant in the iterator.
///
/// Inside the closure, the function first acquires a lock on the `ICON_MAP` static variable,
/// which is a thread-safe `HashMap` that maps `IconKind` enum variants to a tuple of an icon and a color.
/// The `unwrap()` function is used to handle the `Result` returned by `lock()`.
/// If the lock can't be acquired, the program will panic and terminate.
///
/// Then, the function calls `get(icon_kind)` on the `ICON_MAP` to look up the icon and color associated with the current `IconKind` variant.
/// The `unwrap().0` at the end extracts the icon from the tuple (ignoring the color),
/// and if the `get` call returns `None` (i.e., if the `IconKind` variant is not found in the `ICON_MAP`),
/// the program will panic and terminate.
///
/// Finally, the function prints the `IconKind` variant and the associated icon to the console.
///
/// In summary, this test function is used to print all the icons in the `ICON_MAP` to the console.
/// It's a simple way to visually check that all the icons are correctly mapped to their corresponding `IconKind` variants.
#[test]
fn test_print_all_icons() {
all::<IconKind>()
.collect::<Vec<_>>()
.iter()
.for_each(|icon_kind| {
println!(
"{}: {}",
icon_kind,
ICON_MAP.lock().unwrap().get(icon_kind).unwrap().0
);
});
}
/// This test function checks the spacing after each icon in the `ICON_MAP`.
///
/// It iterates over each `IconKind` and its associated icon in the `ICON_MAP`.
/// For each icon, it asserts that the icon ends with exactly one space.
/// If an icon ends with no space or more than one space, the assertion fails and the test function panics.
///
/// # Panics
///
/// This function will panic if any icon in the `ICON_MAP` does not end with exactly one space.
#[test]
fn test_spaces_after_icons() {
let icon_map = {
let guard = ICON_MAP.lock().unwrap();
guard.clone()
};
for (icon_kind, (icon, _)) in &icon_map {
// Check that there is only one space after the icon
assert!(
icon.ends_with(' ') && !icon.ends_with(" "),
"Invalid spacing after {icon_kind} icon: '{icon}'",
);
}
}
}