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