diff --git a/README.md b/README.md index 4d41aeb..a8a8bfe 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Coloring terminal so simple, you already know how to do it! "purple and magenta are the same".purple().magenta(); "and so are normal and clear".normal().clear(); "you can specify color by string".color("blue").on_color("red"); + "you can also use hex colors".color("#0057B7").on_color("#fd0"); String::from("this also works!").green().bold(); format!("{:30}", "format works as expected. This will be padded".blue()); format!("{:.3}", "and this will be green but truncated to 3 chars".green()); diff --git a/src/color.rs b/src/color.rs index a6db719..504a362 100644 --- a/src/color.rs +++ b/src/color.rs @@ -231,11 +231,31 @@ impl FromStr for Color { "bright magenta" => Ok(Self::BrightMagenta), "bright cyan" => Ok(Self::BrightCyan), "bright white" => Ok(Self::BrightWhite), + s if s.starts_with('#') => parse_hex(&s[1..]).ok_or(()), _ => Err(()), } } } +fn parse_hex(s: &str) -> Option { + if s.len() == 6 { + let r = u8::from_str_radix(&s[0..2], 16).ok()?; + let g = u8::from_str_radix(&s[2..4], 16).ok()?; + let b = u8::from_str_radix(&s[4..6], 16).ok()?; + Some(Color::TrueColor { r, g, b }) + } else if s.len() == 3 { + let r = u8::from_str_radix(&s[0..1], 16).ok()?; + let r = r | (r << 4); + let g = u8::from_str_radix(&s[1..2], 16).ok()?; + let g = g | (g << 4); + let b = u8::from_str_radix(&s[2..3], 16).ok()?; + let b = b | (b << 4); + Some(Color::TrueColor { r, g, b }) + } else { + None + } +} + #[cfg(test)] mod tests { pub use super::*; @@ -277,7 +297,17 @@ mod tests { invalid: "invalid" => Color::White, capitalized: "BLUE" => Color::Blue, - mixed_case: "bLuE" => Color::Blue + mixed_case: "bLuE" => Color::Blue, + + hex3_lower: "#abc" => Color::TrueColor { r: 170, g: 187, b: 204 }, + hex3_upper: "#ABC" => Color::TrueColor { r: 170, g: 187, b: 204 }, + hex3_mixed: "#aBc" => Color::TrueColor { r: 170, g: 187, b: 204 }, + hex6_lower: "#abcdef" => Color::TrueColor { r: 171, g: 205, b: 239 }, + hex6_upper: "#ABCDEF" => Color::TrueColor { r: 171, g: 205, b: 239 }, + hex6_mixed: "#aBcDeF" => Color::TrueColor { r: 171, g: 205, b: 239 }, + hex_too_short: "#aa" => Color::White, + hex_too_long: "#aaabbbccc" => Color::White, + hex_invalid: "#abcxyz" => Color::White ); } @@ -318,7 +348,17 @@ mod tests { invalid: "invalid" => Color::White, capitalized: "BLUE" => Color::Blue, - mixed_case: "bLuE" => Color::Blue + mixed_case: "bLuE" => Color::Blue, + + hex3_lower: "#abc" => Color::TrueColor { r: 170, g: 187, b: 204 }, + hex3_upper: "#ABC" => Color::TrueColor { r: 170, g: 187, b: 204 }, + hex3_mixed: "#aBc" => Color::TrueColor { r: 170, g: 187, b: 204 }, + hex6_lower: "#abcdef" => Color::TrueColor { r: 171, g: 205, b: 239 }, + hex6_upper: "#ABCDEF" => Color::TrueColor { r: 171, g: 205, b: 239 }, + hex6_mixed: "#aBcDeF" => Color::TrueColor { r: 171, g: 205, b: 239 }, + hex_too_short: "#aa" => Color::White, + hex_too_long: "#aaabbbccc" => Color::White, + hex_invalid: "#abcxyz" => Color::White ); }