diff --git a/README.md b/README.md index 93feb95..8c86d5f 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,8 @@ println!("{}", "Back to plain text".red().bold().clear()); - RGB values must be in range 0-255 (enforced at compile time via `u8` type) - Attempting to use RGB values > 255 will result in a compile error -- Hex color codes can be provided with or without the '#' prefix +- Hex color codes can be provided with or without the '#' prefix in either + 3-character shorthand or 6-character full form - Invalid hex codes (wrong length, invalid characters) will result in plain unstyled text - All color methods are guaranteed to return a valid string, never panicking @@ -153,10 +154,11 @@ println!("{}", "Gray".hsl(0.0, 0.0, 50.0)); // 50% gray // Hex colors work with or without # println!("{}", "Hex color".hex("#ff8000")); println!("{}", "Also valid".hex("ff8000")); +println!("{}", "Shorthand".hex("#f80")); // Invalid hex codes return uncolored text println!("{}", "Invalid".hex("xyz")); // Returns uncolored text -println!("{}", "Too short".hex("#f8")); // Returns uncolored text +println!("{}", "Wrong length".hex("#1234")); // Returns uncolored text ``` ## NO_COLOR Support diff --git a/TODO.md b/TODO.md index d242581..9cfff44 100644 --- a/TODO.md +++ b/TODO.md @@ -2,5 +2,3 @@ - Revisit whether to add `rust-version` to `Cargo.toml` once we want to commit to an explicit MSRV policy. -- Support 3-character shorthand hex colors like `#f80` in addition to 6-digit - hex input. diff --git a/examples/basic.rs b/examples/basic.rs index 70f45cd..6590edf 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -45,6 +45,9 @@ fn main() { println!("{}", "HSL Background".on_hsl(200.0, 100.0, 50.0)); println!("{}", "Hex color (#ff8000)".hex("#ff8000")); + println!("{}", "Hex without # (ff8000)".hex("ff8000")); + println!("{}", "Hex shorthand (#f80)".hex("#f80")); + println!("{}", "Hex shorthand without # (f80)".hex("f80")); println!("{}", "Hex background (#0080ff)".on_hex("#0080ff")); // Chaining styles diff --git a/src/color.rs b/src/color.rs index eaf17c6..078baf4 100644 --- a/src/color.rs +++ b/src/color.rs @@ -30,13 +30,22 @@ pub(crate) fn hsl_to_rgb(h: f32, s: f32, l: f32) -> (u8, u8, u8) { pub(crate) fn hex_to_rgb(hex: &str) -> Option<(u8, u8, u8)> { let hex = hex.trim_start_matches('#'); - if hex.len() != 6 { - return None; - } + let expanded = match hex.len() { + 3 => { + let mut expanded = String::with_capacity(6); + for ch in hex.chars() { + expanded.push(ch); + expanded.push(ch); + } + expanded + } + 6 => hex.to_string(), + _ => return None, + }; - let r = u8::from_str_radix(&hex[0..2], 16).ok()?; - let g = u8::from_str_radix(&hex[2..4], 16).ok()?; - let b = u8::from_str_radix(&hex[4..6], 16).ok()?; + let r = u8::from_str_radix(&expanded[0..2], 16).ok()?; + let g = u8::from_str_radix(&expanded[2..4], 16).ok()?; + let b = u8::from_str_radix(&expanded[4..6], 16).ok()?; Some((r, g, b)) } diff --git a/src/lib.rs b/src/lib.rs index c1743de..0b48012 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,8 @@ //! //! - RGB values must be in range 0-255 (enforced at compile time via `u8` type) //! - Attempting to use RGB values > 255 will result in a compile error -//! - Hex color codes can be provided with or without the `#` prefix +//! - Hex color codes can be provided with or without the `#` prefix in 3-digit +//! shorthand or 6-digit full form //! - Invalid hex codes (wrong length or invalid characters) return plain //! unstyled text //! - All color methods are guaranteed to return a valid string, never panicking @@ -59,10 +60,11 @@ //! // Valid hex codes (with or without #) //! println!("{}", "Valid hex".hex("#ff8000")); //! println!("{}", "Also valid".hex("ff8000")); +//! println!("{}", "Shorthand".hex("#f80")); //! //! // Invalid hex codes return plain text //! println!("{}", "Invalid hex".hex("xyz")); // Returns plain text -//! println!("{}", "Too short".hex("#f8")); // Returns plain text +//! println!("{}", "Wrong length".hex("#1234")); // Returns plain text //! ``` //! //! # Runtime Color Control diff --git a/src/tests.rs b/src/tests.rs index a6fb60c..0c9140c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -194,7 +194,9 @@ fn test_rgb_colors(#[case] r: u8, #[case] g: u8, #[case] b: u8) { #[rstest] #[case("#ff8000", 255, 128, 0)] +#[case("#f80", 255, 136, 0)] #[case("#00ff00", 0, 255, 0)] +#[case("0f8", 0, 255, 136)] #[case("#808080", 128, 128, 128)] #[case("#000000", 0, 0, 0)] #[case("#ffffff", 255, 255, 255)] @@ -224,6 +226,8 @@ fn test_hex_colors(#[case] hex: &str, #[case] r: u8, #[case] g: u8, #[case] b: u #[rstest] #[case("invalid")] #[case("#12")] +#[case("#1234")] +#[case("#12345678")] #[case("not-a-color")] #[case("#12345")] #[case("#1234567")]