Reject a leading '+' in ACI indices and RGB triples in Color::parse (#1878)

Found while testing crates/color/src/lib.rs:115 and 126

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
NetterSilver
2026-10-24 01:59:45 -04:00
committed by GitHub
co-authored by Claude Opus 5.5 Brandon Thomas
parent 7aa952b8da
commit b062030a03
+25 -4
View File
@@ -111,9 +111,9 @@ impl Color {
_ => {}
}
if let Some(rest) = t.strip_prefix("color ") {
return rest.trim().parse::<u8>().ok().filter(|i| *i >= 1).map(Color::Index);
return parse_digits::<u8>(rest.trim()).filter(|i| *i >= 1).map(Color::Index);
}
if let Ok(i) = t.parse::<u16>() {
if let Some(i) = parse_digits::<u16>(&t) {
return match i {
0 => Some(Color::ByBlock),
256 => Some(Color::ByLayer),
@@ -124,14 +124,22 @@ impl Color {
if t.starts_with('#') {
return Rgb::parse_hex(&t).map(Color::True);
}
let parts: Vec<_> = t.split(',').map(|p| p.trim().parse::<u8>()).collect();
if let [Ok(r), Ok(g), Ok(b)] = parts.as_slice() {
let parts: Vec<_> = t.split(',').map(|p| parse_digits::<u8>(p.trim())).collect();
if let [Some(r), Some(g), Some(b)] = parts.as_slice() {
return Some(Color::True(Rgb(*r, *g, *b)));
}
None
}
}
/// Parse an unsigned integer made of ASCII digits only (no sign, unlike `str::parse`).
fn parse_digits<T: std::str::FromStr>(s: &str) -> Option<T> {
if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
s.parse().ok()
}
/// The RGB of ACI `i` (0 and out-of-range map to white/7).
pub fn aci_rgb(i: u8) -> Rgb {
PALETTE.get(usize::from(i)).copied().unwrap_or(Rgb(255, 255, 255))
@@ -234,6 +242,19 @@ mod tests {
assert_eq!(Rgb::parse_hex("#012345"), Some(Rgb(1, 35, 69)));
}
#[test]
fn parse_rejects_plus_sign() {
assert_eq!(Color::parse("+5"), None);
assert_eq!(Color::parse(" +256"), None);
assert_eq!(Color::parse("color +5"), None);
assert_eq!(Color::parse("+1,+2,+3"), None);
assert_eq!(Color::parse("1,+2,3"), None);
assert_eq!(Color::parse("5"), Some(Color::Index(5)));
assert_eq!(Color::parse("256"), Some(Color::ByLayer));
assert_eq!(Color::parse("color 5"), Some(Color::Index(5)));
assert_eq!(Color::parse("1, 2, 3"), Some(Color::True(Rgb(1, 2, 3))));
}
#[test]
fn parse_hex_rejects_repeated_hash() {
assert_eq!(Rgb::parse_hex("##ff0000"), None);