diff --git a/colorizer/Cargo.lock b/colorizer/Cargo.lock index 52e858b..ca08c2d 100644 --- a/colorizer/Cargo.lock +++ b/colorizer/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "block" version = "0.1.6" @@ -35,6 +44,7 @@ name = "colorizer" version = "0.1.0" dependencies = [ "clipboard", + "regex", ] [[package]] @@ -58,6 +68,12 @@ dependencies = [ "libc", ] +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + [[package]] name = "objc" version = "0.2.7" @@ -87,6 +103,35 @@ dependencies = [ "objc", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "winapi" version = "0.3.9" diff --git a/colorizer/Cargo.toml b/colorizer/Cargo.toml index 9bcf2b5..83b47c9 100644 --- a/colorizer/Cargo.toml +++ b/colorizer/Cargo.toml @@ -5,3 +5,4 @@ edition = "2024" [dependencies] clipboard = "0.5.0" +regex = "1.11.1" diff --git a/colorizer/src/color/hsl.rs b/colorizer/src/color/hsl.rs index a10c0a4..0ad436a 100644 --- a/colorizer/src/color/hsl.rs +++ b/colorizer/src/color/hsl.rs @@ -1,4 +1,5 @@ use crate::color::{ColorHue, HSL, Percentage, RGB}; +use regex::Regex; impl HSL { pub fn new(h: u16, s: u8, l: u8) -> Self { @@ -20,6 +21,28 @@ impl PartialEq for HSL { } } +// TODO: manage error +impl From for HSL { + fn from(value: String) -> Self { + let regex = Regex::new(r"hsl\(([0-9]+),([0-9]+),([0-9 ]+)\)").unwrap(); + let numbers = value.replace(" ", "").replace("%", ""); + let result = regex.captures(&numbers); + + match result { + Some(value_list) => { + // Numeric + let h: u16 = value_list[1].parse::().unwrap(); + let s: u8 = value_list[2].parse::().unwrap(); + let l: u8 = value_list[3].parse::().unwrap(); + return Self::new(h, s, l); + } + None => (), + } + + Self::new(0, 0, 0) + } +} + impl From for HSL { fn from(value: RGB) -> Self { let r = value.0.to_f32() / 255.0; diff --git a/colorizer/src/color/hsv.rs b/colorizer/src/color/hsv.rs index 93fb669..584dcae 100644 --- a/colorizer/src/color/hsv.rs +++ b/colorizer/src/color/hsv.rs @@ -1,3 +1,4 @@ +use regex::Regex; use std::fmt::Display; use crate::color::{ColorHue, HSV, Percentage, RGB}; @@ -28,6 +29,28 @@ impl PartialEq for HSV { } } +// TODO: manage error +impl From for HSV { + fn from(value: String) -> Self { + let regex = Regex::new(r"hsv\(([0-9]+),([0-9]+),([0-9 ]+)\)").unwrap(); + let numbers = value.replace(" ", "").replace("%", ""); + let result = regex.captures(&numbers); + + match result { + Some(value_list) => { + // Numeric + let h: u16 = value_list[1].parse::().unwrap(); + let s: u8 = value_list[2].parse::().unwrap(); + let v: u8 = value_list[3].parse::().unwrap(); + return Self::new(h, s, v); + } + None => (), + } + + Self::new(0, 0, 0) + } +} + impl From for HSV { fn from(color: RGB) -> Self { let r = color.0.to_f32() / 255.0; diff --git a/colorizer/src/color/mod.rs b/colorizer/src/color/mod.rs index 48fb400..35a495e 100644 --- a/colorizer/src/color/mod.rs +++ b/colorizer/src/color/mod.rs @@ -8,6 +8,8 @@ pub mod rgb; use std::fmt::UpperHex; +use regex::Regex; + use crate::core::ranged::RangedInt; pub type ColorIntensity = RangedInt<0, 255>; @@ -22,7 +24,47 @@ pub struct HSV(ColorHue, Percentage, Percentage); #[derive(Debug)] pub struct Color(RGB); -impl Color {} +impl Color { + pub fn try_parse(input: String) -> Result { + let input = input.replace(" ", "").to_uppercase(); + + let hex_regex = Regex::new(r".*(#[a-fA-F0-9]{3,6}).*").unwrap(); + let rgb_regex = + Regex::new(r"(rgb\([ ]*[0-9]+[ ]*,[ ]*[0-9]+[ ]*,[ ]*[0-9 ]+[ ]*\)).*").unwrap(); + let hsl_regex = + Regex::new(r"(hsl\([ ]*[0-9]+[ ]*,[0-9]+[ ]*[%]*[ ]*,[0-9 ]+[ ]*[%]*[ ]*\)).*") + .unwrap(); + let hsv_regex = + Regex::new(r"(hsv\([ ]*[0-9]+[ ]*,[0-9]+[ ]*[%]*[ ]*,[0-9 ]+[ ]*[%]*[ ]*\)).*") + .unwrap(); + + let hex_result = hex_regex.captures(&input); + match hex_result { + Some(color) => return Ok(Color::from(RGB::from(color[1].to_string()))), + None => (), + } + + let rgb_result = rgb_regex.captures(&input); + match rgb_result { + Some(color) => return Ok(Color::from(RGB::from(color[1].to_string()))), + None => (), + } + + let hsl_result = hsl_regex.captures(&input); + match hsl_result { + Some(color) => return Ok(Color::from(HSL::from(color[1].to_string()))), + None => (), + } + + let hsv_result = hsv_regex.captures(&input); + match hsv_result { + Some(color) => return Ok(Color::from(HSV::from(color[1].to_string()))), + None => (), + } + + Err(()) + } +} impl UpperHex for Color { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/colorizer/src/color/rgb.rs b/colorizer/src/color/rgb.rs index 0336f6b..3bc86e1 100644 --- a/colorizer/src/color/rgb.rs +++ b/colorizer/src/color/rgb.rs @@ -1,3 +1,4 @@ +use regex::Regex; use std::fmt::{Display, UpperHex}; use crate::{ @@ -15,6 +16,39 @@ impl RGB { } } +// TODO: manage error +impl From for RGB { + fn from(value: String) -> Self { + let regex = Regex::new(r"rgb\(([0-9]+),([0-9]+),([0-9 ]+)\)").unwrap(); + let mut numbers = value.replace(" ", ""); + let result = regex.captures(&numbers); + + match result { + Some(value_list) => { + // Numeric + let r: u8 = value_list[1].parse::().unwrap(); + let g: u8 = value_list[2].parse::().unwrap(); + let b: u8 = value_list[3].parse::().unwrap(); + return Self::new(r, g, b); + } + None => { + // Hex + if numbers.len() == 7 { + numbers.remove(0); + let hex = numbers.split_at(2); + let r = u8::from_str_radix(hex.0, 16).unwrap(); + let hex = hex.1.split_at(2); + let g = u8::from_str_radix(hex.0, 16).unwrap(); + let b = u8::from_str_radix(hex.1, 16).unwrap(); + return Self::new(r, g, b); + } + } + } + + Self::new(0, 0, 0) + } +} + impl PartialEq for RGB { fn eq(&self, other: &Self) -> bool { self.0 == other.0 && self.1 == other.1 && self.2 == other.2 diff --git a/colorizer/src/color/test/colors.test.rs b/colorizer/src/color/test/colors.test.rs index 0f1cf97..6fd62c5 100644 --- a/colorizer/src/color/test/colors.test.rs +++ b/colorizer/src/color/test/colors.test.rs @@ -162,4 +162,93 @@ pub mod tests { let color = HSV::new(142, 92, 94); assert_eq!(RGB::from(color), RGB::new(19, 240, 100)); } + + #[test] + fn test_rgb_string_parse() { + // Base colors + // HEX + let color = "#FF0000"; + assert_eq!(RGB::from(color.to_string()), RGB::new(255, 0, 0)); + let color = "#00FF00"; + assert_eq!(RGB::from(color.to_string()), RGB::new(0, 255, 0)); + let color = "#0000FF"; + assert_eq!(RGB::from(color.to_string()), RGB::new(0, 0, 255)); + // RGB + let color = "rgb(255 ,0,0)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(255, 0, 0)); + let color = "rgb(0,255, 0)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(0, 255, 0)); + let color = "rgb(0 , 0, 255)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(0, 0, 255)); + + // Complex colors + // HEX + let color = "#Ffb703"; + assert_eq!(RGB::from(color.to_string()), RGB::new(255, 183, 3)); + let color = "#588157"; + assert_eq!(RGB::from(color.to_string()), RGB::new(88, 129, 87)); + let color = "#fB8500"; + assert_eq!(RGB::from(color.to_string()), RGB::new(251, 133, 0)); + let color = "#8338eC"; + assert_eq!(RGB::from(color.to_string()), RGB::new(131, 56, 236)); + let color = "#9D8189"; + assert_eq!(RGB::from(color.to_string()), RGB::new(157, 129, 137)); + // RGB + let color = "rgb(255, 183, 3)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(255, 183, 3)); + let color = "rgb(88, 129, 87)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(88, 129, 87)); + let color = "rgb(251, 133, 0)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(251, 133, 0)); + let color = "rgb(131, 56, 236)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(131, 56, 236)); + let color = "rgb(157, 129, 137)"; + assert_eq!(RGB::from(color.to_string()), RGB::new(157, 129, 137)); + } + + #[test] + fn test_hsl_string_parse() { + // Base colors + let color = "hsl(0, 100, 50)"; + assert_eq!(HSL::from(color.to_string()), HSL::new(0, 100, 50)); + let color = "hsl(120, 100, 50)"; + assert_eq!(HSL::from(color.to_string()), HSL::new(120, 100, 50)); + let color = "hsl(240, 100, 50)"; + assert_eq!(HSL::from(color.to_string()), HSL::new(240, 100, 50)); + + // Complex colors + let color = "hsl(255 , 83, 3)"; + assert_eq!(HSL::from(color.to_string()), HSL::new(255, 83, 3)); + let color = "hsl(88 , 29,87)"; + assert_eq!(HSL::from(color.to_string()), HSL::new(88, 29, 87)); + let color = "hsl(251,33,0)"; + assert_eq!(HSL::from(color.to_string()), HSL::new(251, 33, 0)); + let color = "hsl(131,56,36)"; + assert_eq!(HSL::from(color.to_string()), HSL::new(131, 56, 36)); + let color = "hsl(157,29, 37 )"; + assert_eq!(HSL::from(color.to_string()), HSL::new(157, 29, 37)); + } + + #[test] + fn test_hsv_string_parse() { + // Base colors + let color = "hsv(0, 100, 50)"; + assert_eq!(HSV::from(color.to_string()), HSV::new(0, 100, 50)); + let color = "hsv(120, 100, 50)"; + assert_eq!(HSV::from(color.to_string()), HSV::new(120, 100, 50)); + let color = "hsv(240, 100, 50)"; + assert_eq!(HSV::from(color.to_string()), HSV::new(240, 100, 50)); + + // Complex colors + let color = "hsv(255 , 83, 3)"; + assert_eq!(HSV::from(color.to_string()), HSV::new(255, 83, 3)); + let color = "hsv(88 , 29,87)"; + assert_eq!(HSV::from(color.to_string()), HSV::new(88, 29, 87)); + let color = "hsv(251,33,0)"; + assert_eq!(HSV::from(color.to_string()), HSV::new(251, 33, 0)); + let color = "hsv(131,56,36)"; + assert_eq!(HSV::from(color.to_string()), HSV::new(131, 56, 36)); + let color = "hsv(157,29, 37 )"; + assert_eq!(HSV::from(color.to_string()), HSV::new(157, 29, 37)); + } } diff --git a/colorizer/src/main.rs b/colorizer/src/main.rs index 3139fdb..9aa33b8 100644 --- a/colorizer/src/main.rs +++ b/colorizer/src/main.rs @@ -1,6 +1,7 @@ use clipboard::ClipboardContext; use clipboard::ClipboardProvider; +use crate::color::Color; use crate::color::HSV; use crate::color::RGB; @@ -20,4 +21,11 @@ fn main() { let color = RGB::new(220, 10, 50); let hsv_color = HSV::from(RGB::new(220, 10, 50)); println!("RGB color: {}, HSV Color: {}", color, hsv_color); + + Color::try_parse("#F2FA01".to_string()); + Color::try_parse("rgb(1, 3,4)".to_string()); + Color::try_parse("rgb(1,3 ,4)".to_string()); + Color::try_parse("rgb(1,F,4)".to_string()); + Color::try_parse("hsl(100,100%,40%)".to_string()); + Color::try_parse("hsl(100,100%,40)".to_string()); }