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 bright(
88 u8::from_str_radix(&word.to_string(), 10)
89 .unwrap_or_else(|_| from_display(word.to_string()))
90 .into(),
91 )
92 .into(),
93 )
94}
95pub fn from_display<T: Display>(word: T) -> u8 {
97 let string = format!("{word}");
98 from_bytes(
99 &u8::from_str_radix(&string, 10)
100 .ok()
101 .or_else(|| u8::from_str_radix(&string, 16).ok())
102 .map(|byte| vec![byte])
103 .or_else(|| {
104 if string.to_lowercase().starts_with("0x") {
105 u8::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
106 .map(|byte| vec![byte])
107 .ok()
108 } else {
109 None
110 }
111 })
112 .map(|byte| vec![byte].into_iter().flatten().collect::<Vec<u8>>())
113 .or_else(|| {
114 u16::from_str_radix(&string, 16)
115 .map(|u| u.to_ne_bytes().to_vec())
116 .ok()
117 })
118 .or_else(|| {
119 if string.to_lowercase().starts_with("0x") {
120 u16::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
121 .map(|u| u.to_ne_bytes().to_vec())
122 .ok()
123 } else {
124 None
125 }
126 })
127 .or_else(|| {
128 u32::from_str_radix(&string, 16)
129 .ok()
130 .map(|u| u.to_ne_bytes().to_vec())
131 })
132 .or_else(|| {
133 if string.to_lowercase().starts_with("0x") {
134 u32::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
135 .map(|u| u.to_ne_bytes().to_vec())
136 .ok()
137 } else {
138 None
139 }
140 })
141 .or_else(|| {
142 u64::from_str_radix(&string, 16)
143 .ok()
144 .map(|u| u.to_ne_bytes().to_vec())
145 })
146 .or_else(|| {
147 if string.to_lowercase().starts_with("0x") {
148 u64::from_str_radix(string.to_lowercase().replacen("0x", "", 1).as_str(), 16)
149 .map(|u| u.to_ne_bytes().to_vec())
150 .ok()
151 } else {
152 None
153 }
154 })
155 .unwrap_or_else(|| string.as_bytes().to_vec()),
156 )
157}
158pub fn from_debug<T: Debug>(word: T) -> u8 {
160 from_bytes(format!("{word:#?}").as_bytes())
161}
162pub fn rgb_from_display<T: Display>(word: T) -> [u8; 3] {
164 rgb_from_bytes(word.to_string().as_bytes())
165}
166
167pub fn from_bytes(bytes: &[u8]) -> u8 {
169 let mut color: u8 = 0;
170 for rgb in rgb_from_bytes(bytes) {
171 color ^= rgb
172 }
173 color
174}
175pub fn rgb_from_bytes(bytes: &[u8]) -> [u8; 3] {
178 merge_rgb(bytes.into_iter().map(|byte| rgb_from_byte(*byte)), false)
179}
180pub fn rgb_from_byte(byte: u8) -> [u8; 3] {
182 let tuple = rgb_from_ansi256(byte);
183 [tuple.0, tuple.1, tuple.2]
184}
185
186pub fn rgb_to_byte(rgb: [u8; 3]) -> u8 {
188 ansi256_from_rgb(rgb)
189}
190pub fn merge_rgb<I: IntoIterator<Item = [u8; 3]> + Clone>(rgbs: I, extra: bool) -> [u8; 3] {
192 let mut result = [0u8; 3];
193 for rgb in rgbs.clone().into_iter() {
194 result[0] ^= rgb[0];
195 result[1] ^= rgb[1];
196 result[2] ^= rgb[2];
197 }
198 if extra {
199 for triple in rgbs.clone().into_iter() {
200 for byte in triple.into_iter() {
201 let rgb = rgb_from_byte(byte);
202 result[0] ^= rgb[0];
203 result[1] ^= rgb[1];
204 result[2] ^= rgb[2];
205 }
206 }
207 }
208 result
209}
210
211pub fn couple(color: usize) -> (u8, u8) {
216 let fore = wrap(color);
217 let back = invert_bw(fore as usize);
218 (fore, back)
219}
220
221#[cfg(feature = "colorsys")]
223pub fn invert_ansi(color: usize) -> u8 {
224 let color = Ansi256::new(wrap(color));
225 let mut hsl = Hsl::from(&Rgb::from(color));
226 hsl.set_lightness(100.0-hsl.lightness());
227 let mut rgb = Rgb::from(&hsl);
228 rgb.invert();
229 let color = Ansi256::from(&rgb);
230 color.code()
231}
232#[cfg(not(feature = "colorsys"))]
234pub fn invert_ansi(color: usize) -> u8 {
235 if is_dark_rgb_band(color) {
236 bright(rgb_to_byte(invert_rgb(rgb_from_byte(wrap(color)))) as usize)
237 } else {
238 dark(rgb_to_byte(invert_rgb(rgb_from_byte(wrap(color)))) as usize)
239 }
240}
241
242#[cfg(feature = "colorsys")]
244pub fn invert_rgb(color: [u8; 3]) -> [u8; 3] {
245 let mut rgb = Rgb::from(color);
246 rgb.invert();
247 rgb.into()
248}
249
250#[cfg(not(feature = "colorsys"))]
252pub fn invert_rgb(color: [u8; 3]) -> [u8; 3] {
253 [255u8 - color[0], 255u8 - color[1], 255u8 - color[2]]
254}
255
256#[cfg(not(feature = "colorsys"))]
258pub fn invert_bw(color: usize) -> u8 {
259 match color {
260 0 | 8 | 16..21 | 52..61 | 88..93 | 232..239 => 231,
261 _ => 16,
262 }
263}
264#[cfg(feature = "colorsys")]
266pub fn invert_bw(color: usize) -> u8 {
267 let color = Ansi256::new(wrap(color));
268 let mut rgb = Rgb::from(color);
269 rgb.grayscale_simple();
270 rgb.invert();
271 Ansi256::from(&rgb).code()
272}
273
274pub fn is_bright_rgb_band(color: usize) -> bool {
276 let color = wrap(color);
277 color >= 0x75
278}
279pub fn bright_rgb_band(color: usize) -> u8 {
281 if !is_bright_rgb_band(color) {
282 0xff - wrap(color)
283 } else {
284 wrap(color)
285 }
286}
287#[cfg(feature = "colorsys")]
289pub fn bright(color: usize) -> u8 {
290 let color = wrap(color);
291 let mut rgb = Rgb::from(Ansi256::new(color));
292 rgb.lighten(50.0);
293 Ansi256::from(rgb).code()
294}
295#[cfg(not(feature = "colorsys"))]
297pub fn bright(color: usize) -> u8 {
298 let color = wrap(color);
299 let [r, g, b] = rgb_from_byte(color);
300 rgb_to_byte([
301 bright_rgb_band(r as usize),
302 bright_rgb_band(g as usize),
303 bright_rgb_band(b as usize),
304 ])
305}
306pub fn is_dark_rgb_band(color: usize) -> bool {
308 let color = wrap(color);
309 color <= 0x75
310}
311pub fn dark_rgb_band(color: usize) -> u8 {
313 let color = wrap(color);
314 if !is_dark_rgb_band(color as usize) {
315 color - ((color / 4) * 3)
316 } else {
317 color
318 }
319}
320#[cfg(feature = "colorsys")]
322pub fn dark(color: usize) -> u8 {
323 let color = wrap(color);
324 let mut rgb = Rgb::from(Ansi256::new(color));
325 rgb.lighten(-50.0);
326 Ansi256::from(rgb).code()
327}
328#[cfg(not(feature = "colorsys"))]
329pub fn dark(color: usize) -> u8 {
330 let color = wrap(color);
331 let [r, g, b] = rgb_from_byte(color);
332 rgb_to_byte([
333 dark_rgb_band(r as usize),
334 dark_rgb_band(g as usize),
335 dark_rgb_band(b as usize),
336 ])
337}
338
339pub fn wrap(color: usize) -> u8 {
341 if color <= u8::MAX.into() {
342 color as u8
343 } else {
344 from_bytes(&color.to_ne_bytes())
345 }
346}
347
348pub fn non_zero_be_bytes(color: usize) -> Vec<u8> {
349 let mut bytes = color.to_be_bytes().to_vec();
350 while bytes.len() > 1 && bytes[0] == 0 {
351 bytes.remove(0);
352 }
353 bytes
354}
355
356pub fn term_cols() -> usize {
358 match ioctl_term_light::cols() {
359 0 => DEFAULT_COLUMNS,
360 cols => cols as usize,
361 }
362}
363
364pub fn from_byte(byte: u8) -> u8 {
366 byte
367}
368
369pub fn byte(byte: u8) -> String {
371 let (fg, bg) = couple(from_byte(byte).into());
372 ansi(byte, fg as usize, bg as usize)
373}
374
375pub fn byte_hex(byte: u8) -> String {
377 let (fg, bg) = couple(from_byte(byte).into());
378 ansi(format!("0x{byte:02x}"), fg as usize, bg as usize)
379}
380
381pub fn byte_bin(byte: u8) -> String {
383 let (fg, bg) = couple(from_byte(byte).into());
384 ansi(format!("0b{byte:08b}"), fg as usize, bg as usize)
385}
386
387pub const STD_COLORS: [u8; 48] = [
388 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, ];
405pub fn cube_ansi_256(color: usize, op: usize) -> u8 {
406 let color = wrap(color) as usize;
407 let cube = ((color - 16) / op) % 6;
408 if cube == 0 {
409 0u8
410 } else {
411 wrap((14135 + 10280 * cube) / 256)
412 }
413}
414pub fn get_ansi_rgb(color: usize) -> [u8; 3] {
415 let tuple = rgb_from_ansi256(wrap(color));
416 [tuple.0, tuple.1, tuple.2]
417}
418
419pub fn format_slice_hex<I: IntoIterator<Item: LowerHex>>(items: I, color: bool) -> String {
420 format!(
421 "[{}]",
422 items
423 .into_iter()
424 .map(|el| {
425 let byte = format!("0x{el:02x}");
426 if color {
427 fore(
428 byte,
429 from_byte(
430 u8::from_str_radix(&format!("{el:02x}"), 16).unwrap_or_default(),
431 )
433 .into(),
434 )
435 } else {
436 byte
437 }
438 })
439 .collect::<Vec<String>>()
440 .join(", ")
441 )
442}
443
444pub fn format_slice_display<I: IntoIterator<Item: Display>>(items: I, color: bool) -> String {
445 format!(
446 "[{}]",
447 items
448 .into_iter()
449 .map(|el| {
450 let byte = format!("{el}");
451 if color {
452 fore(byte, from_display(el).into())
453 } else {
454 byte
455 }
456 })
457 .collect::<Vec<String>>()
458 .join(", ")
459 )
460}
461pub fn format_slice_debug<I: IntoIterator<Item: Debug>>(items: I, color: bool) -> String {
462 format!(
463 "[{}]",
464 items
465 .into_iter()
466 .map(|el| {
467 let byte = format!("{el:#?}");
468 if color {
469 fore(byte, from_debug(el).into())
470 } else {
471 byte
472 }
473 })
474 .collect::<Vec<String>>()
475 .join(", ")
476 )
477}
478
479