1use anstyle::{Ansi256Color, AnsiColor, Effects, RgbColor};
3use thiserror::Error;
4
5use super::{Color, Modifier, Style};
6
7#[derive(Debug, Error, PartialEq, Eq)]
9pub enum TryFromColorError {
10 #[error("cannot convert Ratatui Color to an Ansi256Color as it is not an indexed color")]
11 Ansi256,
12 #[error("cannot convert Ratatui Color to AnsiColor as it is not a 4-bit color")]
13 Ansi,
14 #[error("cannot convert Ratatui Color to RgbColor as it is not an RGB color")]
15 RgbColor,
16}
17
18impl From<Ansi256Color> for Color {
19 fn from(color: Ansi256Color) -> Self {
20 Self::Indexed(color.index())
21 }
22}
23
24impl TryFrom<Color> for Ansi256Color {
25 type Error = TryFromColorError;
26
27 fn try_from(color: Color) -> Result<Self, Self::Error> {
28 match color {
29 Color::Indexed(index) => Ok(Self(index)),
30 _ => Err(TryFromColorError::Ansi256),
31 }
32 }
33}
34
35impl From<AnsiColor> for Color {
36 fn from(value: AnsiColor) -> Self {
37 match value {
38 AnsiColor::Black => Self::Black,
39 AnsiColor::Red => Self::Red,
40 AnsiColor::Green => Self::Green,
41 AnsiColor::Yellow => Self::Yellow,
42 AnsiColor::Blue => Self::Blue,
43 AnsiColor::Magenta => Self::Magenta,
44 AnsiColor::Cyan => Self::Cyan,
45 AnsiColor::White => Self::Gray,
46 AnsiColor::BrightBlack => Self::DarkGray,
47 AnsiColor::BrightRed => Self::LightRed,
48 AnsiColor::BrightGreen => Self::LightGreen,
49 AnsiColor::BrightYellow => Self::LightYellow,
50 AnsiColor::BrightBlue => Self::LightBlue,
51 AnsiColor::BrightMagenta => Self::LightMagenta,
52 AnsiColor::BrightCyan => Self::LightCyan,
53 AnsiColor::BrightWhite => Self::White,
54 }
55 }
56}
57
58impl TryFrom<Color> for AnsiColor {
59 type Error = TryFromColorError;
60
61 fn try_from(color: Color) -> Result<Self, Self::Error> {
62 match color {
63 Color::Black => Ok(Self::Black),
64 Color::Red => Ok(Self::Red),
65 Color::Green => Ok(Self::Green),
66 Color::Yellow => Ok(Self::Yellow),
67 Color::Blue => Ok(Self::Blue),
68 Color::Magenta => Ok(Self::Magenta),
69 Color::Cyan => Ok(Self::Cyan),
70 Color::Gray => Ok(Self::White),
71 Color::DarkGray => Ok(Self::BrightBlack),
72 Color::LightRed => Ok(Self::BrightRed),
73 Color::LightGreen => Ok(Self::BrightGreen),
74 Color::LightYellow => Ok(Self::BrightYellow),
75 Color::LightBlue => Ok(Self::BrightBlue),
76 Color::LightMagenta => Ok(Self::BrightMagenta),
77 Color::LightCyan => Ok(Self::BrightCyan),
78 Color::White => Ok(Self::BrightWhite),
79 _ => Err(TryFromColorError::Ansi),
80 }
81 }
82}
83
84impl From<RgbColor> for Color {
85 fn from(color: RgbColor) -> Self {
86 Self::Rgb(color.r(), color.g(), color.b())
87 }
88}
89
90impl TryFrom<Color> for RgbColor {
91 type Error = TryFromColorError;
92
93 fn try_from(color: Color) -> Result<Self, Self::Error> {
94 match color {
95 Color::Rgb(red, green, blue) => Ok(Self(red, green, blue)),
96 _ => Err(TryFromColorError::RgbColor),
97 }
98 }
99}
100
101impl From<anstyle::Color> for Color {
102 fn from(color: anstyle::Color) -> Self {
103 match color {
104 anstyle::Color::Ansi(ansi_color) => Self::from(ansi_color),
105 anstyle::Color::Ansi256(ansi256_color) => Self::from(ansi256_color),
106 anstyle::Color::Rgb(rgb_color) => Self::from(rgb_color),
107 }
108 }
109}
110
111impl From<Color> for anstyle::Color {
112 fn from(color: Color) -> Self {
113 match color {
114 Color::Rgb(_, _, _) => Self::Rgb(RgbColor::try_from(color).unwrap()),
115 Color::Indexed(_) => Self::Ansi256(Ansi256Color::try_from(color).unwrap()),
116 _ => Self::Ansi(AnsiColor::try_from(color).unwrap()),
117 }
118 }
119}
120
121impl From<Effects> for Modifier {
122 fn from(effect: Effects) -> Self {
123 let mut modifier = Self::empty();
124 if effect.contains(Effects::BOLD) {
125 modifier |= Self::BOLD;
126 }
127 if effect.contains(Effects::DIMMED) {
128 modifier |= Self::DIM;
129 }
130 if effect.contains(Effects::ITALIC) {
131 modifier |= Self::ITALIC;
132 }
133 if effect.contains(Effects::UNDERLINE)
134 || effect.contains(Effects::DOUBLE_UNDERLINE)
135 || effect.contains(Effects::CURLY_UNDERLINE)
136 || effect.contains(Effects::DOTTED_UNDERLINE)
137 || effect.contains(Effects::DASHED_UNDERLINE)
138 {
139 modifier |= Self::UNDERLINED;
140 }
141 if effect.contains(Effects::BLINK) {
142 modifier |= Self::SLOW_BLINK;
143 }
144 if effect.contains(Effects::INVERT) {
145 modifier |= Self::REVERSED;
146 }
147 if effect.contains(Effects::HIDDEN) {
148 modifier |= Self::HIDDEN;
149 }
150 if effect.contains(Effects::STRIKETHROUGH) {
151 modifier |= Self::CROSSED_OUT;
152 }
153 modifier
154 }
155}
156
157impl From<Modifier> for Effects {
158 fn from(modifier: Modifier) -> Self {
159 let mut effects = Self::new();
160 if modifier.contains(Modifier::BOLD) {
161 effects |= Self::BOLD;
162 }
163 if modifier.contains(Modifier::DIM) {
164 effects |= Self::DIMMED;
165 }
166 if modifier.contains(Modifier::ITALIC) {
167 effects |= Self::ITALIC;
168 }
169 if modifier.contains(Modifier::UNDERLINED) {
170 effects |= Self::UNDERLINE;
171 }
172 if modifier.contains(Modifier::SLOW_BLINK) || modifier.contains(Modifier::RAPID_BLINK) {
173 effects |= Self::BLINK;
174 }
175 if modifier.contains(Modifier::REVERSED) {
176 effects |= Self::INVERT;
177 }
178 if modifier.contains(Modifier::HIDDEN) {
179 effects |= Self::HIDDEN;
180 }
181 if modifier.contains(Modifier::CROSSED_OUT) {
182 effects |= Self::STRIKETHROUGH;
183 }
184 effects
185 }
186}
187
188impl From<anstyle::Style> for Style {
189 fn from(style: anstyle::Style) -> Self {
190 Self {
191 fg: style.get_fg_color().map(Color::from),
192 bg: style.get_bg_color().map(Color::from),
193 #[cfg(feature = "underline-color")]
194 underline_color: style.get_underline_color().map(Color::from),
195 add_modifier: style.get_effects().into(),
196 ..Default::default()
197 }
198 }
199}
200
201impl From<Style> for anstyle::Style {
202 fn from(style: Style) -> Self {
203 let mut anstyle_style = Self::new();
204 if let Some(fg) = style.fg {
205 let fg = anstyle::Color::from(fg);
206 anstyle_style = anstyle_style.fg_color(Some(fg));
207 }
208 if let Some(bg) = style.bg {
209 let bg = anstyle::Color::from(bg);
210 anstyle_style = anstyle_style.bg_color(Some(bg));
211 }
212 #[cfg(feature = "underline-color")]
213 if let Some(underline) = style.underline_color {
214 let underline = anstyle::Color::from(underline);
215 anstyle_style = anstyle_style.underline_color(Some(underline));
216 }
217 anstyle_style = anstyle_style.effects(style.add_modifier.into());
218 anstyle_style
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn anstyle_to_color() {
228 let anstyle_color = Ansi256Color(42);
229 let color = Color::from(anstyle_color);
230 assert_eq!(color, Color::Indexed(42));
231 }
232
233 #[test]
234 fn color_to_ansi256color() {
235 let color = Color::Indexed(42);
236 let anstyle_color = Ansi256Color::try_from(color);
237 assert_eq!(anstyle_color, Ok(Ansi256Color(42)));
238 }
239
240 #[test]
241 fn color_to_ansi256color_error() {
242 let color = Color::Rgb(0, 0, 0);
243 let anstyle_color = Ansi256Color::try_from(color);
244 assert_eq!(anstyle_color, Err(TryFromColorError::Ansi256));
245 }
246
247 #[test]
248 fn ansi_color_to_color() {
249 let ansi_color = AnsiColor::Red;
250 let color = Color::from(ansi_color);
251 assert_eq!(color, Color::Red);
252 }
253
254 #[test]
255 fn color_to_ansicolor() {
256 let color = Color::Red;
257 let ansi_color = AnsiColor::try_from(color);
258 assert_eq!(ansi_color, Ok(AnsiColor::Red));
259 }
260
261 #[test]
262 fn color_to_ansicolor_error() {
263 let color = Color::Rgb(0, 0, 0);
264 let ansi_color = AnsiColor::try_from(color);
265 assert_eq!(ansi_color, Err(TryFromColorError::Ansi));
266 }
267
268 #[test]
269 fn rgb_color_to_color() {
270 let rgb_color = RgbColor(255, 0, 0);
271 let color = Color::from(rgb_color);
272 assert_eq!(color, Color::Rgb(255, 0, 0));
273 }
274
275 #[test]
276 fn color_to_rgbcolor() {
277 let color = Color::Rgb(255, 0, 0);
278 let rgb_color = RgbColor::try_from(color);
279 assert_eq!(rgb_color, Ok(RgbColor(255, 0, 0)));
280 }
281
282 #[test]
283 fn color_to_rgbcolor_error() {
284 let color = Color::Indexed(42);
285 let rgb_color = RgbColor::try_from(color);
286 assert_eq!(rgb_color, Err(TryFromColorError::RgbColor));
287 }
288
289 #[test]
290 fn effects_to_modifier() {
291 let effects = Effects::BOLD | Effects::ITALIC;
292 let modifier = Modifier::from(effects);
293 assert!(modifier.contains(Modifier::BOLD));
294 assert!(modifier.contains(Modifier::ITALIC));
295 }
296
297 #[test]
298 fn modifier_to_effects() {
299 let modifier = Modifier::BOLD | Modifier::ITALIC;
300 let effects = Effects::from(modifier);
301 assert!(effects.contains(Effects::BOLD));
302 assert!(effects.contains(Effects::ITALIC));
303 }
304
305 #[test]
306 fn anstyle_style_to_style() {
307 let anstyle_style = anstyle::Style::new()
308 .fg_color(Some(anstyle::Color::Ansi(AnsiColor::Red)))
309 .bg_color(Some(anstyle::Color::Ansi(AnsiColor::Blue)))
310 .underline_color(Some(anstyle::Color::Ansi(AnsiColor::Green)))
311 .effects(Effects::BOLD | Effects::ITALIC);
312 let style = Style::from(anstyle_style);
313 assert_eq!(style.fg, Some(Color::Red));
314 assert_eq!(style.bg, Some(Color::Blue));
315 #[cfg(feature = "underline-color")]
316 assert_eq!(style.underline_color, Some(Color::Green));
317 assert!(style.add_modifier.contains(Modifier::BOLD));
318 assert!(style.add_modifier.contains(Modifier::ITALIC));
319 }
320
321 #[test]
322 fn style_to_anstyle_style() {
323 let style = Style {
324 fg: Some(Color::Red),
325 bg: Some(Color::Blue),
326 #[cfg(feature = "underline-color")]
327 underline_color: Some(Color::Green),
328 add_modifier: Modifier::BOLD | Modifier::ITALIC,
329 ..Default::default()
330 };
331 let anstyle_style = anstyle::Style::from(style);
332 assert_eq!(
333 anstyle_style.get_fg_color(),
334 Some(anstyle::Color::Ansi(AnsiColor::Red))
335 );
336 assert_eq!(
337 anstyle_style.get_bg_color(),
338 Some(anstyle::Color::Ansi(AnsiColor::Blue))
339 );
340 #[cfg(feature = "underline-color")]
341 assert_eq!(
342 anstyle_style.get_underline_color(),
343 Some(anstyle::Color::Ansi(AnsiColor::Green))
344 );
345 assert!(anstyle_style.get_effects().contains(Effects::BOLD));
346 assert!(anstyle_style.get_effects().contains(Effects::ITALIC));
347 }
348}