1 use std::convert::TryFrom;
2 
3 #[derive(Clone, Copy, Debug, PartialEq)]
4 pub(crate) enum Color {
5 	LightWhite,
6 	LightBlack,
7 	LightBlue,
8 	LightCyan,
9 	LightGreen,
10 	LightMagenta,
11 	LightRed,
12 	LightYellow,
13 	DarkWhite,
14 	DarkBlack,
15 	DarkBlue,
16 	DarkCyan,
17 	DarkGreen,
18 	DarkMagenta,
19 	DarkRed,
20 	DarkYellow,
21 	Default,
22 	Index(i16),
23 	RGB { red: i16, green: i16, blue: i16 },
24 }
25 
26 impl TryFrom<&str> for Color {
27 	type Error = String;
28 
try_from(s: &str) -> Result<Self, Self::Error>29 	fn try_from(s: &str) -> Result<Self, Self::Error> {
30 		match s {
31 			"black" => Ok(Color::LightBlack),
32 			"blue" => Ok(Color::LightBlue),
33 			"cyan" => Ok(Color::LightCyan),
34 			"green" => Ok(Color::LightGreen),
35 			"magenta" => Ok(Color::LightMagenta),
36 			"red" => Ok(Color::LightRed),
37 			"white" => Ok(Color::LightWhite),
38 			"yellow" => Ok(Color::LightYellow),
39 			"light black" => Ok(Color::LightBlack),
40 			"light blue" => Ok(Color::LightBlue),
41 			"light cyan" => Ok(Color::LightCyan),
42 			"light green" => Ok(Color::LightGreen),
43 			"light magenta" => Ok(Color::LightMagenta),
44 			"light red" => Ok(Color::LightRed),
45 			"light white" => Ok(Color::LightWhite),
46 			"light yellow" => Ok(Color::LightYellow),
47 			"dark black" => Ok(Color::DarkBlack),
48 			"dark blue" => Ok(Color::DarkBlue),
49 			"dark cyan" => Ok(Color::DarkCyan),
50 			"dark green" => Ok(Color::DarkGreen),
51 			"dark magenta" => Ok(Color::DarkMagenta),
52 			"dark red" => Ok(Color::DarkRed),
53 			"dark white" => Ok(Color::DarkWhite),
54 			"dark yellow" => Ok(Color::DarkYellow),
55 			"transparent" | "-1" => Ok(Color::Default),
56 			_ => {
57 				let matches: Vec<&str> = s.split(',').collect();
58 
59 				match matches.len() {
60 					1 => {
61 						let color_index = s.parse::<i16>();
62 						match color_index {
63 							Ok(i) if i >= 0 && i < 256 => Ok(Color::Index(i)),
64 							_ => Err(format!("Invalid color value: {}", s)),
65 						}
66 					},
67 					3 => {
68 						let red = matches.get(0).unwrap().parse::<i16>().unwrap_or(-1);
69 						let green = matches.get(1).unwrap().parse::<i16>().unwrap_or(-1);
70 						let blue = matches.get(2).unwrap().parse::<i16>().unwrap_or(-1);
71 
72 						if red > -1 && green > -1 && blue > -1 && red < 256 && green < 256 && blue < 256 {
73 							return Ok(Color::RGB { red, green, blue });
74 						}
75 						Err(format!("Invalid color string: {}. Values must be within 0-255.", s))
76 					},
77 					_ => Err(format!("Invalid color value: {}", s)),
78 				}
79 			},
80 		}
81 	}
82 }
83 
84 #[cfg(test)]
85 mod tests {
86 	use super::Color;
87 	use std::convert::TryFrom;
88 
89 	#[test]
action_try_from_str_black()90 	fn action_try_from_str_black() {
91 		assert_eq!(Color::try_from("black").unwrap(), Color::LightBlack);
92 	}
93 
94 	#[test]
action_try_from_str_light_black()95 	fn action_try_from_str_light_black() {
96 		assert_eq!(Color::try_from("light black").unwrap(), Color::LightBlack);
97 	}
98 
99 	#[test]
action_try_from_str_dark_black()100 	fn action_try_from_str_dark_black() {
101 		assert_eq!(Color::try_from("dark black").unwrap(), Color::DarkBlack);
102 	}
103 
104 	#[test]
action_try_from_str_blue()105 	fn action_try_from_str_blue() {
106 		assert_eq!(Color::try_from("blue").unwrap(), Color::LightBlue);
107 	}
108 
109 	#[test]
action_try_from_str_light_blue()110 	fn action_try_from_str_light_blue() {
111 		assert_eq!(Color::try_from("light blue").unwrap(), Color::LightBlue);
112 	}
113 
114 	#[test]
action_try_from_str_dark_blue()115 	fn action_try_from_str_dark_blue() {
116 		assert_eq!(Color::try_from("dark blue").unwrap(), Color::DarkBlue);
117 	}
118 
119 	#[test]
action_try_from_str_cyan()120 	fn action_try_from_str_cyan() {
121 		assert_eq!(Color::try_from("cyan").unwrap(), Color::LightCyan);
122 	}
123 
124 	#[test]
action_try_from_str_light_cyan()125 	fn action_try_from_str_light_cyan() {
126 		assert_eq!(Color::try_from("light cyan").unwrap(), Color::LightCyan);
127 	}
128 
129 	#[test]
action_try_from_str_dark_cyan()130 	fn action_try_from_str_dark_cyan() {
131 		assert_eq!(Color::try_from("dark cyan").unwrap(), Color::DarkCyan);
132 	}
133 
134 	#[test]
action_try_from_str_green()135 	fn action_try_from_str_green() {
136 		assert_eq!(Color::try_from("green").unwrap(), Color::LightGreen);
137 	}
138 
139 	#[test]
action_try_from_str_light_green()140 	fn action_try_from_str_light_green() {
141 		assert_eq!(Color::try_from("light green").unwrap(), Color::LightGreen);
142 	}
143 
144 	#[test]
action_try_from_str_dark_green()145 	fn action_try_from_str_dark_green() {
146 		assert_eq!(Color::try_from("dark green").unwrap(), Color::DarkGreen);
147 	}
148 
149 	#[test]
action_try_from_str_magenta()150 	fn action_try_from_str_magenta() {
151 		assert_eq!(Color::try_from("magenta").unwrap(), Color::LightMagenta);
152 	}
153 
154 	#[test]
action_try_from_str_light_magenta()155 	fn action_try_from_str_light_magenta() {
156 		assert_eq!(Color::try_from("light magenta").unwrap(), Color::LightMagenta);
157 	}
158 
159 	#[test]
action_try_from_str_dark_magenta()160 	fn action_try_from_str_dark_magenta() {
161 		assert_eq!(Color::try_from("dark magenta").unwrap(), Color::DarkMagenta);
162 	}
163 
164 	#[test]
action_try_from_str_red()165 	fn action_try_from_str_red() {
166 		assert_eq!(Color::try_from("red").unwrap(), Color::LightRed);
167 	}
168 
169 	#[test]
action_try_from_str_light_red()170 	fn action_try_from_str_light_red() {
171 		assert_eq!(Color::try_from("light red").unwrap(), Color::LightRed);
172 	}
173 
174 	#[test]
action_try_from_str_dark_red()175 	fn action_try_from_str_dark_red() {
176 		assert_eq!(Color::try_from("dark red").unwrap(), Color::DarkRed);
177 	}
178 
179 	#[test]
action_try_from_str_white()180 	fn action_try_from_str_white() {
181 		assert_eq!(Color::try_from("white").unwrap(), Color::LightWhite);
182 	}
183 
184 	#[test]
action_try_from_str_yellow()185 	fn action_try_from_str_yellow() {
186 		assert_eq!(Color::try_from("yellow").unwrap(), Color::LightYellow);
187 	}
188 
189 	#[test]
action_try_from_str_light_yellow()190 	fn action_try_from_str_light_yellow() {
191 		assert_eq!(Color::try_from("light yellow").unwrap(), Color::LightYellow);
192 	}
193 
194 	#[test]
action_try_from_str_dark_yellow()195 	fn action_try_from_str_dark_yellow() {
196 		assert_eq!(Color::try_from("dark yellow").unwrap(), Color::DarkYellow);
197 	}
198 
199 	#[test]
action_try_from_color_index_minimum()200 	fn action_try_from_color_index_minimum() {
201 		assert_eq!(Color::try_from("0").unwrap(), Color::Index(0));
202 	}
203 
204 	#[test]
action_try_from_color_index_maximum()205 	fn action_try_from_color_index_maximum() {
206 		assert_eq!(Color::try_from("255").unwrap(), Color::Index(255));
207 	}
208 
209 	#[test]
action_try_from_color_rgb_color()210 	fn action_try_from_color_rgb_color() {
211 		assert_eq!(Color::try_from("100,101,102").unwrap(), Color::RGB {
212 			red: 100,
213 			green: 101,
214 			blue: 102
215 		});
216 	}
217 
218 	#[test]
action_try_from_str_rgb_invalid_non_number_red()219 	fn action_try_from_str_rgb_invalid_non_number_red() {
220 		assert_eq!(
221 			Color::try_from("red,0,0").unwrap_err(),
222 			"Invalid color string: red,0,0. Values must be within 0-255."
223 		);
224 	}
225 
226 	#[test]
action_try_from_str_rgb_invalid_non_number_green()227 	fn action_try_from_str_rgb_invalid_non_number_green() {
228 		assert_eq!(
229 			Color::try_from("0,green,0").unwrap_err(),
230 			"Invalid color string: 0,green,0. Values must be within 0-255."
231 		);
232 	}
233 
234 	#[test]
action_try_from_str_rgb_invalid_non_number_blue()235 	fn action_try_from_str_rgb_invalid_non_number_blue() {
236 		assert_eq!(
237 			Color::try_from("0,0,blue").unwrap_err(),
238 			"Invalid color string: 0,0,blue. Values must be within 0-255."
239 		);
240 	}
241 
242 	#[test]
action_try_from_str_rgb_invalid_non_number_red_lower_limit()243 	fn action_try_from_str_rgb_invalid_non_number_red_lower_limit() {
244 		assert_eq!(
245 			Color::try_from("-1,0,0").unwrap_err(),
246 			"Invalid color string: -1,0,0. Values must be within 0-255."
247 		);
248 	}
249 
250 	#[test]
action_try_from_str_rgb_invalid_non_number_green_lower_limit()251 	fn action_try_from_str_rgb_invalid_non_number_green_lower_limit() {
252 		assert_eq!(
253 			Color::try_from("0,-1,0").unwrap_err(),
254 			"Invalid color string: 0,-1,0. Values must be within 0-255."
255 		);
256 	}
257 
258 	#[test]
action_try_from_str_rgb_invalid_non_number_blue_lower_limit()259 	fn action_try_from_str_rgb_invalid_non_number_blue_lower_limit() {
260 		assert_eq!(
261 			Color::try_from("0,0,-1").unwrap_err(),
262 			"Invalid color string: 0,0,-1. Values must be within 0-255."
263 		);
264 	}
265 
266 	#[test]
action_try_from_str_rgb_invalid_non_number_red_upper_limit()267 	fn action_try_from_str_rgb_invalid_non_number_red_upper_limit() {
268 		assert_eq!(
269 			Color::try_from("256,0,0").unwrap_err(),
270 			"Invalid color string: 256,0,0. Values must be within 0-255."
271 		);
272 	}
273 
274 	#[test]
action_try_from_str_rgb_invalid_non_number_green_upper_limit()275 	fn action_try_from_str_rgb_invalid_non_number_green_upper_limit() {
276 		assert_eq!(
277 			Color::try_from("0,256,0").unwrap_err(),
278 			"Invalid color string: 0,256,0. Values must be within 0-255."
279 		);
280 	}
281 
282 	#[test]
action_try_from_str_rgb_invalid_non_number_blue_upper_limit()283 	fn action_try_from_str_rgb_invalid_non_number_blue_upper_limit() {
284 		assert_eq!(
285 			Color::try_from("0,0,256").unwrap_err(),
286 			"Invalid color string: 0,0,256. Values must be within 0-255."
287 		);
288 	}
289 
290 	#[test]
action_try_from_color_index_invalid_upper_limit()291 	fn action_try_from_color_index_invalid_upper_limit() {
292 		assert_eq!(Color::try_from("256").unwrap_err(), "Invalid color value: 256");
293 	}
294 
295 	#[test]
action_try_from_color_index_invalid_lower_limit()296 	fn action_try_from_color_index_invalid_lower_limit() {
297 		// -1 is transparent/default and a valid value
298 		assert_eq!(Color::try_from("-2").unwrap_err(), "Invalid color value: -2");
299 	}
300 
301 	#[test]
action_try_from_str_invalid_single_value()302 	fn action_try_from_str_invalid_single_value() {
303 		assert_eq!(Color::try_from("invalid").unwrap_err(), "Invalid color value: invalid");
304 	}
305 
306 	#[test]
action_try_from_str_invalid_multiple_value()307 	fn action_try_from_str_invalid_multiple_value() {
308 		assert_eq!(
309 			Color::try_from("invalid,invalid").unwrap_err(),
310 			"Invalid color value: invalid,invalid"
311 		);
312 	}
313 }
314