mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-31 13:56:54 -06:00
Reject empty arguments in parse_css color functions
Found while testing crates/color/src/lib.rs:247 (css_args, used by the rgb/rgba and hsl/hsla arms of parse_css at :159 and :178) Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
57a51af8bb
commit
9ae43a4c4e
+15
-5
@@ -157,7 +157,7 @@ impl Color {
|
||||
return Self::from_hex(&t);
|
||||
}
|
||||
if let Some(inner) = t.strip_prefix("rgba(").or_else(|| t.strip_prefix("rgb(")).and_then(|r| r.strip_suffix(')')) {
|
||||
let parts = css_args(inner);
|
||||
let parts = css_args(inner)?;
|
||||
let num = |i: usize| -> Option<f32> {
|
||||
let p = parts.get(i)?;
|
||||
if let Some(pc) = p.strip_suffix('%') {
|
||||
@@ -176,7 +176,7 @@ impl Color {
|
||||
return Some(Self::new(num(0)?, num(1)?, num(2)?, alpha));
|
||||
}
|
||||
if let Some(inner) = t.strip_prefix("hsla(").or_else(|| t.strip_prefix("hsl(")).and_then(|r| r.strip_suffix(')')) {
|
||||
let parts = css_args(inner);
|
||||
let parts = css_args(inner)?;
|
||||
let h = parts.first()?.trim_end_matches("deg").parse::<f32>().ok()?;
|
||||
let s = parts.get(1)?.trim_end_matches('%').parse::<f32>().ok()? / 100.0;
|
||||
let l = parts.get(2)?.trim_end_matches('%').parse::<f32>().ok()? / 100.0;
|
||||
@@ -244,14 +244,24 @@ impl Color {
|
||||
}
|
||||
}
|
||||
|
||||
fn css_args(inner: &str) -> Vec<String> {
|
||||
inner.replace('/', ",").split([',', ' ']).map(str::trim).filter(|p| !p.is_empty()).map(str::to_string).collect()
|
||||
/// Split CSS function arguments on `,` and `/` (each may be surrounded by spaces) and on runs of
|
||||
/// whitespace. An empty argument between two separators makes the whole list invalid.
|
||||
fn css_args(inner: &str) -> Option<Vec<String>> {
|
||||
let mut out = Vec::new();
|
||||
for seg in inner.replace('/', ",").split(',') {
|
||||
let before = out.len();
|
||||
out.extend(seg.split_whitespace().map(str::to_string));
|
||||
if out.len() == before {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(out)
|
||||
}
|
||||
|
||||
/// The arguments of `lch()` / `oklch()`: lightness, chroma, hue and alpha. Percentages are of
|
||||
/// `l_full` / `c_full`; `none` is 0.
|
||||
fn css_polar(inner: &str, l_full: f32, c_full: f32) -> Option<([f32; 3], f32)> {
|
||||
let parts = css_args(inner);
|
||||
let parts = css_args(inner)?;
|
||||
if !(3..=4).contains(&parts.len()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -148,3 +148,12 @@ fn css_hsl_rejects_non_finite() {
|
||||
assert_eq!(Color::parse_css(s), None, "{s}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn css_rejects_empty_arguments() {
|
||||
for s in ["rgb(255,,0,0)", "rgb(,1,2,3)", "rgb(1,2,3,)", "hsl(10,,50%,50%)", "rgb(1,2,,)", "oklch(0.5,,0.1,20)"] {
|
||||
assert_eq!(Color::parse_css(s), None, "{s}");
|
||||
}
|
||||
assert!(Color::parse_css("rgb(255 0 0 / 50%)").is_some());
|
||||
assert!(Color::parse_css("hsl(120deg 50% 50%)").is_some());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user