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
6use std::{fmt, ops::BitOr};
7
8use 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)]
39pub struct Color {
40 inner: ColorInner,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
44enum ColorInner {
45 Static(StaticColor<'static>),
46 Allocated(ANSIBuf),
47}
48
49#[rustfmt::skip]
50impl 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
186impl 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
229impl Default for Color {
230 fn default() -> Self {
231 Self {
232 inner: ColorInner::Static(StaticColor::default()),
233 }
234 }
235}
236
237impl 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
246impl From<ANSIBuf> for Color {
247 fn from(color: ANSIBuf) -> Self {
248 Self {
249 inner: ColorInner::Allocated(color),
250 }
251 }
252}
253
254impl 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")]
278impl 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")]
291impl 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
303impl<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
314impl<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
325impl<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
336impl 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)]
353mod 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