Add tests for Color::parse

This commit is contained in:
Alexandre Bury 2020-01-08 17:20:00 -08:00
parent 45a9d54801
commit 29f85a4398

View File

@ -155,14 +155,14 @@ impl Color {
/// * `"#123456"` becomes `Color::Rgb(0x12, 0x34, 0x56)` /// * `"#123456"` becomes `Color::Rgb(0x12, 0x34, 0x56)`
pub fn parse(value: &str) -> Option<Self> { pub fn parse(value: &str) -> Option<Self> {
Some(match value { Some(match value {
"black" => Color::Dark(BaseColor::Black), "dark black" | "black" => Color::Dark(BaseColor::Black),
"red" => Color::Dark(BaseColor::Red), "dark red" | "red" => Color::Dark(BaseColor::Red),
"green" => Color::Dark(BaseColor::Green), "dark green" | "green" => Color::Dark(BaseColor::Green),
"yellow" => Color::Dark(BaseColor::Yellow), "dark yellow" | "yellow" => Color::Dark(BaseColor::Yellow),
"blue" => Color::Dark(BaseColor::Blue), "dark blue" | "blue" => Color::Dark(BaseColor::Blue),
"magenta" => Color::Dark(BaseColor::Magenta), "dark magenta" | "magenta" => Color::Dark(BaseColor::Magenta),
"cyan" => Color::Dark(BaseColor::Cyan), "dark cyan" | "cyan" => Color::Dark(BaseColor::Cyan),
"white" => Color::Dark(BaseColor::White), "dark white" | "white" => Color::Dark(BaseColor::White),
"light black" => Color::Light(BaseColor::Black), "light black" => Color::Light(BaseColor::Black),
"light red" => Color::Light(BaseColor::Red), "light red" => Color::Light(BaseColor::Red),
"light green" => Color::Light(BaseColor::Green), "light green" => Color::Light(BaseColor::Green),
@ -172,24 +172,23 @@ impl Color {
"light cyan" => Color::Light(BaseColor::Cyan), "light cyan" => Color::Light(BaseColor::Cyan),
"light white" => Color::Light(BaseColor::White), "light white" => Color::Light(BaseColor::White),
"default" => Color::TerminalDefault, "default" => Color::TerminalDefault,
value => return Color::parse_special(value), value => {
return parse_special(value).or_else(|| {
log::warn!("Could not parse color `{}`.", value);
None
}) })
} }
})
}
}
fn parse_special(value: &str) -> Option<Color> { fn parse_special(value: &str) -> Option<Color> {
if value.starts_with('#') { if value.starts_with('#') {
let value = &value[1..]; parse_hex(&value[1..])
// Compute per-color length, and amplitude } else if value.starts_with("0x") {
let (l, multiplier) = match value.len() { parse_hex(&value[2..])
6 => (2, 1), } else if value.len() == 6 {
3 => (1, 17), parse_hex(value)
_ => panic!("Cannot parse color: {}", value),
};
let r = load_hex(&value[0..l]) * multiplier;
let g = load_hex(&value[l..2 * l]) * multiplier;
let b = load_hex(&value[2 * l..3 * l]) * multiplier;
Some(Color::Rgb(r as u8, g as u8, b as u8))
} else if value.len() == 3 { } else if value.len() == 3 {
// RGB values between 0 and 5 maybe? // RGB values between 0 and 5 maybe?
// Like 050 for green // Like 050 for green
@ -198,47 +197,111 @@ impl Color {
assert_eq!(rgb.len(), 3); assert_eq!(rgb.len(), 3);
if rgb.iter().all(|&i| i >= 0 && i < 6) { if rgb.iter().all(|&i| i >= 0 && i < 6) {
Some(Color::RgbLowRes( Some(Color::RgbLowRes(rgb[0] as u8, rgb[1] as u8, rgb[2] as u8))
rgb[0] as u8,
rgb[1] as u8,
rgb[2] as u8,
))
} else { } else {
None None
} }
} else { } else {
None None
} }
} }
fn parse_hex(value: &str) -> Option<Color> {
// Compute per-color length, and amplitude
let (l, multiplier) = match value.len() {
6 => (2, 1),
3 => (1, 17),
_ => return None,
};
let r = load_hex(&value[0..l]) * multiplier;
let g = load_hex(&value[l..2 * l]) * multiplier;
let b = load_hex(&value[2 * l..3 * l]) * multiplier;
Some(Color::Rgb(r as u8, g as u8, b as u8))
} }
/// Loads a hexadecimal code /// Loads a hexadecimal code
fn load_hex(s: &str) -> u16 { fn load_hex(s: &str) -> u16 {
let mut sum = 0; s.chars()
for c in s.chars() { .rev()
sum *= 16; .filter_map(|c| {
sum += match c { Some(match c {
n @ '0'..='9' => n as i16 - '0' as i16, '0'..='9' => c as u16 - '0' as u16,
n @ 'a'..='f' => n as i16 - 'a' as i16 + 10, 'a'..='f' => c as u16 - 'a' as u16 + 10,
n @ 'A'..='F' => n as i16 - 'A' as i16 + 10, 'A'..='F' => c as u16 - 'A' as u16 + 10,
_ => 0, other => {
}; log::warn!(
"Invalid character `{}` in hexadecimal value `{}`.",
other,
s
);
return None;
} }
})
sum as u16 })
.enumerate()
.map(|(i, c)| c * 16u16.pow(i as u32))
.sum()
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Color;
#[test] #[test]
fn test_256_colors() { fn test_256_colors() {
// Make sure Color::from_256colors never panics // Make sure Color::from_256colors never panics
use super::Color;
// TODO: use inclusive range when it gets stable for i in 0..=255u8 {
for i in 0..256u16 { Color::from_256colors(i);
Color::from_256colors(i as u8); }
}
#[test]
fn test_parse() {
assert_eq!(Color::parse("#fff"), Some(Color::Rgb(255, 255, 255)));
assert_eq!(
Color::parse("#abcdef"),
Some(Color::Rgb(0xab, 0xcd, 0xef))
);
assert_eq!(
Color::parse("0xFEDCBA"),
Some(Color::Rgb(0xfe, 0xdc, 0xba))
);
}
#[test]
fn test_low_res() {
// Make sure Color::low_res always works with valid ranges.
for r in 0..=5 {
for g in 0..=5 {
for b in 0..=5 {
assert!(
Color::low_res(r, g, b).is_some(),
"Could not create lowres color {}:{}:{}",
r,
g,
b,
);
}
}
}
for r in 6..=10 {
for g in 6..=10 {
for b in 6..=10 {
assert_eq!(
Color::low_res(r, g, b),
None,
"Created invalid lowres color {}:{}:{}",
r,
g,
b,
);
}
}
} }
} }
} }