1#[cfg(not(no_std))]
2use std::{io::IsTerminal, sync::atomic::{AtomicU8, Ordering}};
3
4
5#[cfg(not(no_std))]
6static METADATA : AtomicU8 = AtomicU8::new(u8::MAX);
7
8
9pub fn flush_metadata() {
10 METADATA.store(u8::MAX, Ordering::Relaxed);
11 init_metadata();
12}
13
14
15#[cfg(not(no_std))]
16fn init_metadata() -> (bool, bool, bool) {
17 let mut val = METADATA.load(std::sync::atomic::Ordering::Relaxed);
18 if val == u8::MAX {
19 let mut compute = 0b00000000;
20 if std::env::var("NO_COLOR").is_ok() {
21 compute += 2u8.pow(1);
22 }
23
24
25 if std::env::var("FORCE_COLOR").is_ok() {
26 compute += 2u8.pow(2);
27 }
28
29
30 if std::io::stdin().is_terminal() {
31 compute += 2u8.pow(3);
32 }
33
34 val = compute;
35 METADATA.store(compute, std::sync::atomic::Ordering::Relaxed);
36 }
37
38 (
39 (val & 2u8.pow(1) == 2u8.pow(1)),
40 (val & 2u8.pow(2) == 2u8.pow(2)),
41 (val & 2u8.pow(3) == 2u8.pow(3)),
42 )
43}
44
45
46pub enum Operation {
47 Colour(Colour), BgColour(Colour),
48 Bold,
49 Dim,
50 Italic,
51 Underline,
52 Blinking,
53 Inverse,
54 Hidden,
55 Strikethrough,
56 Reset,
57}
58
59
60#[derive(Clone, Copy)]
61pub struct Colour {
62 r: u8,
63 g: u8,
64 b: u8,
65}
66
67impl Colour {
68 pub fn rgb(r: u8, g: u8, b: u8) -> Self { Self { r, g, b } }
69}
70
71
72pub struct ColouredString<T> {
73 string: T,
74
75 fg_colour: Option<Colour>,
76 bg_colour: Option<Colour>,
77 is_bold: bool,
78 is_dim: bool,
79 is_italic: bool,
80 is_underline: bool,
81 is_blinking: bool,
82 is_inverse: bool,
83 is_hidden: bool,
84 is_strikethrough: bool,
85}
86
87
88impl<T> ColouredString<T> {
89 pub fn colour(mut self, colour: Colour) -> Self {
90 self.fg_colour = Some(colour);
91 self
92 }
93
94
95 pub fn bg_colour(mut self, colour: Colour) -> Self {
96 self.bg_colour = Some(colour);
97 self
98 }
99
100
101 pub fn bold(mut self) -> Self {
102 self.is_bold = true;
103 self
104 }
105
106
107 pub fn dim(mut self) -> Self {
108 self.is_dim = true;
109 self
110 }
111
112
113 pub fn italic(mut self) -> Self {
114 self.is_italic = true;
115 self
116 }
117
118
119 pub fn underline(mut self) -> Self {
120 self.is_underline = true;
121 self
122 }
123
124
125 pub fn blinking(mut self) -> Self {
126 self.is_blinking = true;
127 self
128 }
129
130
131 pub fn inverse(mut self) -> Self {
132 self.is_inverse = true;
133 self
134 }
135
136
137 pub fn hidden(mut self) -> Self {
138 self.is_hidden = true;
139 self
140 }
141
142
143 pub fn strikethrough(mut self) -> Self {
144 self.is_strikethrough = true;
145 self
146 }
147
148
149 fn write_ansi(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
150 write!(f, "\u{001b}[")?;
151 let mut has_updated = false;
152
153 if let Some(Colour { r, g, b }) = self.fg_colour {
154 has_updated = true;
155 write!(f, "38;2;{r};{g};{b}")?;
156 }
157
158
159 if let Some(Colour { r, g, b }) = self.bg_colour {
160 if has_updated { write!(f, ";")?; }
161 has_updated = true;
162 write!(f, "48;2;{r};{g};{b}")?;
163 }
164
165
166 if self.is_bold {
167 if has_updated { write!(f, ";")?; }
168 has_updated = true;
169 write!(f, "1")?;
170 }
171
172
173 if self.is_dim {
174 if has_updated { write!(f, ";")?; }
175 has_updated = true;
176 write!(f, "2")?;
177 }
178
179
180 if self.is_italic {
181 if has_updated { write!(f, ";")?; }
182 has_updated = true;
183 write!(f, "3")?;
184 }
185
186
187 if self.is_underline {
188 if has_updated { write!(f, ";")?; }
189 has_updated = true;
190 write!(f, "4")?;
191 }
192
193
194 if self.is_blinking {
195 if has_updated { write!(f, ";")?; }
196 has_updated = true;
197 write!(f, "5")?;
198 }
199
200
201 if self.is_inverse {
202 if has_updated { write!(f, ";")?; }
203 has_updated = true;
204 write!(f, "7")?;
205 }
206
207
208 if self.is_hidden {
209 if has_updated { write!(f, ";")?; }
210 has_updated = true;
211 write!(f, "8")?;
212 }
213
214
215 if self.is_strikethrough {
216 if has_updated { write!(f, ";")?; }
217 write!(f, "9")?;
218 }
219
220
221 write!(f, "m")
222 }
223}
224
225
226impl<T: core::fmt::Display> core::fmt::Display for ColouredString<T> {
227 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
228 #[cfg(not(no_std))]
229 {
230 let (no_colour, force_colour, is_terminal) = init_metadata();
231 if !force_colour && (no_colour || !is_terminal) {
232 return self.string.fmt(f)
233 }
234 }
235
236 self.write_ansi(f)?;
237
238 write!(f, "{}\u{001b}[0m", self.string)
239 }
240}
241
242
243impl<T: core::fmt::Debug> core::fmt::Debug for ColouredString<T> {
244 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
245 #[cfg(not(no_std))]
246 {
247 let (no_colour, force_colour, is_terminal) = init_metadata();
248 if !force_colour && (no_colour || !is_terminal) {
249 return self.string.fmt(f)
250 }
251 }
252
253 self.write_ansi(f)?;
254
255 self.string.fmt(f)?;
256
257 write!(f, "\u{001b}[0m")
258 }
259}
260
261
262macro_rules! binding {
263 ($($ident: ident)*) => {
264 $(
265 #[inline(always)]
266 fn $ident(self) -> ColouredString<Self> {
267 self.as_brush().$ident()
268 }
269 )*
270 }
271}
272
273
274pub trait ColourBrush: Sized {
275 #[inline(always)]
276 fn as_brush(self) -> ColouredString<Self> {
277 ColouredString {
278 string: self,
279 fg_colour: None,
280 bg_colour: None,
281 is_bold: false,
282 is_dim: false,
283 is_italic: false,
284 is_underline: false,
285 is_blinking: false,
286 is_inverse: false,
287 is_hidden: false,
288 is_strikethrough: false
289 }
290 }
291
292
293 #[inline(always)]
294 fn colour(self, colour: Colour) -> ColouredString<Self> {
295 self.as_brush().colour(colour)
296 }
297
298
299 #[inline(always)]
300 fn bg_colour(self, colour: Colour) -> ColouredString<Self> {
301 self.as_brush().bg_colour(colour)
302 }
303
304
305 binding!(
306 bold
307 dim
308 italic
309 underline
310 blinking
311 inverse
312 hidden
313 strikethrough
314
315 black
316 dark_grey
317 brown
318 navy_blue
319 olive_green
320 teal
321 red
322 green
323 blue
324 magenta
325 yellow
326 orange
327 pink
328 light_grey
329 cyan
330 white
331 bg_dark_grey
332 bg_brown
333 bg_navy_blue
334 bg_olive_green
335 bg_teal
336 bg_red
337 bg_green
338 bg_blue
339 bg_magenta
340 bg_yellow
341 bg_orange
342 bg_pink
343 bg_light_grey
344 bg_cyan
345 bg_white
346 );
347}
348
349
350
351macro_rules! colours {
352 ($($name: ident : $r: literal $g: literal $b: literal),*) => {
353 $(
354 #[inline(always)]
355 pub fn $name(self) -> Self { self.colour(Colour { r: $r, g: $g, b: $b }) }
356 )*
357 }
358}
359
360
361macro_rules! bg_colours {
362 ($($name: ident : $r: literal $g: literal $b: literal),*) => {
363 $(
364 #[inline(always)]
365 pub fn $name(self) -> Self { self.bg_colour(Colour { r: $r, g: $g, b: $b }) }
366 )*
367 }
368}
369
370
371impl<T> ColouredString<T> {
372 colours! {
373 black : 0 0 0,
374 dark_grey : 100 100 100,
375 brown : 165 42 42,
376 navy_blue : 0 0 128,
377 olive_green: 128 128 0,
378 teal : 0 128 128,
379 red : 255 0 0,
380 green : 0 255 0,
381 blue : 0 0 255,
382 magenta : 255 0 255,
383 yellow : 255 255 0,
384 orange : 255 165 0,
385 pink : 255 192 203,
386 light_grey : 200 200 200,
387 cyan : 0 255 255,
388 white : 255 255 255
389 }
390
391
392 bg_colours! {
393 bg_black : 0 0 0,
394 bg_dark_grey : 100 100 100,
395 bg_brown : 165 42 42,
396 bg_navy_blue : 0 0 128,
397 bg_olive_green: 128 128 0,
398 bg_teal : 0 128 128,
399 bg_red : 255 0 0,
400 bg_green : 0 255 0,
401 bg_blue : 0 0 255,
402 bg_magenta : 255 0 255,
403 bg_yellow : 255 255 0,
404 bg_orange : 255 165 0,
405 bg_pink : 255 192 203,
406 bg_light_grey : 200 200 200,
407 bg_cyan : 0 255 255,
408 bg_white : 255 255 255
409 }
410}
411
412
413impl<T> ColourBrush for T {}