color_output/color/impl.rs
1use crate::*;
2
3impl ColorDisplay for Color {
4 fn get_str(&self, display_type: DisplayType) -> String {
5 let str: &str = match display_type {
6 DisplayType::Text => match self {
7 Color::Red => RED,
8 Color::Green => GREEN,
9 Color::Blue => BLUE,
10 Color::Yellow => YELLOW,
11 Color::Black => BLACK,
12 Color::White => WHITE,
13 Color::Default => DEFAULT,
14 Color::Magenta => MAGENTA,
15 Color::Cyan => CYAN,
16 },
17 DisplayType::Background => match self {
18 Color::Red => BG_RED,
19 Color::Green => BG_GREEN,
20 Color::Blue => BG_BLUE,
21 Color::Yellow => BG_YELLOW,
22 Color::Black => BG_BLACK,
23 Color::White => BG_WHITE,
24 Color::Default => DEFAULT,
25 Color::Magenta => BG_MAGENTA,
26 Color::Cyan => BG_CYAN,
27 },
28 };
29 str.to_string()
30 }
31}
32
33impl Display for Color {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 write!(f, "{}", self.get_str(DisplayType::Text))
36 }
37}
38
39impl Display for ColorType {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 write!(f, "{}", self.get_str(DisplayType::Text))
42 }
43}
44
45impl ColorDisplay for ColorType {
46 fn get_str(&self, display_type: DisplayType) -> String {
47 match self {
48 ColorType::Color256(fg) => match display_type {
49 DisplayType::Text => color256_fg_color(*fg),
50 DisplayType::Background => color256_bg_color(*fg),
51 },
52 ColorType::Rgb(r, g, b) => match display_type {
53 DisplayType::Text => rgb_fg_color(*r, *g, *b),
54 DisplayType::Background => rgb_bg_color(*r, *g, *b),
55 },
56 ColorType::Use(color) => color.get_str(display_type),
57 }
58 }
59}
60
61impl Default for ColorType {
62 fn default() -> Self {
63 ColorType::Use(Color::Default)
64 }
65}
66
67impl ColorContrast {
68 /// Calculates the relative luminance of a color.
69 ///
70 /// # Arguments
71 ///
72 /// - `u8` - Red component (0-255)
73 /// - `u8` - Green component (0-255)
74 /// - `u8` - Blue component (0-255)
75 ///
76 /// # Returns
77 ///
78 /// - `f64` - Relative luminance value (0.0-1.0)
79 pub fn calculate_luminance(r: u8, g: u8, b: u8) -> f64 {
80 let r_norm: f64 = r as f64 / 255.0;
81 let g_norm: f64 = g as f64 / 255.0;
82 let b_norm: f64 = b as f64 / 255.0;
83 let r_linear: f64 = if r_norm <= 0.03928 {
84 r_norm / 12.92
85 } else {
86 ((r_norm + 0.055) / 1.055).powf(2.4)
87 };
88 let g_linear: f64 = if g_norm <= 0.03928 {
89 g_norm / 12.92
90 } else {
91 ((g_norm + 0.055) / 1.055).powf(2.4)
92 };
93 let b_linear: f64 = if b_norm <= 0.03928 {
94 b_norm / 12.92
95 } else {
96 ((b_norm + 0.055) / 1.055).powf(2.4)
97 };
98 0.2126 * r_linear + 0.7152 * g_linear + 0.0722 * b_linear
99 }
100
101 /// Calculates the contrast ratio between two colors.
102 ///
103 /// # Arguments
104 ///
105 /// - `(u8, u8, u8)` - First color RGB values
106 /// - `(u8, u8, u8)` - Second color RGB values
107 ///
108 /// # Returns
109 ///
110 /// - `f64` - Contrast ratio (1.0-21.0)
111 pub fn calculate_contrast_ratio(color1: (u8, u8, u8), color2: (u8, u8, u8)) -> f64 {
112 let lum1: f64 = Self::calculate_luminance(color1.0, color1.1, color1.2);
113 let lum2: f64 = Self::calculate_luminance(color2.0, color2.1, color2.2);
114 let lighter: f64 = lum1.max(lum2);
115 let darker: f64 = lum1.min(lum2);
116 (lighter + 0.05) / (darker + 0.05)
117 }
118
119 /// Extracts RGB values from ColorType.
120 ///
121 /// # Arguments
122 ///
123 /// - `&ColorType` - Color to extract RGB from
124 ///
125 /// # Returns
126 ///
127 /// - `(u8, u8, u8)` - RGB values
128 pub fn extract_rgb_from_color_type(color: &ColorType) -> (u8, u8, u8) {
129 match color {
130 ColorType::Rgb(r, g, b) => (*r, *g, *b),
131 ColorType::Color256(hex) => {
132 let r: u8 = ((hex >> 16) & 0xFF) as u8;
133 let g: u8 = ((hex >> 8) & 0xFF) as u8;
134 let b: u8 = (hex & 0xFF) as u8;
135 (r, g, b)
136 }
137 ColorType::Use(color) => {
138 use super::r#enum::Color;
139 match color {
140 Color::Default => (128, 128, 128),
141 Color::Black => (0, 0, 0),
142 Color::Red => (255, 0, 0),
143 Color::Green => (0, 255, 0),
144 Color::Yellow => (255, 255, 0),
145 Color::Blue => (0, 0, 255),
146 Color::Magenta => (255, 0, 255),
147 Color::Cyan => (0, 255, 255),
148 Color::White => (255, 255, 255),
149 }
150 }
151 }
152 }
153
154 /// Checks if two colors have sufficient contrast for readability.
155 ///
156 /// # Arguments
157 ///
158 /// - `&ColorType` - Text color
159 /// - `&ColorType` - Background color
160 ///
161 /// # Returns
162 ///
163 /// - `bool` - Whether contrast is sufficient (ratio >= 4.5)
164 pub fn has_sufficient_contrast(text_color: &ColorType, bg_color: &ColorType) -> bool {
165 let text_rgb: (u8, u8, u8) = Self::extract_rgb_from_color_type(text_color);
166 let bg_rgb: (u8, u8, u8) = Self::extract_rgb_from_color_type(bg_color);
167 let ratio: f64 = Self::calculate_contrast_ratio(text_rgb, bg_rgb);
168 ratio >= 4.5
169 }
170
171 /// Automatically adjusts text color to ensure sufficient contrast with background.
172 ///
173 /// # Arguments
174 ///
175 /// - `&ColorType` - Original text color
176 /// - `&ColorType` - Background color
177 ///
178 /// # Returns
179 ///
180 /// - `ColorType` - Adjusted text color with sufficient contrast
181 pub fn ensure_sufficient_contrast(text_color: &ColorType, bg_color: &ColorType) -> ColorType {
182 if Self::has_sufficient_contrast(text_color, bg_color) {
183 return *text_color;
184 }
185 let text_rgb: (u8, u8, u8) = Self::extract_rgb_from_color_type(text_color);
186 let bg_rgb: (u8, u8, u8) = Self::extract_rgb_from_color_type(bg_color);
187 let bg_luminance: f64 = Self::calculate_luminance(bg_rgb.0, bg_rgb.1, bg_rgb.2);
188 if bg_luminance > 0.5 {
189 Self::darken_color_for_contrast(text_rgb, bg_rgb)
190 } else {
191 Self::lighten_color_for_contrast(text_rgb, bg_rgb)
192 }
193 }
194
195 /// Darkens a color while preserving its hue for better contrast against light backgrounds.
196 ///
197 /// # Arguments
198 ///
199 /// - `(u8, u8, u8)` - Original text color RGB
200 /// - `(u8, u8, u8)` - Background color RGB
201 ///
202 /// # Returns
203 ///
204 /// - `ColorType` - Darkened color with sufficient contrast
205 pub fn darken_color_for_contrast(text_rgb: (u8, u8, u8), bg_rgb: (u8, u8, u8)) -> ColorType {
206 let (r, g, b): (u8, u8, u8) = text_rgb;
207 let max_component: u8 = r.max(g).max(b);
208 if max_component == 0 {
209 return ColorType::Rgb(0, 0, 0);
210 }
211 let scale_factor: f64 = 0.3;
212 let new_r: u8 = ((r as f64 * scale_factor) as u8).min(80);
213 let new_g: u8 = ((g as f64 * scale_factor) as u8).min(80);
214 let new_b: u8 = ((b as f64 * scale_factor) as u8).min(80);
215 let result_color: ColorType = ColorType::Rgb(new_r, new_g, new_b);
216 if Self::calculate_contrast_ratio((new_r, new_g, new_b), bg_rgb) >= 4.5 {
217 result_color
218 } else {
219 ColorType::Rgb(0, 0, 0)
220 }
221 }
222
223 /// Lightens a color while preserving its hue for better contrast against dark backgrounds.
224 ///
225 /// # Arguments
226 ///
227 /// - `(u8, u8, u8)` - Original text color RGB
228 /// - `(u8, u8, u8)` - Background color RGB
229 ///
230 /// # Returns
231 ///
232 /// - `ColorType` - Lightened color with sufficient contrast
233 pub fn lighten_color_for_contrast(text_rgb: (u8, u8, u8), bg_rgb: (u8, u8, u8)) -> ColorType {
234 let (r, g, b): (u8, u8, u8) = text_rgb;
235 let scale_factor: f64 = 2.5;
236 let new_r: u8 = ((r as f64 * scale_factor) as u8).max(200);
237 let new_g: u8 = ((g as f64 * scale_factor) as u8).max(200);
238 let new_b: u8 = ((b as f64 * scale_factor) as u8).max(200);
239 let result_color: ColorType = ColorType::Rgb(new_r, new_g, new_b);
240 if Self::calculate_contrast_ratio((new_r, new_g, new_b), bg_rgb) >= 4.5 {
241 result_color
242 } else {
243 ColorType::Rgb(255, 255, 255)
244 }
245 }
246}
247
248/// Default implementation for ColorOutput with empty configuration.
249impl<'a> Default for ColorOutput<'a> {
250 #[inline(always)]
251 fn default() -> Self {
252 ColorOutput {
253 text: "",
254 color: ColorType::default(),
255 bg_color: ColorType::default(),
256 bold: false,
257 endl: false,
258 }
259 }
260}
261
262impl<'a> ColorOutput<'a> {
263 /// Executes the output operation with current configuration.
264 ///
265 /// # Returns
266 ///
267 /// - `()` - No return value
268 #[inline(always)]
269 pub fn output(self) {
270 output(self);
271 }
272}
273
274/// Implementation of ColorOutputBuilder methods.
275impl<'a> Default for ColorOutputBuilder<'a> {
276 #[inline(always)]
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282impl<'a> ColorOutputBuilder<'a> {
283 /// Creates a new ColorOutputBuilder instance.
284 ///
285 /// # Returns
286 ///
287 /// - `ColorOutputBuilder<'a>` - The new builder instance.
288 #[inline(always)]
289 pub fn new() -> Self {
290 Self {
291 output: ColorOutput::default(),
292 }
293 }
294
295 /// Creates a new ColorOutputBuilder from existing ColorOutput.
296 ///
297 /// # Arguments
298 ///
299 /// - `ColorOutput` - The output configuration to initialize from
300 ///
301 /// # Returns
302 ///
303 /// - `ColorOutputBuilder` - The new builder instance
304 #[inline(always)]
305 pub fn new_from(output: ColorOutput<'a>) -> Self {
306 Self { output }
307 }
308
309 /// Sets the output text.
310 ///
311 /// # Arguments
312 ///
313 /// - `&str` - The text content to display
314 ///
315 /// # Returns
316 ///
317 /// - `&mut Self` - The builder for method chaining
318 #[inline(always)]
319 pub fn text(&mut self, text: &'a str) -> &mut Self {
320 self.output.text = text;
321 self
322 }
323
324 /// Sets the text color.
325 ///
326 /// # Arguments
327 ///
328 /// - `ColorType` - The color to apply to text
329 ///
330 /// # Returns
331 ///
332 /// - `&mut Self` - The builder for method chaining
333 #[inline(always)]
334 pub fn color(&mut self, color: ColorType) -> &mut Self {
335 self.output.color = color;
336 self
337 }
338
339 /// Sets the background color.
340 ///
341 /// # Arguments
342 ///
343 /// - `ColorType` - The background color type.
344 ///
345 /// # Returns
346 ///
347 /// - `&mut Self` - The builder for chaining.
348 #[inline(always)]
349 pub fn bg_color(&mut self, bg_color: ColorType) -> &mut Self {
350 self.output.bg_color = bg_color;
351 self
352 }
353
354 /// Sets bold text style.
355 ///
356 /// # Arguments
357 ///
358 /// - `bool` - Whether to use bold style.
359 ///
360 /// # Returns
361 ///
362 /// - `&mut Self` - The builder for chaining.
363 #[inline(always)]
364 pub fn bold(&mut self, bold: bool) -> &mut Self {
365 self.output.bold = bold;
366 self
367 }
368
369 /// Sets whether to add newline at end.
370 ///
371 /// # Arguments
372 ///
373 /// - `bool` - Whether to add newline.
374 ///
375 /// # Returns
376 ///
377 /// - `&mut Self` - The builder for chaining.
378 #[inline(always)]
379 pub fn endl(&mut self, endl: bool) -> &mut Self {
380 self.output.endl = endl;
381 self
382 }
383
384 /// Builds the final ColorOutput.
385 ///
386 /// # Returns
387 ///
388 /// - `ColorOutput<'a>` - The constructed output.
389 #[inline(always)]
390 pub fn build(&'_ self) -> ColorOutput<'_> {
391 self.output
392 }
393
394 /// ColorOutputs the current state.
395 ///
396 /// # Returns
397 ///
398 /// - `()` - No return value.
399 #[inline(always)]
400 pub fn output(&self) {
401 output(self.output);
402 }
403}
404
405impl<'a> Default for ColorOutputList<'a> {
406 /// Provides a default implementation for ColorOutputList.
407 ///
408 /// # Returns
409 ///
410 /// - `ColorOutputList` - New instance containing a single default ColorOutput
411 #[inline(always)]
412 fn default() -> Self {
413 ColorOutputList(vec![ColorOutput::<'a>::default()])
414 }
415}
416
417impl<'a> Deref for ColorOutputList<'a> {
418 type Target = Vec<ColorOutput<'a>>;
419
420 /// Dereferences ColorOutputList to its internal Vec of ColorOutputs.
421 ///
422 /// # Returns
423 ///
424 /// - `&Vec<ColorOutput>` - Reference to the internal vector of outputs
425 #[inline(always)]
426 fn deref(&self) -> &Self::Target {
427 &self.0
428 }
429}
430
431impl<'a> IntoIterator for &'a ColorOutputList<'a> {
432 type Item = &'a ColorOutput<'a>;
433 type IntoIter = Iter<'a, ColorOutput<'a>>;
434
435 /// Returns an iterator over the elements of the internal Vec.
436 ///
437 /// # Returns
438 ///
439 /// - `Iter<ColorOutput>` - Iterator over references to ColorOutput elements
440 #[inline(always)]
441 fn into_iter(self) -> Self::IntoIter {
442 self.0.iter()
443 }
444}
445
446impl<'a> ColorOutputList<'a> {
447 /// Provides an iterator over the elements in the internal `Vec<ColorOutput<'a>>`.
448 ///
449 /// # Returns
450 /// - `Iter<'_, ColorOutput<'a>>`: An iterator over references to `ColorOutput` elements.
451 #[inline(always)]
452 pub fn iter(&self) -> std::slice::Iter<'_, ColorOutput<'a>> {
453 self.0.iter()
454 }
455
456 /// ColorOutputs the content of each `ColorOutput` in the list.
457 ///
458 /// This method clones the `ColorOutputList` and iterates through its elements, calling the `output` method on each cloned `ColorOutput`.
459 ///
460 /// # Returns
461 /// - `()` : Nothing is returned.
462 pub fn output(self) {
463 output_list(&self.to_vec());
464 }
465}
466
467impl<'a> Default for ColorOutputListBuilder<'a> {
468 #[inline(always)]
469 fn default() -> Self {
470 Self::new()
471 }
472}
473
474impl<'a> ColorOutputListBuilder<'a> {
475 /// Creates a new empty ColorOutputListBuilder.
476 ///
477 /// # Returns
478 ///
479 /// - `ColorOutputListBuilder` - New instance with empty output list
480 #[inline(always)]
481 pub fn new() -> Self {
482 Self {
483 output_list: vec![],
484 }
485 }
486
487 /// Creates a new ColorOutputListBuilder from existing outputs.
488 ///
489 /// # Arguments
490 ///
491 /// - `Vec<ColorOutput>` - Collection of outputs to initialize with
492 ///
493 /// # Returns
494 ///
495 /// - `ColorOutputListBuilder` - New instance containing the specified outputs
496 #[inline(always)]
497 pub fn new_from(output_list: Vec<ColorOutput<'a>>) -> Self {
498 Self { output_list }
499 }
500
501 /// Adds an output to the list.
502 ///
503 /// # Arguments
504 ///
505 /// - `ColorOutput` - The output configuration to add
506 ///
507 /// # Returns
508 ///
509 /// - `&mut Self` - The builder for method chaining
510 #[inline(always)]
511 pub fn add(&mut self, output: ColorOutput<'a>) -> &mut Self {
512 self.output_list.push(output);
513 self
514 }
515
516 /// Removes an output item from the list at the specified index.
517 ///
518 /// # Parameters
519 /// - `&mut self`: A mutable reference to the current instance of `ColorOutputListBuilder`.
520 /// - `idx`: The index of the output item to be removed.
521 ///
522 /// # Returns
523 /// - `&mut Self`: A mutable reference to the current instance, allowing for method chaining.
524 ///
525 /// If the index is out of bounds, the list remains unchanged.
526 pub fn remove(&mut self, idx: usize) -> &mut Self {
527 if idx >= self.output_list.len() {
528 return self;
529 }
530 self.output_list.remove(idx);
531 self
532 }
533
534 /// Clears all output items from the output list.
535 ///
536 /// # Parameters
537 /// - `&mut self`: A mutable reference to the current instance of `ColorOutputListBuilder`.
538 #[inline(always)]
539 pub fn clear(&mut self) {
540 self.output_list.clear();
541 }
542
543 /// Runs all output items in the list, executing their output logic.
544 ///
545 /// # Parameters
546 /// - `&mut self`: A mutable reference to the current instance of `ColorOutputListBuilder`.
547 ///
548 /// # Returns
549 /// - `&mut Self`: A mutable reference to the current instance, allowing for method chaining.
550 ///
551 /// The method clones the current output list, clears the original list, and executes
552 /// the output for each cloned item.
553 pub fn run(&mut self) -> &mut Self {
554 let outputs: Vec<ColorOutput<'_>> = self.output_list.to_vec();
555 self.clear();
556 output_list(&outputs);
557 self
558 }
559
560 /// Queries the output item at the specified index.
561 ///
562 /// # Parameters
563 /// - `&self`: An immutable reference to the current instance of `ColorOutputListBuilder`.
564 /// - `idx`: The index of the output item to query.
565 ///
566 /// # Returns
567 /// - `ColorOutput`: The output item at the specified index, or a default output if the index is out of bounds.
568 pub fn query_idx(&'_ self, idx: usize) -> ColorOutput<'_> {
569 if idx >= self.output_list.len() {
570 return ColorOutput::default();
571 }
572 self.output_list[idx]
573 }
574
575 /// Runs the output item at the specified index.
576 ///
577 /// # Parameters
578 /// - `&mut self`: A mutable reference to the current instance of `ColorOutputListBuilder`.
579 /// - `idx`: The index of the output item to run.
580 ///
581 /// # Returns
582 /// - `&mut Self`: A mutable reference to the current instance, allowing for method chaining.
583 ///
584 /// If the index is out of bounds, the list remains unchanged.
585 pub fn run_idx(&mut self, idx: usize) -> &mut Self {
586 if idx >= self.output_list.len() {
587 return self;
588 }
589 let output: ColorOutput<'_> = self.query_idx(idx);
590 output.output();
591 self
592 }
593}