1use nerd_font_symbols::md;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum IconMode {
10 Nerd,
11 Ascii,
12}
13
14#[derive(Debug, Clone, Copy)]
15pub struct IconSet {
16 pub logo: &'static str,
17 pub dashboard: &'static str,
18 pub tasks: &'static str,
19 pub stats: &'static str,
20 pub settings: &'static str,
21 pub help: &'static str,
22 pub play: &'static str,
23 pub pause: &'static str,
24 pub check: &'static str,
25 pub idle: &'static str,
26 pub timer: &'static str,
27 pub fire: &'static str,
28 pub target: &'static str,
29 pub calendar: &'static str,
30 pub chart: &'static str,
31 pub cycle: &'static str,
32 pub task_active: &'static str,
33 pub task_todo: &'static str,
34 pub task_progress: &'static str,
35 pub task_done: &'static str,
36 pub star: &'static str,
37 pub alert: &'static str,
38 pub plus: &'static str,
39 pub delete: &'static str,
40 pub edit: &'static str,
41 pub export: &'static str,
42 pub zen: &'static str,
43 pub skip: &'static str,
44 pub reset: &'static str,
45 pub end: &'static str,
46 pub chevron: &'static str,
47 pub focus: &'static str,
48 pub heart: &'static str,
49 pub dot: &'static str,
50}
51
52const NERD: IconSet = IconSet {
53 logo: md::MD_WEATHER_NIGHT,
54 dashboard: md::MD_VIEW_DASHBOARD,
55 tasks: md::MD_FORMAT_LIST_BULLETED,
56 stats: md::MD_CHART_LINE,
57 settings: md::MD_COG,
58 help: md::MD_HELP_CIRCLE,
59 play: md::MD_PLAY,
60 pause: md::MD_PAUSE,
61 check: md::MD_CHECK_CIRCLE,
62 idle: md::MD_TIMER_OUTLINE,
63 timer: md::MD_TIMER,
64 fire: md::MD_FIRE,
65 target: md::MD_TARGET,
66 calendar: md::MD_CALENDAR,
67 chart: md::MD_CHART_BAR,
68 cycle: md::MD_DOTS_HORIZONTAL,
69 task_active: md::MD_PLAY_CIRCLE,
70 task_todo: md::MD_CHECKBOX_BLANK_CIRCLE_OUTLINE,
71 task_progress: md::MD_PROGRESS_CLOCK,
72 task_done: md::MD_CHECK_CIRCLE,
73 star: md::MD_STAR,
74 alert: md::MD_CALENDAR_ALERT,
75 plus: md::MD_PLUS,
76 delete: md::MD_DELETE,
77 edit: md::MD_PENCIL,
78 export: md::MD_EXPORT,
79 zen: md::MD_WEATHER_NIGHT,
80 skip: md::MD_SKIP_NEXT,
81 reset: md::MD_REFRESH,
82 end: md::MD_STOP,
83 chevron: md::MD_CHEVRON_RIGHT,
84 focus: md::MD_CROSSHAIRS,
85 heart: md::MD_HEART,
86 dot: "ยท",
87};
88
89const ASCII: IconSet = IconSet {
90 logo: "*",
91 dashboard: "#",
92 tasks: "T",
93 stats: "S",
94 settings: "G",
95 help: "?",
96 play: ">",
97 pause: "||",
98 check: "+",
99 idle: "-",
100 timer: "t",
101 fire: "^",
102 target: "@",
103 calendar: "C",
104 chart: "=",
105 cycle: "...",
106 task_active: ">",
107 task_todo: "o",
108 task_progress: "~",
109 task_done: "x",
110 star: "*",
111 alert: "!",
112 plus: "+",
113 delete: "X",
114 edit: "E",
115 export: "S",
116 zen: "z",
117 skip: ">>",
118 reset: "R",
119 end: "#",
120 chevron: ">",
121 focus: "*",
122 heart: "<3",
123 dot: ".",
124};
125
126impl IconSet {
127 pub fn detect() -> Self {
128 match std::env::var("VOID_ICONS")
129 .ok()
130 .map(|v| v.to_ascii_lowercase())
131 .as_deref()
132 {
133 Some("nerd") | Some("nerd-font") => NERD,
134 Some("ascii") | Some("text") => ASCII,
135 Some("auto") | None => Self::detect_auto(),
136 Some(other) => {
137 eprintln!("void: unknown VOID_ICONS={other:?}, using auto");
138 Self::detect_auto()
139 }
140 }
141 }
142
143 pub fn mode(self) -> IconMode {
144 if self.logo == NERD.logo {
145 IconMode::Nerd
146 } else {
147 IconMode::Ascii
148 }
149 }
150
151 fn detect_auto() -> Self {
152 #[cfg(windows)]
153 {
154 if std::env::var("VOID_USE_NERD_FONTS")
155 .is_ok_and(|v| matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes"))
156 {
157 NERD
158 } else {
159 ASCII
160 }
161 }
162 #[cfg(not(windows))]
163 {
164 NERD
165 }
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn ascii_set_uses_plain_characters() {
175 assert_eq!(ASCII.play, ">");
176 assert_eq!(ASCII.check, "+");
177 assert_eq!(ASCII.task_done, "x");
178 }
179
180 #[test]
181 fn nerd_set_uses_private_use_glyphs() {
182 assert!(NERD.play.chars().any(|c| c as u32 >= 0xe000));
183 }
184
185 #[test]
186 fn explicit_env_overrides_auto() {
187 std::env::set_var("VOID_ICONS", "ascii");
188 assert_eq!(IconSet::detect().play, ASCII.play);
189 std::env::set_var("VOID_ICONS", "nerd");
190 assert_eq!(IconSet::detect().play, NERD.play);
191 std::env::remove_var("VOID_ICONS");
192 }
193}