git_iris/theme/adapters/
cli.rs1use colored::{ColoredString, Colorize};
6
7use crate::theme::{Gradient, ThemeColor, ThemeStyle};
8
9pub trait ToColoredRgb {
11 fn to_rgb(&self) -> (u8, u8, u8);
13}
14
15impl ToColoredRgb for ThemeColor {
16 fn to_rgb(&self) -> (u8, u8, u8) {
17 (self.r, self.g, self.b)
18 }
19}
20
21pub trait ColoredExt {
23 fn theme_fg(self, color: ThemeColor) -> ColoredString;
25
26 fn theme_bg(self, color: ThemeColor) -> ColoredString;
28
29 fn theme_style(self, style: &ThemeStyle) -> ColoredString;
31}
32
33impl<S: AsRef<str>> ColoredExt for S {
34 fn theme_fg(self, color: ThemeColor) -> ColoredString {
35 self.as_ref().truecolor(color.r, color.g, color.b)
36 }
37
38 fn theme_bg(self, color: ThemeColor) -> ColoredString {
39 self.as_ref().on_truecolor(color.r, color.g, color.b)
40 }
41
42 fn theme_style(self, style: &ThemeStyle) -> ColoredString {
43 let mut result: ColoredString = self.as_ref().into();
44
45 if let Some(fg) = style.fg {
46 result = result.truecolor(fg.r, fg.g, fg.b);
47 }
48
49 if let Some(bg) = style.bg {
50 result = result.on_truecolor(bg.r, bg.g, bg.b);
51 }
52
53 if style.bold {
54 result = result.bold();
55 }
56
57 if style.italic {
58 result = result.italic();
59 }
60
61 if style.underline {
62 result = result.underline();
63 }
64
65 if style.dim {
66 result = result.dimmed();
67 }
68
69 result
70 }
71}
72
73#[allow(clippy::cast_precision_loss, clippy::as_conversions)]
77pub fn gradient_string(text: &str, gradient: &Gradient) -> String {
78 let chars: Vec<char> = text.chars().collect();
79 let len = chars.len().max(1);
80
81 let mut result = String::new();
82
83 for (i, c) in chars.into_iter().enumerate() {
84 let t = if len == 1 {
85 0.0
86 } else {
87 i as f32 / (len - 1) as f32
88 };
89 let color = gradient.at(t);
90 let colored = c.to_string().truecolor(color.r, color.g, color.b);
91 result.push_str(&colored.to_string());
92 }
93
94 result
95}
96
97pub trait ThemeCliExt {
99 fn cli_rgb(&self, token: &str) -> (u8, u8, u8);
101
102 fn cli_colored(&self, text: &str, token: &str) -> ColoredString;
104
105 fn cli_gradient(&self, text: &str, gradient_name: &str) -> String;
107}
108
109impl ThemeCliExt for crate::theme::Theme {
110 fn cli_rgb(&self, token: &str) -> (u8, u8, u8) {
111 self.color(token).to_rgb()
112 }
113
114 fn cli_colored(&self, text: &str, token: &str) -> ColoredString {
115 let color = self.color(token);
116 text.truecolor(color.r, color.g, color.b)
117 }
118
119 fn cli_gradient(&self, text: &str, gradient_name: &str) -> String {
120 if let Some(gradient) = self.get_gradient(gradient_name) {
121 gradient_string(text, gradient)
122 } else {
123 text.to_string()
124 }
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_to_rgb() {
134 let color = ThemeColor::new(225, 53, 255);
135 assert_eq!(color.to_rgb(), (225, 53, 255));
136 }
137
138 #[test]
139 fn test_theme_fg() {
140 let color = ThemeColor::new(255, 0, 0);
141 let result = "test".theme_fg(color);
142 assert!(!result.to_string().is_empty());
144 }
145
146 #[test]
147 fn test_gradient_string() {
148 colored::control::set_override(true);
150
151 let gradient = Gradient::new(vec![ThemeColor::new(255, 0, 0), ThemeColor::new(0, 0, 255)]);
152 let result = gradient_string("test", &gradient);
153 assert!(result.contains("\x1b[") || result.contains("test"));
155
156 colored::control::unset_override();
157 }
158}