Skip to content

Commit e7b8a9a

Browse files
committed
Add #rgb and #rrggbb color parsing.
Only functional colors are missing now: (rgb|hsl)a?
1 parent e2f6135 commit e7b8a9a

File tree

3 files changed

+87
-27
lines changed

3 files changed

+87
-27
lines changed

color.rs

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,67 @@ pub enum Color {
1414

1515

1616
// Return None on invalid/unsupported value (not a color)
17-
pub fn parse_color(component_value: ComponentValue) -> Option<Color> {
18-
match component_value {
19-
Ident(value) => {
20-
let lower_value = to_ascii_lower(value);
21-
match COLOR_KEYWORDS.bsearch_elem(&lower_value.as_slice()) {
22-
Some(index) => Some(COLOR_VALUES[index]),
23-
None => {
24-
if "currentcolor" == lower_value { Some(CurrentColor) }
25-
else { None }
26-
}
17+
pub fn parse_color(component_value: &ComponentValue) -> Option<Color> {
18+
match *component_value {
19+
Hash(ref value) | IDHash(ref value) => parse_hash_color(*value),
20+
Ident(ref value) => parse_keyword_color(*value),
21+
Function(ref name, ref arguments) => parse_function_color(*name, *arguments),
22+
_ => None
23+
}
24+
}
25+
26+
27+
#[inline]
28+
fn parse_keyword_color(value: &str) -> Option<Color> {
29+
let lower_value = to_ascii_lower(value);
30+
match COLOR_KEYWORDS.bsearch_elem(&lower_value.as_slice()) {
31+
Some(index) => Some(COLOR_VALUES[index]),
32+
None => if "currentcolor" == lower_value { Some(CurrentColor) }
33+
else { None }
34+
}
35+
}
36+
37+
38+
#[inline]
39+
fn parse_hash_color(value: &str) -> Option<Color> {
40+
macro_rules! from_hex(
41+
($c: expr) => {{
42+
let c = $c;
43+
match c as char {
44+
'0' .. '9' => c - ('0' as u8),
45+
'a' .. 'f' => c - ('a' as u8) + 10,
46+
'A' .. 'F' => c - ('A' as u8) + 10,
47+
_ => return None // Not a valid color
2748
}
28-
}
49+
}};
50+
)
51+
macro_rules! to_rgba(
52+
($r: expr, $g: expr, $b: expr,) => {
53+
Some(RGBA($r as ColorFloat / 255., $g as ColorFloat / 255.,
54+
$b as ColorFloat / 255., 1.))
55+
};
56+
)
57+
58+
match value.len() {
59+
6 => to_rgba!(
60+
from_hex!(value[0]) * 16 + from_hex!(value[1]),
61+
from_hex!(value[2]) * 16 + from_hex!(value[3]),
62+
from_hex!(value[4]) * 16 + from_hex!(value[5]),
63+
),
64+
3 => to_rgba!(
65+
from_hex!(value[0]) * 17,
66+
from_hex!(value[1]) * 17,
67+
from_hex!(value[2]) * 17,
68+
),
2969
_ => None
3070
}
3171
}
72+
73+
74+
#[inline]
75+
fn parse_function_color(name: &str, arguments: &[(ComponentValue, SourceLocation)])
76+
-> Option<Color> {
77+
let _lower_name = to_ascii_lower(name);
78+
let _iter = arguments.iter();
79+
None // TODO
80+
}

make_color_data.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ALL_COLORS = sorted([
1+
COLORS = sorted([
22
('transparent', (0, 0, 0, 0)),
33

44
('black', (0, 0, 0, 1)),
@@ -171,14 +171,11 @@
171171
// This file is generated by make_color_data.py
172172
// This is to make sure that COLOR_KEYWORDS is sorted, to allow binary search.
173173
174-
use super::{Color, RGBA, ColorFloat};
174+
use super::{Color, RGBA};
175175
176176
macro_rules! RGBA(
177177
($r:expr, $g:expr, $b:expr, $a:expr) => {
178-
RGBA(($r as ColorFloat) / (255 as ColorFloat),
179-
($g as ColorFloat) / (255 as ColorFloat),
180-
($b as ColorFloat) / (255 as ColorFloat),
181-
($a as ColorFloat))
178+
RGBA($r / 255., $g / 255., $b / 255., $a)
182179
};
183180
)
184181
@@ -190,6 +187,6 @@
190187
%s
191188
];
192189
''' % (
193-
'\n'.join(' "%s",' % name for name, _ in ALL_COLORS),
194-
'\n'.join(' RGBA!(%s, %s, %s, %s),' % rgba for _, rgba in ALL_COLORS),
190+
'\n'.join(' "%s",' % keyword for keyword, _ in COLORS),
191+
'\n'.join(' RGBA!(%s., %s., %s., %s.),' % rgba for _, rgba in COLORS),
195192
))

tests.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,22 @@ fn write_whole_file(path: &Path, data: &str) {
2020
}
2121

2222

23+
fn almost_equals(a: &json::Json, b: &json::Json) -> bool {
24+
match (a, b) {
25+
(&json::Number(a), &json::Number(b)) => (a - b).abs() < 1e-10,
26+
(&json::String(ref a), &json::String(ref b)) => a == b,
27+
(&json::Boolean(a), &json::Boolean(b)) => a == b,
28+
(&json::List(ref a), &json::List(ref b))
29+
=> a.iter().zip(b.iter()).all(|(ref a, ref b)| almost_equals(*a, *b)),
30+
(&json::Object(_), &json::Object(_)) => fail!(~"Not implemented"),
31+
(&json::Null, &json::Null) => true,
32+
_ => false,
33+
}
34+
}
35+
36+
2337
fn assert_json_eq(results: json::Json, expected: json::Json, message: ~str) {
24-
if results != expected {
38+
if !almost_equals(&results, &expected) {
2539
let temp = tempfile::mkdtemp(&os::tmpdir(), "rust-cssparser-tests").get();
2640
let temp_ = copy temp;
2741
let results = json::to_pretty_str(&results) + "\n";
@@ -138,17 +152,17 @@ fn one_rule() {
138152
fn run_color_tests(json_data: &str, to_json: &fn(result: Option<Color>) -> json::Json) {
139153
do run_json_tests(json_data) |input| {
140154
match parse_one_component_value(&mut ComponentValueIterator::from_str(input)) {
141-
Ok((component_value, _location)) => to_json(parse_color(component_value)),
155+
Ok((component_value, _location)) => to_json(parse_color(&component_value)),
142156
Err(_reason) => json::Null,
143157
}
144158
}
145159
}
146160
147161
148-
//#[test]
149-
//fn color3() {
150-
// run_color_tests(include_str!("css-parsing-tests/color3.json"), |c| c.to_json())
151-
//}
162+
#[test]
163+
fn color3() {
164+
run_color_tests(include_str!("css-parsing-tests/color3.json"), |c| c.to_json())
165+
}
152166
153167
154168
//#[test]
@@ -157,12 +171,12 @@ fn run_color_tests(json_data: &str, to_json: &fn(result: Option<Color>) -> json:
157171
//}
158172
159173
174+
/// color3_keywords.json is different: R, G and B are in 0..255 rather than 0..1
160175
#[test]
161176
fn color3_keywords() {
162177
do run_color_tests(include_str!("css-parsing-tests/color3_keywords.json")) |c| {
163-
let m = 255 as ColorFloat;
164178
match c {
165-
Some(RGBA(r, g, b, a)) => (~[r * m, g * m, b * m, a]).to_json(),
179+
Some(RGBA(r, g, b, a)) => (~[r * 255., g * 255., b * 255., a]).to_json(),
166180
Some(CurrentColor) => json::String(~"currentColor"),
167181
None => json::Null,
168182
}

0 commit comments

Comments
 (0)