Skip to content

Commit

Permalink
feat: support hsla color space
Browse files Browse the repository at this point in the history
  • Loading branch information
JiatLn committed May 5, 2023
1 parent 9871292 commit d1354f5
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 1 deletion.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Currently supported color formats:
- `rgba`
- `hex`
- `hsl`
- `hsla`
- `hsv`
- `hwb`
- `cmyk`
Expand All @@ -69,6 +70,7 @@ let color = Color::from_str("rgb(255, 255, 0)").unwrap();
let color = Color::from_str("rgba(255, 255, 0, 0.5)").unwrap();
let color = Color::from_str("#ffff00").unwrap();
let color = Color::from_str("hsl(60, 100%, 50%)").unwrap();
let color = Color::from_str("hsla(60, 100%, 50%, 0.6)").unwrap();
let color = Color::from_str("hsv(60, 100%, 100%)").unwrap();
let color = Color::from_str("hwb(60, 0%, 0%)").unwrap();
let color = Color::from_str("cmyk(0%, 0%, 100%, 0%)").unwrap();
Expand Down Expand Up @@ -143,7 +145,7 @@ let color = Color::from_hex("#ffff00").unwrap();

Stringify a color to a string.

You can use the `hex`, `rgb`, `rgba`, `hsl`, `hsv`, `hwb`, `cmyk`, `xyz`, `yuv`, `YCbCr`, `lab`, `name` method to stringify a color to a string.
You can use the `hex`, `rgb`, `rgba`, `hsl`, `hsla`, `hsv`, `hwb`, `cmyk`, `xyz`, `yuv`, `YCbCr`, `lab`, `name` method to stringify a color to a string.

For example:

Expand All @@ -156,6 +158,7 @@ color.hex(); // "#ffff00"
color.rgb(); // "rgb(255, 255, 0)"
color.rgba(); // "rgba(255, 255, 0, 1)"
color.hsl(); // "hsl(60, 100%, 50%)"
color.hsla(); // "hsl(60, 100%, 50%, 1)"
color.hsv(); // "hsv(60, 100%, 100%)"
color.hwb(); // "hwb(60, 0%, 0%)"
color.cmyk(); // "cmyk(0%, 0%, 100%, 0%)"
Expand Down
13 changes: 13 additions & 0 deletions src/color/from_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ impl FromStr for Color {
let (r, g, b) = conversion::hsl::hsl2rgb(hsl);
(r, g, b, 1.0)
}
s if s.starts_with("hsla(") => {
let (h, s, l, a) = parser::hsla::parse_hsla_str(s)?;
let (r, g, b) = conversion::hsl::hsl2rgb((h, s, l));
(r, g, b, a)
}
s if s.starts_with("hsv(") => {
let hsv = parser::hsv::parse_hsv_str(s)?;
let (r, g, b) = conversion::hsv::hsv2rgb(hsv);
Expand Down Expand Up @@ -239,6 +244,14 @@ mod tests {
assert_eq!(color.rgb(), "rgb(0, 255, 255)");
}

#[test]
fn test_color_from_hsla_str() {
let s = "hsla(180, 100%, 50%, 0.6)";
let color = Color::from_str(s).unwrap();
assert_eq!(color.hsla(), "hsla(180, 100%, 50%, 0.6)");
assert_eq!(color.rgba(), "rgba(0, 255, 255, 0.6)");
}

#[test]
fn test_color_from_hsv_str() {
let s = "hsv(180, 100%, 100%)";
Expand Down
23 changes: 23 additions & 0 deletions src/color/stringify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ impl Color {
round(l * 100., 0),
)
}
/// `hsla` string of the color
///
/// # Examples
///
/// ```rust
/// use color_art::Color;
///
/// let color = Color::new(255, 255, 255, 0.3);
/// assert_eq!(color.hsla(), "hsla(0, 0%, 100%, 0.3)");
/// ```
pub fn hsla(self) -> String {
let (h, s, l) = rgb2hsl(self.rgb);
format!(
"hsla({}, {}%, {}%, {})",
round(h, 0),
round(s * 100., 0),
round(l * 100., 0),
self.alpha
)
}
/// `hsv` string of the color
///
/// # Examples
Expand Down Expand Up @@ -247,6 +267,7 @@ mod tests {
assert_eq!(color.rgb(), "rgb(255, 255, 255)");
assert_eq!(color.rgba(), "rgba(255, 255, 255, 1)");
assert_eq!(color.hsl(), "hsl(0, 0%, 100%)");
assert_eq!(color.hsla(), "hsla(0, 0%, 100%, 1)");
assert_eq!(color.hsv(), "hsv(0, 0%, 100%)");
assert_eq!(color.hwb(), "hwb(0, 100%, 0%)");
assert_eq!(color.xyz(), "xyz(1, 1, 1)");
Expand All @@ -259,6 +280,7 @@ mod tests {
assert_eq!(color.rgb(), "rgb(0, 0, 0)");
assert_eq!(color.rgba(), "rgba(0, 0, 0, 0.5)");
assert_eq!(color.hsl(), "hsl(0, 0%, 0%)");
assert_eq!(color.hsla(), "hsla(0, 0%, 0%, 0.5)");
assert_eq!(color.hsv(), "hsv(0, 0%, 0%)");
assert_eq!(color.hwb(), "hwb(0, 0%, 100%)");
assert_eq!(color.xyz(), "xyz(0.137931, 0.137931, 0.137931)");
Expand All @@ -271,6 +293,7 @@ mod tests {
assert_eq!(color.rgb(), "rgb(0, 128, 128)");
assert_eq!(color.rgba(), "rgba(0, 128, 128, 1)");
assert_eq!(color.hsl(), "hsl(180, 100%, 25%)");
assert_eq!(color.hsla(), "hsla(180, 100%, 25%, 1)");
assert_eq!(color.hsv(), "hsv(180, 100%, 50%)");
assert_eq!(color.hwb(), "hwb(180, 0%, 50%)");
assert_eq!(color.xyz(), "xyz(0.496222, 0.553915, 0.596299)");
Expand Down
4 changes: 4 additions & 0 deletions src/color/vec_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ impl Color {
let (h, s, l) = conversion::hsl::rgb2hsl(color);
vec![h, s, l]
}
ColorSpace::HSLA => {
let (h, s, l) = conversion::hsl::rgb2hsl(color);
vec![h, s, l, self.alpha]
}
ColorSpace::HSV => {
let (h, s, v) = conversion::hsv::rgb2hsv(color);
vec![h, s, v]
Expand Down
5 changes: 5 additions & 0 deletions src/color_space/color_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ pub enum ColorSpace {
///
/// HSL stands for hue, saturation, and lightness.
HSL,
/// HSLA color space.
///
/// HSLA stands for hue, saturation, lightness, and alpha.
HSLA,
/// HSV color space.
///
/// HSV stands for hue, saturation, and value.
Expand Down Expand Up @@ -54,6 +58,7 @@ where
"rgb" => ColorSpace::RGB,
"rgba" => ColorSpace::RGBA,
"hsl" => ColorSpace::HSL,
"hsla" => ColorSpace::HSLA,
"hsv" => ColorSpace::HSV,
"hex" => ColorSpace::HEX,
"hwb" => ColorSpace::HWB,
Expand Down
19 changes: 19 additions & 0 deletions src/color_space/valid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,25 @@ impl ColorSpace {
}
}
}
ColorSpace::HSLA => {
if vec.len() != 4 {
anyhow::bail!("HSLA color space requires 4 values")
}
if let [h, s, l, a] = vec[..] {
if h < 0.0 || h > 360.0 {
anyhow::bail!("Hue must be between 0.0 and 360.0, got {}", h)
}
if s < 0.0 || s > 1.0 {
anyhow::bail!("Saturation must be between 0.0 and 1.0, got {}", s)
}
if l < 0.0 || l > 1.0 {
anyhow::bail!("Lightness must be between 0.0 and 1.0, got {}", l)
}
if a < 0.0 || a > 1.0 {
anyhow::bail!("Alpha must be between 0.0 and 1.0, got {}", a)
}
}
}
ColorSpace::HSV => {
if vec.len() != 3 {
anyhow::bail!("HSV color space requires 3 values")
Expand Down
49 changes: 49 additions & 0 deletions src/parser/hsla.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::ColorSpace;
use anyhow::Result;
use std::str::FromStr;

/// HSLA(Hue, Saturation, Lightness, Alpha)
pub fn parse_hsla_str(hsla_str: impl ToString) -> Result<(f64, f64, f64, f64)> {
// hsla_str like "hsla(120°, 1, 0.75, 0.6)"
let hsla_str = hsla_str
.to_string()
.trim()
.to_lowercase()
.replace(" ", "")
.replace("°", "")
.replace("hsla(", "")
.replace(")", "");

let mut hsla_vec = hsla_str.split(",").map(|s| {
if s.contains('%') {
f64::from_str(s.replace("%", "").as_str()).unwrap() / 100.
} else {
f64::from_str(s).unwrap()
}
});

let h = hsla_vec.next().unwrap();
let s = hsla_vec.next().unwrap();
let l = hsla_vec.next().unwrap();
let a = hsla_vec.next().unwrap();

ColorSpace::HSLA.valid(&vec![h, s, l, a])?;

Ok((h, s, l, a))
}

#[cfg(test)]
mod tests {

use super::*;

#[test]
fn test_parse_hsla_str() {
let s = "hsla(120°, 1, 0.75, 0.6)";
let (h, s, l, a) = parse_hsla_str(s).unwrap();
assert_eq!(h, 120.0);
assert_eq!(s, 1.0);
assert_eq!(l, 0.75);
assert_eq!(a, 0.6);
}
}
1 change: 1 addition & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub(crate) mod cmyk;
pub(crate) mod hex;
pub(crate) mod hsl;
pub(crate) mod hsla;
pub(crate) mod hsv;
pub(crate) mod hwb;
pub(crate) mod lab;
Expand Down
1 change: 1 addition & 0 deletions tests/color_stringify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ fn test_color_stringify() {
assert_eq!(color.rgb(), "rgb(255, 255, 0)");
assert_eq!(color.rgba(), "rgba(255, 255, 0, 1)");
assert_eq!(color.hsl(), "hsl(60, 100%, 50%)");
assert_eq!(color.hsla(), "hsla(60, 100%, 50%, 1)");
assert_eq!(color.hsv(), "hsv(60, 100%, 100%)");
assert_eq!(color.hwb(), "hwb(60, 0%, 0%)");
assert_eq!(color.cmyk(), "cmyk(0%, 0%, 100%, 0%)");
Expand Down

0 comments on commit d1354f5

Please sign in to comment.