Skip to content

Commit 06e204e

Browse files
author
bors-servo
authored
Auto merge of #155 - jdm:err2, r=SimonSapin
Return meaningful error values when parsing This is #143 with an extra commit added to accommodate changes requested in servo/servo#16752. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-cssparser/155) <!-- Reviewable:end -->
2 parents 1b1f703 + 970e1ca commit 06e204e

File tree

8 files changed

+572
-390
lines changed

8 files changed

+572
-390
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
name = "cssparser"
4-
version = "0.13.7"
4+
version = "0.14.0"
55
authors = [ "Simon Sapin <simon.sapin@exyr.org>" ]
66

77
description = "Rust implementation of CSS Syntax Level 3"

src/color.rs

+45-41
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use std::fmt;
66
use std::f32::consts::PI;
77

8-
use super::{Token, Parser, ToCss};
8+
use super::{Token, Parser, ToCss, ParseError, BasicParseError};
99
use tokenizer::NumericValue;
1010

1111
#[cfg(feature = "serde")]
@@ -141,46 +141,48 @@ impl Color {
141141
/// Parse a <color> value, per CSS Color Module Level 3.
142142
///
143143
/// FIXME(#2) Deprecated CSS2 System Colors are not supported yet.
144-
pub fn parse(input: &mut Parser) -> Result<Color, ()> {
145-
match try!(input.next()) {
146-
Token::Hash(value) | Token::IDHash(value) => {
144+
pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Color, BasicParseError<'i>> {
145+
let token = try!(input.next());
146+
match token {
147+
Token::Hash(ref value) | Token::IDHash(ref value) => {
147148
Color::parse_hash(value.as_bytes())
148149
},
149-
Token::Ident(value) => parse_color_keyword(&*value),
150-
Token::Function(name) => {
151-
input.parse_nested_block(|arguments| {
150+
Token::Ident(ref value) => parse_color_keyword(&*value),
151+
Token::Function(ref name) => {
152+
return input.parse_nested_block(|arguments| {
152153
parse_color_function(&*name, arguments)
153-
})
154+
.map_err(|e| ParseError::Basic(e))
155+
}).map_err(ParseError::<()>::basic);
154156
}
155157
_ => Err(())
156-
}
158+
}.map_err(|()| BasicParseError::UnexpectedToken(token))
157159
}
158160

159161
/// Parse a color hash, without the leading '#' character.
160162
#[inline]
161163
pub fn parse_hash(value: &[u8]) -> Result<Self, ()> {
162164
match value.len() {
163-
8 => rgba(
165+
8 => Ok(rgba(
164166
try!(from_hex(value[0])) * 16 + try!(from_hex(value[1])),
165167
try!(from_hex(value[2])) * 16 + try!(from_hex(value[3])),
166168
try!(from_hex(value[4])) * 16 + try!(from_hex(value[5])),
167-
try!(from_hex(value[6])) * 16 + try!(from_hex(value[7])),
169+
try!(from_hex(value[6])) * 16 + try!(from_hex(value[7]))),
168170
),
169-
6 => rgb(
171+
6 => Ok(rgb(
170172
try!(from_hex(value[0])) * 16 + try!(from_hex(value[1])),
171173
try!(from_hex(value[2])) * 16 + try!(from_hex(value[3])),
172-
try!(from_hex(value[4])) * 16 + try!(from_hex(value[5])),
174+
try!(from_hex(value[4])) * 16 + try!(from_hex(value[5]))),
173175
),
174-
4 => rgba(
176+
4 => Ok(rgba(
175177
try!(from_hex(value[0])) * 17,
176178
try!(from_hex(value[1])) * 17,
177179
try!(from_hex(value[2])) * 17,
178-
try!(from_hex(value[3])) * 17,
180+
try!(from_hex(value[3])) * 17),
179181
),
180-
3 => rgb(
182+
3 => Ok(rgb(
181183
try!(from_hex(value[0])) * 17,
182184
try!(from_hex(value[1])) * 17,
183-
try!(from_hex(value[2])) * 17,
185+
try!(from_hex(value[2])) * 17),
184186
),
185187
_ => Err(())
186188
}
@@ -190,13 +192,13 @@ impl Color {
190192

191193

192194
#[inline]
193-
fn rgb(red: u8, green: u8, blue: u8) -> Result<Color, ()> {
195+
fn rgb(red: u8, green: u8, blue: u8) -> Color {
194196
rgba(red, green, blue, 255)
195197
}
196198

197199
#[inline]
198-
fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Result<Color, ()> {
199-
Ok(Color::RGBA(RGBA::new(red, green, blue, alpha)))
200+
fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
201+
Color::RGBA(RGBA::new(red, green, blue, alpha))
200202
}
201203

202204

@@ -410,11 +412,11 @@ fn clamp_floor_256_f32(val: f32) -> u8 {
410412
}
411413

412414
#[inline]
413-
fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()> {
415+
fn parse_color_function<'i, 't>(name: &str, arguments: &mut Parser<'i, 't>) -> Result<Color, BasicParseError<'i>> {
414416
let (red, green, blue, uses_commas) = match_ignore_ascii_case! { name,
415417
"rgb" | "rgba" => parse_rgb_components_rgb(arguments)?,
416418
"hsl" | "hsla" => parse_rgb_components_hsl(arguments)?,
417-
_ => return Err(())
419+
_ => return Err(BasicParseError::UnexpectedToken(Token::Ident(name.to_owned().into()))),
418420
};
419421

420422
let alpha = if !arguments.is_exhausted() {
@@ -423,7 +425,7 @@ fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()>
423425
} else {
424426
match try!(arguments.next()) {
425427
Token::Delim('/') => {},
426-
_ => return Err(())
428+
t => return Err(BasicParseError::UnexpectedToken(t)),
427429
};
428430
};
429431
let token = try!(arguments.next());
@@ -434,21 +436,21 @@ fn parse_color_function(name: &str, arguments: &mut Parser) -> Result<Color, ()>
434436
Token::Percentage(ref v) => {
435437
clamp_unit_f32(v.unit_value)
436438
}
437-
_ => {
438-
return Err(())
439+
t => {
440+
return Err(BasicParseError::UnexpectedToken(t))
439441
}
440442
}
441443
} else {
442444
255
443445
};
444446

445447
try!(arguments.expect_exhausted());
446-
rgba(red, green, blue, alpha)
448+
Ok(rgba(red, green, blue, alpha))
447449
}
448450

449451

450452
#[inline]
451-
fn parse_rgb_components_rgb(arguments: &mut Parser) -> Result<(u8, u8, u8, bool), ()> {
453+
fn parse_rgb_components_rgb<'i, 't>(arguments: &mut Parser<'i, 't>) -> Result<(u8, u8, u8, bool), BasicParseError<'i>> {
452454
let red: u8;
453455
let green: u8;
454456
let blue: u8;
@@ -465,7 +467,7 @@ fn parse_rgb_components_rgb(arguments: &mut Parser) -> Result<(u8, u8, u8, bool)
465467
uses_commas = true;
466468
try!(arguments.expect_number())
467469
}
468-
_ => return Err(())
470+
t => return Err(BasicParseError::UnexpectedToken(t))
469471
});
470472
if uses_commas {
471473
try!(arguments.expect_comma());
@@ -480,36 +482,38 @@ fn parse_rgb_components_rgb(arguments: &mut Parser) -> Result<(u8, u8, u8, bool)
480482
uses_commas = true;
481483
try!(arguments.expect_percentage())
482484
}
483-
_ => return Err(())
485+
t => return Err(BasicParseError::UnexpectedToken(t))
484486
});
485487
if uses_commas {
486488
try!(arguments.expect_comma());
487489
}
488490
blue = clamp_unit_f32(try!(arguments.expect_percentage()));
489491
}
490-
_ => return Err(())
492+
t => return Err(BasicParseError::UnexpectedToken(t))
491493
};
492494
return Ok((red, green, blue, uses_commas));
493495
}
494496

495497
#[inline]
496-
fn parse_rgb_components_hsl(arguments: &mut Parser) -> Result<(u8, u8, u8, bool), ()> {
498+
fn parse_rgb_components_hsl<'i, 't>(arguments: &mut Parser<'i, 't>) -> Result<(u8, u8, u8, bool), BasicParseError<'i>> {
497499
let mut uses_commas = false;
498500
// Hue given as an angle
499501
// https://drafts.csswg.org/css-values/#angles
500-
let hue_degrees = match try!(arguments.next()) {
501-
Token::Number(NumericValue { value: v, .. }) => v,
502-
Token::Dimension(NumericValue { value: v, .. }, unit) => {
502+
let token = try!(arguments.next());
503+
let hue_degrees = match token {
504+
Token::Number(NumericValue { value: v, .. }) => Ok(v),
505+
Token::Dimension(NumericValue { value: v, .. }, ref unit) => {
503506
match_ignore_ascii_case! { &*unit,
504-
"deg" => v,
505-
"grad" => v * 360. / 400.,
506-
"rad" => v * 360. / (2. * PI),
507-
"turn" => v * 360.,
508-
_ => return Err(())
507+
"deg" => Ok(v),
508+
"grad" => Ok(v * 360. / 400.),
509+
"rad" => Ok(v * 360. / (2. * PI)),
510+
"turn" => Ok(v * 360.),
511+
_ => Err(()),
509512
}
510513
}
511-
_ => return Err(())
514+
t => return Err(BasicParseError::UnexpectedToken(t))
512515
};
516+
let hue_degrees = try!(hue_degrees.map_err(|()| BasicParseError::UnexpectedToken(token)));
513517
// Subtract an integer before rounding, to avoid some rounding errors:
514518
let hue_normalized_degrees = hue_degrees - 360. * (hue_degrees / 360.).floor();
515519
let hue = hue_normalized_degrees / 360.;
@@ -522,7 +526,7 @@ fn parse_rgb_components_hsl(arguments: &mut Parser) -> Result<(u8, u8, u8, bool)
522526
uses_commas = true;
523527
try!(arguments.expect_percentage())
524528
}
525-
_ => return Err(())
529+
t => return Err(BasicParseError::UnexpectedToken(t))
526530
};
527531
let saturation = saturation.max(0.).min(1.);
528532

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub use from_bytes::{stylesheet_encoding, EncodingSupport};
8989
pub use color::{RGBA, Color, parse_color_keyword};
9090
pub use nth::parse_nth;
9191
pub use serializer::{ToCss, CssStringWriter, serialize_identifier, serialize_string, TokenSerializationType};
92-
pub use parser::{Parser, Delimiter, Delimiters, SourcePosition};
92+
pub use parser::{Parser, Delimiter, Delimiters, SourcePosition, ParseError, BasicParseError, ParserInput};
9393
pub use unicode_range::UnicodeRange;
9494

9595
// For macros

src/nth.rs

+55-33
Original file line numberDiff line numberDiff line change
@@ -4,76 +4,98 @@
44

55
use std::ascii::AsciiExt;
66

7-
use super::{Token, Parser};
7+
use super::{Token, Parser, BasicParseError};
88

99

1010
/// Parse the *An+B* notation, as found in the `:nth-child()` selector.
1111
/// The input is typically the arguments of a function,
1212
/// in which case the caller needs to check if the arguments’ parser is exhausted.
1313
/// Return `Ok((A, B))`, or `Err(())` for a syntax error.
14-
pub fn parse_nth(input: &mut Parser) -> Result<(i32, i32), ()> {
15-
match try!(input.next()) {
16-
Token::Number(value) => Ok((0, try!(value.int_value.ok_or(())) as i32)),
17-
Token::Dimension(value, unit) => {
18-
let a = try!(value.int_value.ok_or(())) as i32;
19-
match_ignore_ascii_case! { &unit,
20-
"n" => parse_b(input, a),
21-
"n-" => parse_signless_b(input, a, -1),
22-
_ => Ok((a, try!(parse_n_dash_digits(&*unit))))
14+
pub fn parse_nth<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(i32, i32), BasicParseError<'i>> {
15+
let token = try!(input.next());
16+
match token {
17+
Token::Number(ref value) => {
18+
match value.int_value {
19+
Some(v) => Ok((0, v as i32)),
20+
None => Err(()),
2321
}
2422
}
25-
Token::Ident(value) => {
23+
Token::Dimension(value, ref unit) => {
24+
match value.int_value {
25+
Some(v) => {
26+
let a = v as i32;
27+
match_ignore_ascii_case! {
28+
&unit,
29+
"n" => Ok(try!(parse_b(input, a))),
30+
"n-" => Ok(try!(parse_signless_b(input, a, -1))),
31+
_ => {
32+
parse_n_dash_digits(&*unit).map(|val| (a, val))
33+
}
34+
}
35+
}
36+
None => Err(()),
37+
}
38+
}
39+
Token::Ident(ref value) => {
2640
match_ignore_ascii_case! { &value,
2741
"even" => Ok((2, 0)),
2842
"odd" => Ok((2, 1)),
29-
"n" => parse_b(input, 1),
30-
"-n" => parse_b(input, -1),
31-
"n-" => parse_signless_b(input, 1, -1),
32-
"-n-" => parse_signless_b(input, -1, -1),
43+
"n" => Ok(try!(parse_b(input, 1))),
44+
"-n" => Ok(try!(parse_b(input, -1))),
45+
"n-" => Ok(try!(parse_signless_b(input, 1, -1))),
46+
"-n-" => Ok(try!(parse_signless_b(input, -1, -1))),
3347
_ => if value.starts_with("-") {
34-
Ok((-1, try!(parse_n_dash_digits(&value[1..]))))
48+
parse_n_dash_digits(&value[1..]).map(|v| (-1, v))
3549
} else {
36-
Ok((1, try!(parse_n_dash_digits(&*value))))
50+
parse_n_dash_digits(&*value).map(|v| (1, v))
3751
}
3852
}
3953
}
4054
Token::Delim('+') => match try!(input.next_including_whitespace()) {
4155
Token::Ident(value) => {
4256
match_ignore_ascii_case! { &value,
43-
"n" => parse_b(input, 1),
44-
"n-" => parse_signless_b(input, 1, -1),
45-
_ => Ok((1, try!(parse_n_dash_digits(&*value))))
57+
"n" => Ok(try!(parse_b(input, 1))),
58+
"n-" => Ok(try!(parse_signless_b(input, 1, -1))),
59+
_ => parse_n_dash_digits(&*value).map(|v| (1, v))
4660
}
4761
}
48-
_ => Err(())
62+
t => return Err(BasicParseError::UnexpectedToken(t)),
4963
},
50-
_ => Err(())
51-
}
64+
_ => Err(()),
65+
}.map_err(|()| BasicParseError::UnexpectedToken(token))
5266
}
5367

5468

55-
fn parse_b(input: &mut Parser, a: i32) -> Result<(i32, i32), ()> {
69+
fn parse_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32) -> Result<(i32, i32), BasicParseError<'i>> {
5670
let start_position = input.position();
57-
match input.next() {
58-
Ok(Token::Delim('+')) => parse_signless_b(input, a, 1),
59-
Ok(Token::Delim('-')) => parse_signless_b(input, a, -1),
71+
let token = input.next();
72+
match token {
73+
Ok(Token::Delim('+')) => Ok(try!(parse_signless_b(input, a, 1))),
74+
Ok(Token::Delim('-')) => Ok(try!(parse_signless_b(input, a, -1))),
6075
Ok(Token::Number(ref value)) if value.has_sign => {
61-
Ok((a, try!(value.int_value.ok_or(())) as i32))
76+
match value.int_value {
77+
Some(v) => Ok((a, v as i32)),
78+
None => Err(()),
79+
}
6280
}
6381
_ => {
6482
input.reset(start_position);
6583
Ok((a, 0))
6684
}
67-
}
85+
}.map_err(|()| BasicParseError::UnexpectedToken(token.unwrap()))
6886
}
6987

70-
fn parse_signless_b(input: &mut Parser, a: i32, b_sign: i32) -> Result<(i32, i32), ()> {
71-
match try!(input.next()) {
88+
fn parse_signless_b<'i, 't>(input: &mut Parser<'i, 't>, a: i32, b_sign: i32) -> Result<(i32, i32), BasicParseError<'i>> {
89+
let token = try!(input.next());
90+
match token {
7291
Token::Number(ref value) if !value.has_sign => {
73-
Ok((a, b_sign * (try!(value.int_value.ok_or(())) as i32)))
92+
match value.int_value {
93+
Some(v) => Ok((a, b_sign * v as i32)),
94+
None => Err(()),
95+
}
7496
}
7597
_ => Err(())
76-
}
98+
}.map_err(|()| BasicParseError::UnexpectedToken(token))
7799
}
78100

79101
fn parse_n_dash_digits(string: &str) -> Result<i32, ()> {

0 commit comments

Comments
 (0)