├── .gitignore ├── src ├── commands │ ├── mod.rs │ ├── help.rs │ └── color_string.rs ├── color │ ├── mod.rs │ ├── utils.rs │ ├── hsl.rs │ └── rgb.rs ├── main.rs └── error.rs ├── logo.png ├── .editorconfig ├── Cargo.toml ├── README.md ├── LICENSE.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod help; 2 | pub mod color_string; 3 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/monodyle/cooler/HEAD/logo.png -------------------------------------------------------------------------------- /src/color/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod hsl; 2 | pub mod rgb; 3 | pub mod utils; 4 | -------------------------------------------------------------------------------- /src/commands/help.rs: -------------------------------------------------------------------------------- 1 | use colored::Colorize; 2 | 3 | pub fn run() { 4 | println!("help, {}", format!("hehe").blue()); 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod color; 2 | mod commands; 3 | mod error; 4 | 5 | fn main() { 6 | let color = std::env::args().nth(1).unwrap_or("help".to_string()); 7 | match color.as_ref() { 8 | "help" => commands::help::run(), 9 | _ => commands::color_string::run(&color), 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coolor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["monodyle "] 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | anyhow = "1.0.52" 11 | colored = "2" 12 | hex = "0.4" 13 | lazy_static = "1.4.0" 14 | regex = "1.5" 15 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug)] 4 | pub struct Error { 5 | pub message: String, 6 | } 7 | 8 | impl Error { 9 | pub fn new(message: &str) -> Self { 10 | Self { 11 | message: message.to_string(), 12 | } 13 | } 14 | } 15 | 16 | impl fmt::Display for Error { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 18 | f.write_str(self.message.as_str()) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Cooler 3 | Cooler 4 |

5 | 6 |

7 | MIT License 8 | 9 | 10 | 11 |

12 | 13 | Color converter tool, making from scratch with Rust. 14 | -------------------------------------------------------------------------------- /src/commands/color_string.rs: -------------------------------------------------------------------------------- 1 | pub fn run(color: &String) { 2 | // if is_hex_string(color) { 3 | // HexColor::from(color).unwrap().print_others() 4 | // } else if is_rgb_string(color) { 5 | // RGBColor::from(color).unwrap().print_others() 6 | // } else if is_hsl_string(color) { 7 | // HSLColor::from(color).unwrap().print_others() 8 | // } else if is_cmyk_string(color) { 9 | // CMYKColor::from(color).unwrap().print_others() 10 | // } else { 11 | println!("Invalid color string: {}", color); 12 | // } 13 | } 14 | -------------------------------------------------------------------------------- /src/color/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | 3 | pub fn color_string_splitter(color: &String) -> Vec<&str> { 4 | if color.contains(",") { 5 | color.trim().split(",").collect::>() 6 | } else { 7 | color.trim().split_ascii_whitespace().collect::>() 8 | } 9 | } 10 | 11 | pub fn safe_value(low: T, high: T, value: T) -> T 12 | where 13 | T: PartialOrd + Copy, 14 | { 15 | let min = if low < value { value } else { low }; 16 | if high > min { 17 | min 18 | } else { 19 | high 20 | } 21 | } 22 | 23 | pub fn hex_to_u8(value: String) -> Result { 24 | let value = if value.len() == 2 { 25 | value 26 | } else if value.len() == 1 { 27 | value.repeat(2) 28 | } else { 29 | return Err(Error::new("Cannot read hex value")); 30 | }; 31 | let result = hex::decode(value); 32 | match result { 33 | Ok(result) => Ok(result[0]), 34 | Err(_) => Err(Error::new("Cannot decode hex value")), 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 monodyle 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/color/hsl.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | 3 | use super::{rgb::RGBColor, utils::safe_value}; 4 | 5 | #[derive(Debug, PartialEq, PartialOrd)] 6 | pub struct HSLColor { 7 | pub h: u16, 8 | pub s: u8, 9 | pub l: u8, 10 | pub a: f32, 11 | } 12 | 13 | impl HSLColor { 14 | pub fn new(h: u16, s: u8, l: u8, a: Option) -> Self { 15 | Self { 16 | h: safe_value(0, 360, h), 17 | s: safe_value(0, 100, s), 18 | l: safe_value(0, 100, l), 19 | a: safe_value(0.0, 1.0, a.unwrap_or(1.0)), 20 | } 21 | } 22 | 23 | pub fn to_rgb(&self) -> RGBColor { 24 | let (h, mut s, mut l) = (self.h as f64, self.s as f64, self.l as f64); 25 | s /= 100.0; 26 | l /= 100.0; 27 | 28 | let c = (1.0 - (2.0 * l - 1.0).abs()) * s; 29 | let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs()); 30 | let m = l - c / 2.0; 31 | let (mut r, mut g, mut b) = (0.0, 0.0, 0.0); 32 | 33 | if 0.0 <= h && h < 60.0 { 34 | r = c; 35 | g = x; 36 | b = 0.0; 37 | } else if 60.0 <= h && h < 120.0 { 38 | r = x; 39 | g = c; 40 | b = 0.0; 41 | } else if 120.0 <= h && h < 180.0 { 42 | r = 0.0; 43 | g = c; 44 | b = x; 45 | } else if 180.0 <= h && h < 240.0 { 46 | r = 0.0; 47 | g = x; 48 | b = c; 49 | } else if 240.0 <= h && h < 300.0 { 50 | r = x; 51 | g = 0.0; 52 | b = c; 53 | } else if 300.0 <= h && h < 360.0 { 54 | r = c; 55 | g = 0.0; 56 | b = x; 57 | } 58 | 59 | r = ((r + m) * 255.0).round(); 60 | g = ((g + m) * 255.0).round(); 61 | b = ((b + m) * 255.0).round(); 62 | 63 | RGBColor { r: r as u8, g: g as u8, b: b as u8, a: self.a } 64 | } 65 | 66 | pub fn parse(color: &String) -> Result { 67 | let rgb = RGBColor::parse(color)?; 68 | Ok(rgb.to_hsl()) 69 | } 70 | 71 | pub fn set_alpha(&mut self, alpha: f32) { 72 | self.a = safe_value(0.0, 1.0, alpha); 73 | } 74 | 75 | pub fn is_hsl_string(color: &String) -> bool { 76 | match Self::parse(color) { 77 | Ok(_) => true, 78 | Err(_) => false, 79 | } 80 | } 81 | 82 | pub fn print(&self) { 83 | println!( 84 | "HSL Color:\nHue: {}\nSaturation: {}\nLightness: {}\nAlpha: {}", 85 | &self.h, &self.s, &self.l, &self.a 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.52" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3" 19 | 20 | [[package]] 21 | name = "atty" 22 | version = "0.2.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 25 | dependencies = [ 26 | "hermit-abi", 27 | "libc", 28 | "winapi", 29 | ] 30 | 31 | [[package]] 32 | name = "colored" 33 | version = "2.0.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" 36 | dependencies = [ 37 | "atty", 38 | "lazy_static", 39 | "winapi", 40 | ] 41 | 42 | [[package]] 43 | name = "coolor" 44 | version = "0.1.0" 45 | dependencies = [ 46 | "anyhow", 47 | "colored", 48 | "hex", 49 | "lazy_static", 50 | "regex", 51 | ] 52 | 53 | [[package]] 54 | name = "hermit-abi" 55 | version = "0.1.19" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 58 | dependencies = [ 59 | "libc", 60 | ] 61 | 62 | [[package]] 63 | name = "hex" 64 | version = "0.4.3" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 67 | 68 | [[package]] 69 | name = "lazy_static" 70 | version = "1.4.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 73 | 74 | [[package]] 75 | name = "libc" 76 | version = "0.2.112" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" 79 | 80 | [[package]] 81 | name = "memchr" 82 | version = "2.4.1" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 85 | 86 | [[package]] 87 | name = "regex" 88 | version = "1.5.4" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 91 | dependencies = [ 92 | "aho-corasick", 93 | "memchr", 94 | "regex-syntax", 95 | ] 96 | 97 | [[package]] 98 | name = "regex-syntax" 99 | version = "0.6.25" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 102 | 103 | [[package]] 104 | name = "winapi" 105 | version = "0.3.9" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 108 | dependencies = [ 109 | "winapi-i686-pc-windows-gnu", 110 | "winapi-x86_64-pc-windows-gnu", 111 | ] 112 | 113 | [[package]] 114 | name = "winapi-i686-pc-windows-gnu" 115 | version = "0.4.0" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 118 | 119 | [[package]] 120 | name = "winapi-x86_64-pc-windows-gnu" 121 | version = "0.4.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 124 | -------------------------------------------------------------------------------- /src/color/rgb.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use regex::Regex; 3 | 4 | use crate::{color::utils, error::Error}; 5 | 6 | use super::{hsl::HSLColor, utils::safe_value}; 7 | 8 | #[derive(Debug, PartialEq, PartialOrd)] 9 | pub struct RGBColor { 10 | pub r: u8, 11 | pub g: u8, 12 | pub b: u8, 13 | pub a: f32, 14 | } 15 | 16 | lazy_static! { 17 | static ref REDUCED_HEX_RE: Regex = 18 | Regex::new("^#(?P[a-f0-9])(?P[a-f0-9])(?P[a-f0-9])(?P[a-f0-9])?$").unwrap(); 19 | static ref HEX_RE: Regex = 20 | Regex::new("^#(?P[a-f0-9]{2})(?P[a-f0-9]{2})(?P[a-f0-9]{2})(?P[a-f0-9]{2})?$") 21 | .unwrap(); 22 | static ref RGB_RE: Regex = Regex::new( 23 | r"^(?i:rgba?)\(\s*(?P\d+)\s*,\s*(?P\d+)\s*,\s*(?P\d+)\s*(?:,\s*(?P[\d.]+))?\s*\)$" 24 | ) 25 | .unwrap(); 26 | static ref HSL_RE: Regex = Regex::new( 27 | r"^(?i:hsla?)\(\s*(?P[\d.]+)\s*,\s*(?P[\d.]+)%\s*,\s*(?P[\d.]+)%(?:\s*,\s*(?P[\d.]+))?\s*\)$" 28 | ) 29 | .unwrap(); 30 | } 31 | 32 | impl RGBColor { 33 | pub fn new(r: u8, g: u8, b: u8, a: Option) -> Self { 34 | Self { 35 | r: safe_value(0, 255, r), 36 | g: safe_value(0, 255, g), 37 | b: safe_value(0, 255, b), 38 | a: safe_value(0.0, 1.0, a.unwrap_or(1.0)), 39 | } 40 | } 41 | 42 | pub fn to_hsl(&self) -> HSLColor { 43 | let (r, g, b) = ( 44 | self.r as f64 / 255.0, 45 | self.g as f64 / 255.0, 46 | self.b as f64 / 255.0, 47 | ); 48 | let cmin = f64::min(f64::min(r, g), b); 49 | let cmax = f64::max(f64::max(r, g), b); 50 | let delta = cmax - cmin; 51 | 52 | let (mut h, mut s, mut l): (f64, f64, f64); 53 | 54 | if delta == 0.0 { 55 | h = 0.0; 56 | } else if cmax == r { 57 | h = ((g - b) as f64 / delta as f64) % 6.0; 58 | } else if cmax == g { 59 | h = (b - r) as f64 / delta as f64 + 2.0; 60 | } else { 61 | h = (r - g) as f64 / delta as f64 + 4.0; 62 | } 63 | h = (h * 60.0).ceil(); 64 | if h < 0.0 { 65 | h += 360.0 66 | } 67 | 68 | l = (cmax + cmin) as f64 / 2.0; 69 | s = if delta == 0.0 { 70 | 0.0 71 | } else { 72 | delta as f64 / (1.0 - f64::abs(2.0 * l - 1.0)) 73 | }; 74 | 75 | s = (s * 100.0).ceil(); 76 | l = (l * 100.0).ceil(); 77 | 78 | HSLColor { h: h as u16, s: s as u8, l: l as u8, a: self.a } 79 | } 80 | 81 | pub fn parse(color: &String) -> Result { 82 | let color: String = color.trim().to_string(); 83 | if color == "transparent" { 84 | return Ok(Self { 85 | r: 0, 86 | g: 0, 87 | b: 0, 88 | a: 0.0, 89 | }); 90 | } 91 | 92 | if REDUCED_HEX_RE.is_match(&color) { 93 | let Some(values) = REDUCED_HEX_RE.captures(&color) else { 94 | return Err(Error::new("Invalid color string")) 95 | }; 96 | let extracted_alpha = values.name("a"); 97 | let alpha_hex = if extracted_alpha.is_some() { 98 | &values["a"] 99 | } else { 100 | "f" 101 | }; 102 | return Ok(Self { 103 | r: utils::hex_to_u8(String::from(&values["r"]))?, 104 | g: utils::hex_to_u8(String::from(&values["g"]))?, 105 | b: utils::hex_to_u8(String::from(&values["b"]))?, 106 | a: (utils::hex_to_u8(alpha_hex.to_owned())? as f32) / 255.0, 107 | }); 108 | } 109 | 110 | if HEX_RE.is_match(&color) { 111 | let Some(values) = HEX_RE.captures(&color) else { 112 | return Err(Error::new("Invalid color string")) 113 | }; 114 | let extracted_alpha = values.name("a"); 115 | let alpha_hex = if extracted_alpha.is_some() { 116 | &values["a"] 117 | } else { 118 | "ff" 119 | }; 120 | return Ok(Self { 121 | r: utils::hex_to_u8(String::from(&values["r"]))?, 122 | g: utils::hex_to_u8(String::from(&values["g"]))?, 123 | b: utils::hex_to_u8(String::from(&values["b"]))?, 124 | a: (utils::hex_to_u8(alpha_hex.to_owned())? as f32) / 255.0, 125 | }); 126 | } 127 | 128 | if RGB_RE.is_match(&color) { 129 | let Some(values) = RGB_RE.captures(&color) else { 130 | return Err(Error::new("Invalid color string")) 131 | }; 132 | let extracted_alpha = values.name("a"); 133 | let alpha = if extracted_alpha.is_some() { 134 | values["a"] 135 | .parse::() 136 | .or(Err(Error::new("Invalid color string")))? 137 | } else { 138 | 1.0 139 | }; 140 | 141 | return Ok(Self { 142 | r: values["r"] 143 | .parse::() 144 | .or(Err(Error::new("Invalid color string")))?, 145 | g: values["g"] 146 | .parse::() 147 | .or(Err(Error::new("Invalid color string")))?, 148 | b: values["b"] 149 | .parse::() 150 | .or(Err(Error::new("Invalid color string")))?, 151 | a: alpha, 152 | }); 153 | } 154 | 155 | if HSL_RE.is_match(&color) { 156 | let Some(values) = HSL_RE.captures(&color) else { 157 | return Err(Error::new("Invalid color string")) 158 | }; 159 | let extracted_alpha = values.name("a"); 160 | 161 | let alpha = if extracted_alpha.is_some() { 162 | values["a"] 163 | .parse::() 164 | .or(Err(Error::new("Invalid color string")))? 165 | } else { 166 | 1.0 167 | }; 168 | 169 | let h = values["h"] 170 | .parse::() 171 | .or(Err(Error::new("Invalid color string")))?; 172 | let s = values["s"] 173 | .parse::() 174 | .or(Err(Error::new("Invalid color string")))?; 175 | let l = values["l"] 176 | .parse::() 177 | .or(Err(Error::new("Invalid color string")))?; 178 | 179 | if safe_value(0, 100, s) != s || safe_value(0, 100, l) != l { 180 | return Err(Error::new("Invalid color string")); 181 | } 182 | 183 | let hsl = HSLColor::new(h, s, l, Some(alpha)); 184 | return Ok(hsl.to_rgb()); 185 | } 186 | 187 | Err(Error::new("Invalid color string")) 188 | } 189 | 190 | pub fn set_alpha(&mut self, alpha: f32) { 191 | self.a = safe_value(0.0, 1.0, alpha); 192 | } 193 | 194 | pub fn is_rgb_string(color: &String) -> bool { 195 | match Self::parse(color) { 196 | Ok(_) => true, 197 | Err(_) => false, 198 | } 199 | } 200 | 201 | pub fn print(&self) { 202 | println!( 203 | "RGB Color:\nRed: {}\nGreen: {}\nBlue: {}\nAlpha: {}", 204 | &self.r, &self.g, &self.b, &self.a 205 | ); 206 | } 207 | } 208 | 209 | #[cfg(test)] 210 | mod tests { 211 | use crate::color::rgb::RGBColor; 212 | 213 | #[test] 214 | fn transparent_string() { 215 | assert_eq!( 216 | RGBColor::parse(&String::from("transparent")).unwrap(), 217 | RGBColor { 218 | r: 0, 219 | g: 0, 220 | b: 0, 221 | a: 0.0 222 | } 223 | ); 224 | } 225 | 226 | #[test] 227 | fn reduced_hex_string() { 228 | assert_eq!( 229 | RGBColor::parse(&String::from("#fff")).unwrap(), 230 | RGBColor { 231 | r: 255, 232 | g: 255, 233 | b: 255, 234 | a: 1.0 235 | } 236 | ); 237 | } 238 | 239 | #[test] 240 | fn reduced_hex_with_alpha_string() { 241 | assert_eq!( 242 | RGBColor::parse(&String::from("#fff0")).unwrap(), 243 | RGBColor { 244 | r: 255, 245 | g: 255, 246 | b: 255, 247 | a: 0.0 248 | } 249 | ); 250 | } 251 | 252 | #[test] 253 | fn invalid_reduced_hex_string() { 254 | let result_1 = RGBColor::parse(&String::from("#zzz")).map_err(|e| e.message); 255 | assert_eq!(result_1, Err(String::from("Invalid color string"))); 256 | 257 | let result_2 = RGBColor::parse(&String::from("#ffag")).map_err(|e| e.message); 258 | assert_eq!(result_2, Err(String::from("Invalid color string"))); 259 | } 260 | 261 | #[test] 262 | fn hex_string() { 263 | assert_eq!( 264 | RGBColor::parse(&String::from("#000000")).unwrap(), 265 | RGBColor { 266 | r: 0, 267 | g: 0, 268 | b: 0, 269 | a: 1.0 270 | } 271 | ); 272 | } 273 | 274 | #[test] 275 | fn hex_with_alpha_string() { 276 | assert_eq!( 277 | RGBColor::parse(&String::from("#000000ff")).unwrap(), 278 | RGBColor { 279 | r: 0, 280 | g: 0, 281 | b: 0, 282 | a: 1.0 283 | } 284 | ); 285 | } 286 | 287 | #[test] 288 | fn invalid_hex_string() { 289 | let result_1 = RGBColor::parse(&String::from("#abcdefgh")).map_err(|e| e.message); 290 | assert_eq!(result_1, Err(String::from("Invalid color string"))); 291 | 292 | let result_2 = RGBColor::parse(&String::from("#iklmno")).map_err(|e| e.message); 293 | assert_eq!(result_2, Err(String::from("Invalid color string"))); 294 | } 295 | 296 | #[test] 297 | fn rgb_string() { 298 | assert_eq!( 299 | RGBColor::parse(&String::from("rgb(0, 0, 0)")).unwrap(), 300 | RGBColor { 301 | r: 0, 302 | g: 0, 303 | b: 0, 304 | a: 1.0 305 | } 306 | ); 307 | } 308 | 309 | #[test] 310 | fn rgba_string() { 311 | assert_eq!( 312 | RGBColor::parse(&String::from("rgba( 255, 255, 255, 0)")).unwrap(), 313 | RGBColor { 314 | r: 255, 315 | g: 255, 316 | b: 255, 317 | a: 0.0 318 | } 319 | ); 320 | } 321 | 322 | #[test] 323 | fn hsl_string() { 324 | assert_eq!(RGBColor::parse(&String::from("hsl(136, 32%, 62%)")).unwrap(), 325 | RGBColor { 326 | r: 127, 327 | g: 189, 328 | b: 144, 329 | a: 1.0 330 | }) 331 | } 332 | } 333 | --------------------------------------------------------------------------------