1#[cfg(feature = "color")]
4pub use anstyle::{Ansi256Color, AnsiColor, Color, Effects, Reset, RgbColor};
5
6#[cfg(not(feature = "color"))]
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub enum AnsiColor {
10 Black,
11 Red,
12 Green,
13 Yellow,
14 Blue,
15 Magenta,
16 Cyan,
17 White,
18 BrightBlack,
19 BrightRed,
20 BrightGreen,
21 BrightYellow,
22 BrightBlue,
23 BrightMagenta,
24 BrightCyan,
25 BrightWhite,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
34pub struct Style {
35 #[cfg(feature = "color")]
36 inner: anstyle::Style,
37}
38
39impl Style {
40 #[inline]
42 pub const fn new() -> Self {
43 #[cfg(feature = "color")]
44 {
45 Self { inner: anstyle::Style::new() }
46 }
47 #[cfg(not(feature = "color"))]
48 {
49 Self {}
50 }
51 }
52
53 #[inline]
55 #[allow(unused_mut)]
56 pub const fn bold(mut self) -> Self {
57 #[cfg(feature = "color")]
58 {
59 self.inner = self.inner.bold();
60 }
61 self
62 }
63
64 #[inline]
66 #[allow(unused_mut)]
67 pub const fn dimmed(mut self) -> Self {
68 #[cfg(feature = "color")]
69 {
70 self.inner = self.inner.dimmed();
71 }
72 self
73 }
74
75 #[inline]
77 #[allow(unused_mut)]
78 pub const fn italic(mut self) -> Self {
79 #[cfg(feature = "color")]
80 {
81 self.inner = self.inner.italic();
82 }
83 self
84 }
85
86 #[inline]
88 #[allow(unused_mut)]
89 pub const fn underline(mut self) -> Self {
90 #[cfg(feature = "color")]
91 {
92 self.inner = self.inner.underline();
93 }
94 self
95 }
96
97 #[inline]
99 #[allow(unused_mut)]
100 pub const fn fg_rgb(mut self, r: u8, g: u8, b: u8) -> Self {
101 #[cfg(feature = "color")]
102 {
103 self.inner = self.inner.fg_color(Some(anstyle::Color::Rgb(anstyle::RgbColor(r, g, b))));
104 }
105 let _ = (r, g, b);
106 self
107 }
108
109 #[inline]
111 #[allow(unused_mut)]
112 pub const fn fg_ansi256(mut self, code: u8) -> Self {
113 #[cfg(feature = "color")]
114 {
115 self.inner =
116 self.inner.fg_color(Some(anstyle::Color::Ansi256(anstyle::Ansi256Color(code))));
117 }
118 let _ = code;
119 self
120 }
121
122 #[inline]
124 #[allow(unused_mut)]
125 pub const fn fg_ansi(mut self, color: AnsiColor) -> Self {
126 #[cfg(feature = "color")]
127 {
128 self.inner = self.inner.fg_color(Some(anstyle::Color::Ansi(color)));
129 }
130 let _ = color;
131 self
132 }
133
134 #[inline]
136 pub fn is_plain(&self) -> bool {
137 #[cfg(feature = "color")]
138 {
139 self.inner == anstyle::Style::new()
140 }
141 #[cfg(not(feature = "color"))]
142 {
143 true
144 }
145 }
146
147 #[inline]
149 pub fn render(&self) -> impl core::fmt::Display {
150 #[cfg(feature = "color")]
151 {
152 self.inner.render()
153 }
154 #[cfg(not(feature = "color"))]
155 {
156 ""
157 }
158 }
159
160 #[inline]
162 pub fn render_reset(&self) -> impl core::fmt::Display {
163 #[cfg(feature = "color")]
164 {
165 self.inner.render_reset()
166 }
167 #[cfg(not(feature = "color"))]
168 {
169 ""
170 }
171 }
172
173 #[cfg(feature = "color")]
174 pub const fn inner(&self) -> anstyle::Style {
176 self.inner
177 }
178
179 pub fn to_ansi256(self) -> Self {
181 #[cfg(feature = "color")]
182 {
183 let mut styled = self.inner;
184 if let Some(color) = styled.get_fg_color() {
185 let converted = match color {
186 anstyle::Color::Rgb(rgb) => anstyle::Color::Ansi256(anstyle::Ansi256Color(
187 rgb_to_ansi256(rgb.0, rgb.1, rgb.2),
188 )),
189 other => other,
190 };
191 styled = styled.fg_color(Some(converted));
192 }
193 Self { inner: styled }
194 }
195 #[cfg(not(feature = "color"))]
196 {
197 self
198 }
199 }
200
201 pub fn to_ansi16(self) -> Self {
203 #[cfg(feature = "color")]
204 {
205 let mut styled = self.inner;
206 if let Some(color) = styled.get_fg_color() {
207 let converted = match color {
208 anstyle::Color::Rgb(rgb) => {
209 anstyle::Color::Ansi(rgb_to_ansi16(rgb.0, rgb.1, rgb.2))
210 }
211 anstyle::Color::Ansi256(code) => {
212 let (r, g, b) = ansi256_to_rgb(code.0);
213 anstyle::Color::Ansi(rgb_to_ansi16(r, g, b))
214 }
215 anstyle::Color::Ansi(ansi) => anstyle::Color::Ansi(ansi),
216 };
217 styled = styled.fg_color(Some(converted));
218 }
219 Self { inner: styled }
220 }
221 #[cfg(not(feature = "color"))]
222 {
223 self
224 }
225 }
226
227 pub fn to_monochrome(self) -> Self {
229 #[cfg(feature = "color")]
230 {
231 let styled = self.inner.fg_color(None).bg_color(None);
232 Self { inner: styled }
233 }
234 #[cfg(not(feature = "color"))]
235 {
236 self
237 }
238 }
239}
240
241#[cfg(feature = "color")]
242impl From<anstyle::Style> for Style {
243 fn from(inner: anstyle::Style) -> Self {
244 Self { inner }
245 }
246}
247
248#[cfg(feature = "color")]
249impl From<Style> for anstyle::Style {
250 fn from(style: Style) -> Self {
251 style.inner
252 }
253}
254
255pub fn rgb_to_ansi256(r: u8, g: u8, b: u8) -> u8 {
257 if r == g && g == b {
258 if r < 8 {
259 16
260 } else if r > 248 {
261 231
262 } else {
263 (((r as u16 - 8) * 24) / 240) as u8 + 232
264 }
265 } else {
266 let r_idx = ((r as u16 * 5) + 127) / 255;
267 let g_idx = ((g as u16 * 5) + 127) / 255;
268 let b_idx = ((b as u16 * 5) + 127) / 255;
269 (16 + 36 * r_idx + 6 * g_idx + b_idx) as u8
270 }
271}
272
273pub fn ansi256_to_rgb(code: u8) -> (u8, u8, u8) {
275 if code < 16 {
276 ANSI16_PALETTE[code as usize].1
277 } else if code < 232 {
278 let idx = code - 16;
279 let b = (idx % 6) * 51;
280 let g = ((idx / 6) % 6) * 51;
281 let r = (idx / 36) * 51;
282 (r, g, b)
283 } else {
284 let gray = (code - 232) * 10 + 8;
285 (gray, gray, gray)
286 }
287}
288
289static ANSI16_PALETTE: [(AnsiColor, (u8, u8, u8)); 16] = [
290 (AnsiColor::Black, (0, 0, 0)),
291 (AnsiColor::Red, (205, 49, 49)),
292 (AnsiColor::Green, (13, 188, 121)),
293 (AnsiColor::Yellow, (229, 229, 16)),
294 (AnsiColor::Blue, (36, 114, 200)),
295 (AnsiColor::Magenta, (188, 63, 188)),
296 (AnsiColor::Cyan, (17, 168, 205)),
297 (AnsiColor::White, (229, 229, 229)),
298 (AnsiColor::BrightBlack, (102, 102, 102)),
299 (AnsiColor::BrightRed, (241, 76, 76)),
300 (AnsiColor::BrightGreen, (35, 209, 139)),
301 (AnsiColor::BrightYellow, (245, 245, 67)),
302 (AnsiColor::BrightBlue, (59, 142, 234)),
303 (AnsiColor::BrightMagenta, (214, 112, 214)),
304 (AnsiColor::BrightCyan, (41, 184, 219)),
305 (AnsiColor::BrightWhite, (255, 255, 255)),
306];
307
308pub fn rgb_to_ansi16(r: u8, g: u8, b: u8) -> AnsiColor {
310 let mut closest = AnsiColor::White;
311 let mut min_dist = u32::MAX;
312
313 for (color, (pr, pg, pb)) in ANSI16_PALETTE {
314 let dr = (r as i32) - (pr as i32);
315 let dg = (g as i32) - (pg as i32);
316 let db = (b as i32) - (pb as i32);
317 let dist = (dr * dr + dg * dg + db * db) as u32;
318 if dist < min_dist {
319 min_dist = dist;
320 closest = color;
321 }
322 }
323
324 closest
325}