1 | //! This module contains a configuration of a [`Border`] or a [`Table`] to set its borders color via [`Color`]. |
2 | //! |
3 | //! [`Border`]: crate::settings::Border |
4 | //! [`Table`]: crate::Table |
5 | |
6 | use std::{fmt, ops::BitOr}; |
7 | |
8 | use crate::{ |
9 | grid::{ |
10 | ansi::{ANSIBuf, ANSIFmt, ANSIStr as StaticColor}, |
11 | config::{ColoredConfig, Entity}, |
12 | }, |
13 | settings::{CellOption, TableOption}, |
14 | }; |
15 | |
16 | /// Color represents a color which can be set to things like [`Border`], [`Padding`] and [`Margin`]. |
17 | /// |
18 | /// # Example |
19 | /// |
20 | /// ``` |
21 | /// use tabled::{settings::Color, Table}; |
22 | /// |
23 | /// let data = [ |
24 | /// (0u8, "Hello" ), |
25 | /// (1u8, "World" ), |
26 | /// ]; |
27 | /// |
28 | /// let table = Table::new(data) |
29 | /// .with(Color::BG_BLUE) |
30 | /// .to_string(); |
31 | /// |
32 | /// println!("{}" , table); |
33 | /// ``` |
34 | /// |
35 | /// [`Padding`]: crate::settings::Padding |
36 | /// [`Margin`]: crate::settings::Margin |
37 | /// [`Border`]: crate::settings::Border |
38 | #[derive (Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
39 | pub struct Color { |
40 | inner: ColorInner, |
41 | } |
42 | |
43 | #[derive (Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] |
44 | enum ColorInner { |
45 | Static(StaticColor<'static>), |
46 | Allocated(ANSIBuf), |
47 | } |
48 | |
49 | #[rustfmt::skip] |
50 | impl Color { |
51 | /// A color representation. |
52 | /// |
53 | /// Notice that the colors are constants so you can't combine them. |
54 | pub const FG_BLACK: Self = Self::new_static(" \u{1b}[30m" , " \u{1b}[39m" ); |
55 | /// A color representation. |
56 | /// |
57 | /// Notice that the colors are constants so you can't combine them. |
58 | pub const FG_BLUE: Self = Self::new_static(" \u{1b}[34m" , " \u{1b}[39m" ); |
59 | /// A color representation. |
60 | /// |
61 | /// Notice that the colors are constants so you can't combine them. |
62 | pub const FG_BRIGHT_BLACK: Self = Self::new_static(" \u{1b}[90m" , " \u{1b}[39m" ); |
63 | /// A color representation. |
64 | /// |
65 | /// Notice that the colors are constants so you can't combine them. |
66 | pub const FG_BRIGHT_BLUE: Self = Self::new_static(" \u{1b}[94m" , " \u{1b}[39m" ); |
67 | /// A color representation. |
68 | /// |
69 | /// Notice that the colors are constants so you can't combine them. |
70 | pub const FG_BRIGHT_CYAN: Self = Self::new_static(" \u{1b}[96m" , " \u{1b}[39m" ); |
71 | /// A color representation. |
72 | /// |
73 | /// Notice that the colors are constants so you can't combine them. |
74 | pub const FG_BRIGHT_GREEN: Self = Self::new_static(" \u{1b}[92m" , " \u{1b}[39m" ); |
75 | /// A color representation. |
76 | /// |
77 | /// Notice that the colors are constants so you can't combine them. |
78 | pub const FG_BRIGHT_MAGENTA: Self = Self::new_static(" \u{1b}[95m" , " \u{1b}[39m" ); |
79 | /// A color representation. |
80 | /// |
81 | /// Notice that the colors are constants so you can't combine them. |
82 | pub const FG_BRIGHT_RED: Self = Self::new_static(" \u{1b}[91m" , " \u{1b}[39m" ); |
83 | /// A color representation. |
84 | /// |
85 | /// Notice that the colors are constants so you can't combine them. |
86 | pub const FG_BRIGHT_WHITE: Self = Self::new_static(" \u{1b}[97m" , " \u{1b}[39m" ); |
87 | /// A color representation. |
88 | /// |
89 | /// Notice that the colors are constants so you can't combine them. |
90 | pub const FG_BRIGHT_YELLOW: Self = Self::new_static(" \u{1b}[93m" , " \u{1b}[39m" ); |
91 | /// A color representation. |
92 | /// |
93 | /// Notice that the colors are constants so you can't combine them. |
94 | pub const FG_CYAN: Self = Self::new_static(" \u{1b}[36m" , " \u{1b}[39m" ); |
95 | /// A color representation. |
96 | /// |
97 | /// Notice that the colors are constants so you can't combine them. |
98 | pub const FG_GREEN: Self = Self::new_static(" \u{1b}[32m" , " \u{1b}[39m" ); |
99 | /// A color representation. |
100 | /// |
101 | /// Notice that the colors are constants so you can't combine them. |
102 | pub const FG_MAGENTA: Self = Self::new_static(" \u{1b}[35m" , " \u{1b}[39m" ); |
103 | /// A color representation. |
104 | /// |
105 | /// Notice that the colors are constants so you can't combine them. |
106 | pub const FG_RED: Self = Self::new_static(" \u{1b}[31m" , " \u{1b}[39m" ); |
107 | /// A color representation. |
108 | /// |
109 | /// Notice that the colors are constants so you can't combine them. |
110 | pub const FG_WHITE: Self = Self::new_static(" \u{1b}[37m" , " \u{1b}[39m" ); |
111 | /// A color representation. |
112 | /// |
113 | /// Notice that the colors are constants so you can't combine them. |
114 | pub const FG_YELLOW: Self = Self::new_static(" \u{1b}[33m" , " \u{1b}[39m" ); |
115 | /// A color representation. |
116 | /// |
117 | /// Notice that the colors are constants so you can't combine them. |
118 | |
119 | pub const BG_BLACK: Self = Self::new_static(" \u{1b}[40m" , " \u{1b}[49m" ); |
120 | /// A color representation. |
121 | /// |
122 | /// Notice that the colors are constants so you can't combine them. |
123 | pub const BG_BLUE: Self = Self::new_static(" \u{1b}[44m" , " \u{1b}[49m" ); |
124 | /// A color representation. |
125 | /// |
126 | /// Notice that the colors are constants so you can't combine them. |
127 | pub const BG_BRIGHT_BLACK: Self = Self::new_static(" \u{1b}[100m" , " \u{1b}[49m" ); |
128 | /// A color representation. |
129 | /// |
130 | /// Notice that the colors are constants so you can't combine them. |
131 | pub const BG_BRIGHT_BLUE: Self = Self::new_static(" \u{1b}[104m" , " \u{1b}[49m" ); |
132 | /// A color representation. |
133 | /// |
134 | /// Notice that the colors are constants so you can't combine them. |
135 | pub const BG_BRIGHT_CYAN: Self = Self::new_static(" \u{1b}[106m" , " \u{1b}[49m" ); |
136 | /// A color representation. |
137 | /// |
138 | /// Notice that the colors are constants so you can't combine them. |
139 | pub const BG_BRIGHT_GREEN: Self = Self::new_static(" \u{1b}[102m" , " \u{1b}[49m" ); |
140 | /// A color representation. |
141 | /// |
142 | /// Notice that the colors are constants so you can't combine them. |
143 | pub const BG_BRIGHT_MAGENTA: Self = Self::new_static(" \u{1b}[105m" , " \u{1b}[49m" ); |
144 | /// A color representation. |
145 | /// |
146 | /// Notice that the colors are constants so you can't combine them. |
147 | pub const BG_BRIGHT_RED: Self = Self::new_static(" \u{1b}[101m" , " \u{1b}[49m" ); |
148 | /// A color representation. |
149 | /// |
150 | /// Notice that the colors are constants so you can't combine them. |
151 | pub const BG_BRIGHT_WHITE: Self = Self::new_static(" \u{1b}[107m" , " \u{1b}[49m" ); |
152 | /// A color representation. |
153 | /// |
154 | /// Notice that the colors are constants so you can't combine them. |
155 | pub const BG_BRIGHT_YELLOW: Self = Self::new_static(" \u{1b}[103m" , " \u{1b}[49m" ); |
156 | /// A color representation. |
157 | /// |
158 | /// Notice that the colors are constants so you can't combine them. |
159 | pub const BG_CYAN: Self = Self::new_static(" \u{1b}[46m" , " \u{1b}[49m" ); |
160 | /// A color representation. |
161 | /// |
162 | /// Notice that the colors are constants so you can't combine them. |
163 | pub const BG_GREEN: Self = Self::new_static(" \u{1b}[42m" , " \u{1b}[49m" ); |
164 | /// A color representation. |
165 | /// |
166 | /// Notice that the colors are constants so you can't combine them. |
167 | pub const BG_MAGENTA: Self = Self::new_static(" \u{1b}[45m" , " \u{1b}[49m" ); |
168 | /// A color representation. |
169 | /// |
170 | /// Notice that the colors are constants so you can't combine them. |
171 | pub const BG_RED: Self = Self::new_static(" \u{1b}[41m" , " \u{1b}[49m" ); |
172 | /// A color representation. |
173 | /// |
174 | /// Notice that the colors are constants so you can't combine them. |
175 | pub const BG_WHITE: Self = Self::new_static(" \u{1b}[47m" , " \u{1b}[49m" ); |
176 | /// A color representation. |
177 | /// |
178 | /// Notice that the colors are constants so you can't combine them. |
179 | pub const BG_YELLOW: Self = Self::new_static(" \u{1b}[43m" , " \u{1b}[49m" ); |
180 | /// A color representation. |
181 | /// |
182 | /// Notice that the colors are constants so you can't combine them. |
183 | pub const BOLD: Self = Self::new_static(" \u{1b}[1m" , " \u{1b}[22m" ); |
184 | } |
185 | |
186 | impl Color { |
187 | /// Creates a new [`Color`]` instance, with ANSI prefix and ANSI suffix. |
188 | /// You can use [`TryFrom`] to construct it from [`String`]. |
189 | pub fn new<P, S>(prefix: P, suffix: S) -> Self |
190 | where |
191 | P: Into<String>, |
192 | S: Into<String>, |
193 | { |
194 | let color = ANSIBuf::new(prefix, suffix); |
195 | let inner = ColorInner::Allocated(color); |
196 | |
197 | Self { inner } |
198 | } |
199 | |
200 | /// Creates a new empty [`Color`]`. |
201 | pub fn empty() -> Self { |
202 | Self::new_static("" , "" ) |
203 | } |
204 | |
205 | const fn new_static(prefix: &'static str, suffix: &'static str) -> Self { |
206 | let color = StaticColor::new(prefix, suffix); |
207 | let inner = ColorInner::Static(color); |
208 | |
209 | Self { inner } |
210 | } |
211 | |
212 | /// Return a prefix. |
213 | pub fn get_prefix(&self) -> &str { |
214 | match &self.inner { |
215 | ColorInner::Static(color) => color.get_prefix(), |
216 | ColorInner::Allocated(color) => color.get_prefix(), |
217 | } |
218 | } |
219 | |
220 | /// Return a suffix. |
221 | pub fn get_suffix(&self) -> &str { |
222 | match &self.inner { |
223 | ColorInner::Static(color) => color.get_suffix(), |
224 | ColorInner::Allocated(color) => color.get_suffix(), |
225 | } |
226 | } |
227 | } |
228 | |
229 | impl Default for Color { |
230 | fn default() -> Self { |
231 | Self { |
232 | inner: ColorInner::Static(StaticColor::default()), |
233 | } |
234 | } |
235 | } |
236 | |
237 | impl From<Color> for ANSIBuf { |
238 | fn from(color: Color) -> Self { |
239 | match color.inner { |
240 | ColorInner::Static(color: ANSIStr<'static>) => ANSIBuf::from(color), |
241 | ColorInner::Allocated(color: ANSIBuf) => color, |
242 | } |
243 | } |
244 | } |
245 | |
246 | impl From<ANSIBuf> for Color { |
247 | fn from(color: ANSIBuf) -> Self { |
248 | Self { |
249 | inner: ColorInner::Allocated(color), |
250 | } |
251 | } |
252 | } |
253 | |
254 | impl BitOr for Color { |
255 | type Output = Color; |
256 | |
257 | fn bitor(self, rhs: Self) -> Self::Output { |
258 | let l_prefix: &str = self.get_prefix(); |
259 | let l_suffix: &str = self.get_suffix(); |
260 | let r_prefix: &str = rhs.get_prefix(); |
261 | let r_suffix: &str = rhs.get_suffix(); |
262 | |
263 | let mut prefix: String = l_prefix.to_string(); |
264 | if l_prefix != r_prefix { |
265 | prefix.push_str(string:r_prefix); |
266 | } |
267 | |
268 | let mut suffix: String = l_suffix.to_string(); |
269 | if l_suffix != r_suffix { |
270 | suffix.push_str(string:r_suffix); |
271 | } |
272 | |
273 | Self::new(prefix, suffix) |
274 | } |
275 | } |
276 | |
277 | #[cfg (feature = "ansi" )] |
278 | impl std::convert::TryFrom<&str> for Color { |
279 | type Error = (); |
280 | |
281 | fn try_from(value: &str) -> Result<Self, Self::Error> { |
282 | let buf = ANSIBuf::try_from(value)?; |
283 | |
284 | Ok(Color { |
285 | inner: ColorInner::Allocated(buf), |
286 | }) |
287 | } |
288 | } |
289 | |
290 | #[cfg (feature = "ansi" )] |
291 | impl std::convert::TryFrom<String> for Color { |
292 | type Error = (); |
293 | |
294 | fn try_from(value: String) -> Result<Self, Self::Error> { |
295 | let buf = ANSIBuf::try_from(value)?; |
296 | |
297 | Ok(Color { |
298 | inner: ColorInner::Allocated(buf), |
299 | }) |
300 | } |
301 | } |
302 | |
303 | impl<R, D> TableOption<R, ColoredConfig, D> for Color { |
304 | fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) { |
305 | let color: ANSIBuf = self.into(); |
306 | let _ = cfg.set_color(pos:Entity::Global, color); |
307 | } |
308 | |
309 | fn hint_change(&self) -> Option<Entity> { |
310 | None |
311 | } |
312 | } |
313 | |
314 | impl<R> CellOption<R, ColoredConfig> for Color { |
315 | fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) { |
316 | let color: ANSIBuf = self.into(); |
317 | let _ = cfg.set_color(pos:entity, color); |
318 | } |
319 | |
320 | fn hint_change(&self) -> Option<Entity> { |
321 | None |
322 | } |
323 | } |
324 | |
325 | impl<R> CellOption<R, ColoredConfig> for &Color { |
326 | fn change(self, _: &mut R, cfg: &mut ColoredConfig, entity: Entity) { |
327 | let color: ANSIBuf = self.clone().into(); |
328 | let _ = cfg.set_color(pos:entity, color); |
329 | } |
330 | |
331 | fn hint_change(&self) -> Option<Entity> { |
332 | None |
333 | } |
334 | } |
335 | |
336 | impl ANSIFmt for Color { |
337 | fn fmt_ansi_prefix<W: fmt::Write>(&self, f: &mut W) -> fmt::Result { |
338 | match &self.inner { |
339 | ColorInner::Static(color: &ANSIStr<'static>) => color.fmt_ansi_prefix(f), |
340 | ColorInner::Allocated(color: &ANSIBuf) => color.fmt_ansi_prefix(f), |
341 | } |
342 | } |
343 | |
344 | fn fmt_ansi_suffix<W: fmt::Write>(&self, f: &mut W) -> fmt::Result { |
345 | match &self.inner { |
346 | ColorInner::Static(color: &ANSIStr<'static>) => color.fmt_ansi_suffix(f), |
347 | ColorInner::Allocated(color: &ANSIBuf) => color.fmt_ansi_suffix(f), |
348 | } |
349 | } |
350 | } |
351 | |
352 | #[cfg (test)] |
353 | mod tests { |
354 | use super::*; |
355 | |
356 | #[cfg (feature = "ansi" )] |
357 | use ::{owo_colors::OwoColorize, std::convert::TryFrom}; |
358 | |
359 | #[test ] |
360 | fn test_xor_operation() { |
361 | assert_eq!( |
362 | Color::FG_BLACK | Color::FG_BLUE, |
363 | Color::new(" \u{1b}[30m \u{1b}[34m" , " \u{1b}[39m" ) |
364 | ); |
365 | assert_eq!( |
366 | Color::FG_BRIGHT_GREEN | Color::BG_BLUE, |
367 | Color::new(" \u{1b}[92m \u{1b}[44m" , " \u{1b}[39m \u{1b}[49m" ) |
368 | ); |
369 | assert_eq!( |
370 | Color::new("..." , "!!!" ) | Color::new("@@@" , "###" ), |
371 | Color::new("...@@@" , "!!!###" ) |
372 | ); |
373 | assert_eq!( |
374 | Color::new("..." , "!!!" ) | Color::new("@@@" , "###" ) | Color::new("$$$" , "%%%" ), |
375 | Color::new("...@@@$$$" , "!!!###%%%" ) |
376 | ); |
377 | } |
378 | |
379 | #[cfg (feature = "ansi" )] |
380 | #[test ] |
381 | fn test_try_from() { |
382 | assert_eq!(Color::try_from("" ), Err(())); |
383 | assert_eq!(Color::try_from("" .red().on_green().to_string()), Err(())); |
384 | assert_eq!(Color::try_from("." ), Ok(Color::new("" , "" ))); |
385 | assert_eq!(Color::try_from("...." ), Ok(Color::new("" , "" ))); |
386 | assert_eq!( |
387 | Color::try_from("." .red().on_green().to_string()), |
388 | Ok(Color::new(" \u{1b}[31m \u{1b}[42m" , " \u{1b}[39m \u{1b}[49m" )) |
389 | ); |
390 | assert_eq!( |
391 | Color::try_from("...." .red().on_green().to_string()), |
392 | Ok(Color::new(" \u{1b}[31m \u{1b}[42m" , " \u{1b}[39m \u{1b}[49m" )) |
393 | ); |
394 | } |
395 | } |
396 | |