1use std::{borrow::Cow, str::FromStr};
2
3/// The 8 standard colors.
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5#[allow(missing_docs)]
6pub enum Color {
7 Black,
8 Red,
9 Green,
10 Yellow,
11 Blue,
12 Magenta,
13 Cyan,
14 White,
15 BrightBlack,
16 BrightRed,
17 BrightGreen,
18 BrightYellow,
19 BrightBlue,
20 BrightMagenta,
21 BrightCyan,
22 BrightWhite,
23 TrueColor { r: u8, g: u8, b: u8 },
24}
25
26#[allow(missing_docs)]
27impl Color {
28 pub fn to_fg_str(&self) -> Cow<'static, str> {
29 match *self {
30 Color::Black => "30".into(),
31 Color::Red => "31".into(),
32 Color::Green => "32".into(),
33 Color::Yellow => "33".into(),
34 Color::Blue => "34".into(),
35 Color::Magenta => "35".into(),
36 Color::Cyan => "36".into(),
37 Color::White => "37".into(),
38 Color::BrightBlack => "90".into(),
39 Color::BrightRed => "91".into(),
40 Color::BrightGreen => "92".into(),
41 Color::BrightYellow => "93".into(),
42 Color::BrightBlue => "94".into(),
43 Color::BrightMagenta => "95".into(),
44 Color::BrightCyan => "96".into(),
45 Color::BrightWhite => "97".into(),
46 Color::TrueColor { r, g, b } => format!("38;2;{};{};{}", r, g, b).into(),
47 }
48 }
49
50 pub fn to_bg_str(&self) -> Cow<'static, str> {
51 match *self {
52 Color::Black => "40".into(),
53 Color::Red => "41".into(),
54 Color::Green => "42".into(),
55 Color::Yellow => "43".into(),
56 Color::Blue => "44".into(),
57 Color::Magenta => "45".into(),
58 Color::Cyan => "46".into(),
59 Color::White => "47".into(),
60 Color::BrightBlack => "100".into(),
61 Color::BrightRed => "101".into(),
62 Color::BrightGreen => "102".into(),
63 Color::BrightYellow => "103".into(),
64 Color::BrightBlue => "104".into(),
65 Color::BrightMagenta => "105".into(),
66 Color::BrightCyan => "106".into(),
67 Color::BrightWhite => "107".into(),
68 Color::TrueColor { r, g, b } => format!("48;2;{};{};{}", r, g, b).into(),
69 }
70 }
71}
72
73impl<'a> From<&'a str> for Color {
74 fn from(src: &str) -> Self {
75 src.parse().unwrap_or(default:Color::White)
76 }
77}
78
79impl From<String> for Color {
80 fn from(src: String) -> Self {
81 src.parse().unwrap_or(default:Color::White)
82 }
83}
84
85impl FromStr for Color {
86 type Err = ();
87
88 fn from_str(src: &str) -> Result<Self, Self::Err> {
89 let src = src.to_lowercase();
90
91 match src.as_ref() {
92 "black" => Ok(Color::Black),
93 "red" => Ok(Color::Red),
94 "green" => Ok(Color::Green),
95 "yellow" => Ok(Color::Yellow),
96 "blue" => Ok(Color::Blue),
97 "magenta" => Ok(Color::Magenta),
98 "purple" => Ok(Color::Magenta),
99 "cyan" => Ok(Color::Cyan),
100 "white" => Ok(Color::White),
101 "bright black" => Ok(Color::BrightBlack),
102 "bright red" => Ok(Color::BrightRed),
103 "bright green" => Ok(Color::BrightGreen),
104 "bright yellow" => Ok(Color::BrightYellow),
105 "bright blue" => Ok(Color::BrightBlue),
106 "bright magenta" => Ok(Color::BrightMagenta),
107 "bright cyan" => Ok(Color::BrightCyan),
108 "bright white" => Ok(Color::BrightWhite),
109 _ => Err(()),
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 pub use super::*;
117
118 mod from_str {
119 pub use super::*;
120
121 macro_rules! make_test {
122 ( $( $name:ident: $src:expr => $dst:expr),* ) => {
123
124 $(
125 #[test]
126 fn $name() {
127 let color : Color = $src.into();
128 assert_eq!($dst, color)
129 }
130 )*
131 }
132 }
133
134 make_test!(
135 black: "black" => Color::Black,
136 red: "red" => Color::Red,
137 green: "green" => Color::Green,
138 yellow: "yellow" => Color::Yellow,
139 blue: "blue" => Color::Blue,
140 magenta: "magenta" => Color::Magenta,
141 purple: "purple" => Color::Magenta,
142 cyan: "cyan" => Color::Cyan,
143 white: "white" => Color::White,
144 brightblack: "bright black" => Color::BrightBlack,
145 brightred: "bright red" => Color::BrightRed,
146 brightgreen: "bright green" => Color::BrightGreen,
147 brightyellow: "bright yellow" => Color::BrightYellow,
148 brightblue: "bright blue" => Color::BrightBlue,
149 brightmagenta: "bright magenta" => Color::BrightMagenta,
150 brightcyan: "bright cyan" => Color::BrightCyan,
151 brightwhite: "bright white" => Color::BrightWhite,
152
153 invalid: "invalid" => Color::White,
154 capitalized: "BLUE" => Color::Blue,
155 mixed_case: "bLuE" => Color::Blue
156 );
157 }
158
159 mod from_string {
160 pub use super::*;
161
162 macro_rules! make_test {
163 ( $( $name:ident: $src:expr => $dst:expr),* ) => {
164
165 $(
166 #[test]
167 fn $name() {
168 let src = String::from($src);
169 let color : Color = src.into();
170 assert_eq!($dst, color)
171 }
172 )*
173 }
174 }
175
176 make_test!(
177 black: "black" => Color::Black,
178 red: "red" => Color::Red,
179 green: "green" => Color::Green,
180 yellow: "yellow" => Color::Yellow,
181 blue: "blue" => Color::Blue,
182 magenta: "magenta" => Color::Magenta,
183 cyan: "cyan" => Color::Cyan,
184 white: "white" => Color::White,
185 brightblack: "bright black" => Color::BrightBlack,
186 brightred: "bright red" => Color::BrightRed,
187 brightgreen: "bright green" => Color::BrightGreen,
188 brightyellow: "bright yellow" => Color::BrightYellow,
189 brightblue: "bright blue" => Color::BrightBlue,
190 brightmagenta: "bright magenta" => Color::BrightMagenta,
191 brightcyan: "bright cyan" => Color::BrightCyan,
192 brightwhite: "bright white" => Color::BrightWhite,
193
194 invalid: "invalid" => Color::White,
195 capitalized: "BLUE" => Color::Blue,
196 mixed_case: "bLuE" => Color::Blue
197 );
198 }
199
200 mod fromstr {
201 pub use super::*;
202
203 #[test]
204 fn parse() {
205 let color: Result<Color, _> = "blue".parse();
206 assert_eq!(Ok(Color::Blue), color)
207 }
208
209 #[test]
210 fn error() {
211 let color: Result<Color, ()> = "bloublou".parse();
212 assert_eq!(Err(()), color)
213 }
214 }
215}
216