1use ansi_colours::{ansi256_from_rgb, rgb_from_ansi256};
2use std::fmt::{Debug, Display, LowerHex};
3use std::iter::{IntoIterator, Iterator};
4
5#[cfg(feature = "colorsys")]
6use colorsys::{Ansi256, ColorAlpha, ColorTransform, Hsl, Rgb};
7
8const DEFAULT_COLUMNS: usize = 130;
9
10pub fn reset<T: Display>(text: T) -> String {
12 format!("{}\x1b[0m", text)
13}
14
15pub fn fg<T: Display>(text: T, fg: usize) -> String {
17 format!("\x1b[1;38;5;{}m{}", wrap(fg), text)
18}
19pub fn bg<T: Display>(text: T, bg: usize) -> String {
21 format!("\x1b[1;48;5;{}m{}", wrap(bg), text)
22}
23pub fn bgfg<T: Display>(text: T, fore: usize, back: usize) -> String {
27 bg(fg(text, wrap(fore) as usize), wrap(back) as usize)
28}
29pub fn ansi<T: Display>(text: T, fore: usize, back: usize) -> String {
31 reset(bgfg(text, fore as usize, back as usize))
32}
33pub fn pad_columns<T: Display>(text: T) -> String {
35 let text = text.to_string();
36 let cols = term_cols();
37 pad(text, cols)
38}
39pub fn pad<T: Display>(text: T, length: usize) -> String {
41 let text = text.to_string();
42 let len = text
43 .as_bytes()
44 .iter()
45 .map(|c| char::from(*c))
46 .map(|c| {
47 u32::from(c)
48 .to_ne_bytes()
49 .iter()
50 .map(Clone::clone)
51 .filter(|c| *c > 0)
52 .collect::<Vec<u8>>()
53 })
54 .flatten()
55 .count();
56
57 format!(
58 "{}{}",
59 text,
60 " ".repeat(if length > len {
61 length - len
62 } else if len < length {
63 0
64 } else {
65 0
66 })
67 )
68}
69pub fn ansi_clear() -> String {
71 "\x1b[2J\x1b[3J\x1b[H".to_string()
72}
73pub fn fore<T: Display>(text: T, fore: usize) -> String {
75 let (fore, back) = couple(fore);
76 ansi(text, fore as usize, back as usize)
77}
78pub fn back<T: Display>(text: T, back: usize) -> String {
80 let (back, fore) = couple(back);
81 ansi(text, fore as usize, back as usize)
82}
83pub fn auto<T: Display>(word: T) -> String {
85 fore(
86 word.to_string(),
87 u8::from_str_radix(&word.to_string(), 10)
88 .unwrap_or_else(|_| from_display(word.to_string()))
89 .into(),
90 )
91}
92pub fn auto_bright<T: Display>(word: T) -> String {
94 fore(
95 word.to_string(),
96 bright(
97 u8::from_str_radix(&word.to_string(), 10)
98 .unwrap_or_else(|_| from_display(word.to_string()))
99 .into(),
100 )
101 .into(),
102 )
103}
104pub fn auto_dark<T: Display>(word: T) -> String {
106 fore(
107 word.to_string(),
108 dark(
109 u8::from_str_radix(&word.to_string(), 10)
110 .unwrap_or_else(|_| from_display(word.to_string()))
111 .into(),
112 )
113 .into(),
114 )
115}
116pub fn from_display<T: Display>(word: T) -> u8 {
118 let string = format!("{word}");
119 from_bytes(
120 &u8::from_str_radix(&string, 10)
121 .ok()
122 .or_else(|| u8::from_str_radix(&string, 16).ok())
123 .map(|byte| vec![byte])
124 .or_else(|| {
125 if string.to_lowercase().starts_with("0x") {
126 u8::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
127 .map(|byte| vec![byte])
128 .ok()
129 } else {
130 None
131 }
132 })
133 .map(|byte| vec![byte].into_iter().flatten().collect::<Vec<u8>>())
134 .or_else(|| {
135 u16::from_str_radix(&string, 16)
136 .map(|u| u.to_ne_bytes().to_vec())
137 .ok()
138 })
139 .or_else(|| {
140 if string.to_lowercase().starts_with("0x") {
141 u16::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
142 .map(|u| u.to_ne_bytes().to_vec())
143 .ok()
144 } else {
145 None
146 }
147 })
148 .or_else(|| {
149 u32::from_str_radix(&string, 16)
150 .ok()
151 .map(|u| u.to_ne_bytes().to_vec())
152 })
153 .or_else(|| {
154 if string.to_lowercase().starts_with("0x") {
155 u32::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
156 .map(|u| u.to_ne_bytes().to_vec())
157 .ok()
158 } else {
159 None
160 }
161 })
162 .or_else(|| {
163 u64::from_str_radix(&string, 16)
164 .ok()
165 .map(|u| u.to_ne_bytes().to_vec())
166 })
167 .or_else(|| {
168 if string.to_lowercase().starts_with("0x") {
169 u64::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
170 .map(|u| u.to_ne_bytes().to_vec())
171 .ok()
172 } else {
173 None
174 }
175 })
176 .unwrap_or_else(|| string.as_bytes().to_vec()),
177 )
178}
179pub fn from_debug<T: Debug>(word: T) -> u8 {
181 from_bytes(format!("{word:#?}").as_bytes())
182}
183pub fn rgb_from_display<T: Display>(word: T) -> [u8; 3] {
185 rgb_from_bytes(word.to_string().as_bytes())
186}
187
188pub fn from_bytes(bytes: &[u8]) -> u8 {
190 let mut color: u8 = 0;
191 for rgb in rgb_from_bytes(bytes) {
192 color ^= rgb
193 }
194 color
195}
196pub fn rgb_from_bytes(bytes: &[u8]) -> [u8; 3] {
199 merge_rgb(bytes.into_iter().map(|byte| rgb_from_byte(*byte)), false)
200}
201pub fn rgb_from_byte(byte: u8) -> [u8; 3] {
203 let tuple = rgb_from_ansi256(byte);
204 [tuple.0, tuple.1, tuple.2]
205}
206
207pub fn rgb_to_byte(rgb: [u8; 3]) -> u8 {
209 ansi256_from_rgb(rgb)
210}
211pub fn merge_rgb<I: IntoIterator<Item = [u8; 3]> + Clone>(rgbs: I, extra: bool) -> [u8; 3] {
213 let mut result = [0u8; 3];
214 for rgb in rgbs.clone().into_iter() {
215 result[0] ^= rgb[0];
216 result[1] ^= rgb[1];
217 result[2] ^= rgb[2];
218 }
219 if extra {
220 for triple in rgbs.clone().into_iter() {
221 for byte in triple.into_iter() {
222 let rgb = rgb_from_byte(byte);
223 result[0] ^= rgb[0];
224 result[1] ^= rgb[1];
225 result[2] ^= rgb[2];
226 }
227 }
228 }
229 result
230}
231
232pub fn couple(color: usize) -> (u8, u8) {
237 let fore = wrap(color);
238 let back = invert_bw(fore as usize);
239 (fore, back)
240}
241
242#[cfg(feature = "colorsys")]
244pub fn invert_ansi(color: usize) -> u8 {
245 let color = Ansi256::new(wrap(color));
246 let mut hsl = Hsl::from(&Rgb::from(color));
247 hsl.set_lightness(100.0 - hsl.lightness());
248 let mut rgb = Rgb::from(&hsl);
249 rgb.invert();
250 let color = Ansi256::from(&rgb);
251 color.code()
252}
253#[cfg(not(feature = "colorsys"))]
255pub fn invert_ansi(color: usize) -> u8 {
256 if is_dark_rgb_band(color) {
257 bright(rgb_to_byte(invert_rgb(rgb_from_byte(wrap(color)))) as usize)
258 } else {
259 dark(rgb_to_byte(invert_rgb(rgb_from_byte(wrap(color)))) as usize)
260 }
261}
262
263#[cfg(feature = "colorsys")]
265pub fn invert_rgb(color: [u8; 3]) -> [u8; 3] {
266 let mut rgb = Rgb::from(color);
267 rgb.invert();
268 rgb.into()
269}
270
271#[cfg(not(feature = "colorsys"))]
273pub fn invert_rgb(color: [u8; 3]) -> [u8; 3] {
274 [255u8 - color[0], 255u8 - color[1], 255u8 - color[2]]
275}
276
277#[cfg(not(feature = "colorsys"))]
279pub fn invert_bw(color: usize) -> u8 {
280 match color {
281 0 | 8 | 16..21 | 52..61 | 88..93 | 232..239 => 231,
282 _ => 16,
283 }
284}
285#[cfg(feature = "colorsys")]
287pub fn invert_bw(color: usize) -> u8 {
288 let color = Ansi256::new(wrap(color));
289 let mut rgb = Rgb::from(color);
290 rgb.grayscale_simple();
291 rgb.invert();
292 Ansi256::from(&rgb).code()
293}
294
295pub fn is_bright_rgb_band(color: usize) -> bool {
297 let color = wrap(color);
298 color >= 0x75
299}
300pub fn bright_rgb_band(color: usize) -> u8 {
302 if !is_bright_rgb_band(color) {
303 0xff - wrap(color)
304 } else {
305 wrap(color)
306 }
307}
308#[cfg(feature = "colorsys")]
310pub fn bright(color: usize) -> u8 {
311 let color = wrap(color);
312 let mut rgb = Rgb::from(Ansi256::new(color));
313 rgb.lighten(50.0);
314 Ansi256::from(rgb).code()
315}
316#[cfg(not(feature = "colorsys"))]
318pub fn bright(color: usize) -> u8 {
319 let color = wrap(color);
320 let [r, g, b] = rgb_from_byte(color);
321 rgb_to_byte([
322 bright_rgb_band(r as usize),
323 bright_rgb_band(g as usize),
324 bright_rgb_band(b as usize),
325 ])
326}
327pub fn is_dark_rgb_band(color: usize) -> bool {
329 let color = wrap(color);
330 color <= 0x75
331}
332pub fn dark_rgb_band(color: usize) -> u8 {
334 let color = wrap(color);
335 if !is_dark_rgb_band(color as usize) {
336 color - ((color / 4) * 3)
337 } else {
338 color
339 }
340}
341#[cfg(feature = "colorsys")]
343pub fn dark(color: usize) -> u8 {
344 let color = wrap(color);
345 let mut rgb = Rgb::from(Ansi256::new(color));
346 rgb.lighten(-50.0);
347 Ansi256::from(rgb).code()
348}
349#[cfg(not(feature = "colorsys"))]
350pub fn dark(color: usize) -> u8 {
351 let color = wrap(color);
352 let [r, g, b] = rgb_from_byte(color);
353 rgb_to_byte([
354 dark_rgb_band(r as usize),
355 dark_rgb_band(g as usize),
356 dark_rgb_band(b as usize),
357 ])
358}
359
360pub fn wrap(color: usize) -> u8 {
362 if color <= u8::MAX.into() {
363 color as u8
364 } else {
365 from_bytes(&color.to_ne_bytes())
366 }
367}
368
369pub fn non_zero_be_bytes(color: usize) -> Vec<u8> {
370 let mut bytes = color.to_be_bytes().to_vec();
371 while bytes.len() > 1 && bytes[0] == 0 {
372 bytes.remove(0);
373 }
374 bytes
375}
376
377pub fn term_cols() -> usize {
379 match ioctl_term_light::cols() {
380 0 => DEFAULT_COLUMNS,
381 cols => cols as usize,
382 }
383}
384
385pub fn from_byte(byte: u8) -> u8 {
387 byte
388}
389
390pub fn byte(byte: u8) -> String {
392 let (fg, bg) = couple(from_byte(byte).into());
393 ansi(byte, fg as usize, bg as usize)
394}
395
396pub fn byte_hex(byte: u8) -> String {
398 let (fg, bg) = couple(from_byte(byte).into());
399 ansi(format!("0x{byte:02x}"), fg as usize, bg as usize)
400}
401
402pub fn byte_bin(byte: u8) -> String {
404 let (fg, bg) = couple(from_byte(byte).into());
405 ansi(format!("0b{byte:08b}"), fg as usize, bg as usize)
406}
407
408pub const STD_COLORS: [u8; 48] = [
409 0x00u8, 0x00u8, 0x00u8, 0x80u8, 0x00u8, 0x00u8, 0x00u8, 0x80u8, 0x00u8, 0x80u8, 0x80u8, 0x00u8, 0x00u8, 0x00u8, 0x80u8, 0x80u8, 0x00u8, 0x80u8, 0x00u8, 0x80u8, 0x80u8, 0xc0u8, 0xc0u8, 0xc0u8, 0x80u8, 0x80u8, 0x80u8, 0xffu8, 0x00u8, 0x00u8, 0xffu8, 0xffu8, 0x00u8, 0x00u8, 0xffu8, 0x00u8, 0x00u8, 0x00u8, 0xffu8, 0xffu8, 0x00u8, 0xffu8, 0x00u8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, 0xffu8, ];
426pub fn cube_ansi_256(color: usize, op: usize) -> u8 {
427 let color = wrap(color) as usize;
428 let cube = ((color - 16) / op) % 6;
429 if cube == 0 {
430 0u8
431 } else {
432 wrap((14135 + 10280 * cube) / 256)
433 }
434}
435pub fn get_ansi_rgb(color: usize) -> [u8; 3] {
436 let tuple = rgb_from_ansi256(wrap(color));
437 [tuple.0, tuple.1, tuple.2]
438}
439
440pub fn format_slice_hex<I: IntoIterator<Item: LowerHex>>(items: I, color: bool) -> String {
441 format!(
442 "[{}]",
443 items
444 .into_iter()
445 .map(|el| {
446 let byte = format!("0x{el:02x}");
447 if color {
448 fore(
449 byte,
450 from_byte(
451 u8::from_str_radix(&format!("{el:02x}"), 16).unwrap_or_default(),
452 )
454 .into(),
455 )
456 } else {
457 byte
458 }
459 })
460 .collect::<Vec<String>>()
461 .join(", ")
462 )
463}
464
465pub fn format_slice_display<I: IntoIterator<Item: Display>>(items: I, color: bool) -> String {
466 format!(
467 "[{}]",
468 items
469 .into_iter()
470 .map(|el| {
471 let byte = format!("{el}");
472 if color {
473 fore(byte, from_display(el).into())
474 } else {
475 byte
476 }
477 })
478 .collect::<Vec<String>>()
479 .join(", ")
480 )
481}
482pub fn format_slice_debug<I: IntoIterator<Item: Debug>>(items: I, color: bool) -> String {
483 format!(
484 "[{}]",
485 items
486 .into_iter()
487 .map(|el| {
488 let byte = format!("{el:#?}");
489 if color {
490 fore(byte, from_debug(el).into())
491 } else {
492 byte
493 }
494 })
495 .collect::<Vec<String>>()
496 .join(", ")
497 )
498}
499
500