1use cssparser::{Parser, ParserInput, Token};
4use markup5ever::QualName;
5use std::str;
6
7use crate::error::*;
8
9pub trait Parse: Sized {
11 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>>;
15
16 fn parse_str(s: &str) -> Result<Self, ParseError<'_>> {
21 let mut input = ParserInput::new(s);
22 let mut parser = Parser::new(&mut input);
23
24 let res = Self::parse(&mut parser)?;
25 parser.expect_exhausted()?;
26
27 Ok(res)
28 }
29}
30
31pub fn optional_comma(parser: &mut Parser<'_, '_>) {
33 let _ = parser.try_parse(|p| p.expect_comma());
34}
35
36pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
38 if n.is_finite() {
39 Ok(n)
40 } else {
41 Err(ValueErrorKind::Value("expected finite number".to_string()))
42 }
43}
44
45pub trait ParseValue<T: Parse> {
68 fn parse(&self, value: &str) -> Result<T, ElementError>;
70}
71
72impl<T: Parse> ParseValue<T> for QualName {
73 fn parse(&self, value: &str) -> Result<T, ElementError> {
85 let mut input = ParserInput::new(value);
86 let mut parser = Parser::new(&mut input);
87
88 T::parse(&mut parser)
89 .and_then(|v| {
90 parser.expect_exhausted()?;
91 Ok(v)
92 })
93 .attribute(self.clone())
94 }
95}
96
97impl<T: Parse> Parse for Option<T> {
98 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
99 T::parse(parser).map(Some)
100 }
101}
102
103impl Parse for f64 {
104 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
105 let loc = parser.current_source_location();
106 let n = parser.expect_number()?;
107 if n.is_finite() {
108 Ok(f64::from(n))
109 } else {
110 Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
111 }
112 }
113}
114
115#[derive(Debug, Copy, Clone, PartialEq)]
117pub struct NonNegative(pub f64);
118
119impl Parse for NonNegative {
120 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
121 let loc = parser.current_source_location();
122 let n = Parse::parse(parser)?;
123 if n >= 0.0 {
124 Ok(NonNegative(n))
125 } else {
126 Err(loc.new_custom_error(ValueErrorKind::value_error("expected non negative number")))
127 }
128 }
129}
130
131#[derive(Debug, Copy, Clone, PartialEq)]
135pub struct NumberOptionalNumber<T: Parse>(pub T, pub T);
136
137impl<T: Parse + Copy> Parse for NumberOptionalNumber<T> {
138 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
139 let x = Parse::parse(parser)?;
140
141 if !parser.is_exhausted() {
142 optional_comma(parser);
143 let y = Parse::parse(parser)?;
144 Ok(NumberOptionalNumber(x, y))
145 } else {
146 Ok(NumberOptionalNumber(x, x))
147 }
148 }
149}
150
151#[derive(Debug, Copy, Clone, PartialEq)]
155pub struct NumberOrPercentage {
156 pub value: f64,
157}
158
159impl Parse for NumberOrPercentage {
160 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
161 let loc = parser.current_source_location();
162
163 let value = match parser.next()? {
164 Token::Number { value, .. } => Ok(*value),
165 Token::Percentage { unit_value, .. } => Ok(*unit_value),
166 tok => Err(loc.new_unexpected_token_error(tok.clone())),
167 }?;
168
169 let v = finite_f32(value).map_err(|e| parser.new_custom_error(e))?;
170 Ok(NumberOrPercentage {
171 value: f64::from(v),
172 })
173 }
174}
175
176impl Parse for i32 {
177 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
181 Ok(parser.expect_integer()?)
182 }
183}
184
185impl Parse for u32 {
186 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
187 let loc = parser.current_source_location();
188 let n = parser.expect_integer()?;
189 if n >= 0 {
190 Ok(n as u32)
191 } else {
192 Err(loc.new_custom_error(ValueErrorKind::value_error("expected unsigned number")))
193 }
194 }
195}
196
197#[derive(Clone, Debug, PartialEq)]
199pub struct CommaSeparatedList<T: Parse, const REQUIRED: usize, const MAX: usize>(pub Vec<T>);
200
201impl<T: Parse, const REQUIRED: usize, const MAX: usize> Parse
202 for CommaSeparatedList<T, REQUIRED, MAX>
203{
204 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
205 let loc = parser.current_source_location();
206 let mut v = Vec::<T>::with_capacity(MAX);
207 for i in 0..MAX {
208 if i != 0 {
209 optional_comma(parser);
210 }
211
212 v.push(T::parse(parser)?);
213
214 if parser.is_exhausted() {
215 break;
216 }
217 }
218
219 if REQUIRED > 0 && v.len() < REQUIRED {
220 Err(loc.new_custom_error(ValueErrorKind::value_error("expected more values")))
221 } else {
222 v.shrink_to_fit();
223 Ok(CommaSeparatedList(v))
224 }
225 }
226}
227
228#[doc(hidden)]
247#[macro_export]
248macro_rules! parse_identifiers {
249 ($parser:expr,
250 $($str:expr => $val:expr,)+) => {
251 {
252 let loc = $parser.current_source_location();
253 let token = $parser.next()?;
254
255 match token {
256 $(&cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+
257
258 _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
259 }
260 }
261 };
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, Hash)]
268pub struct CustomIdent(pub String);
269
270impl Parse for CustomIdent {
271 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
272 let loc = parser.current_source_location();
273 let token = parser.next()?;
274
275 match token {
276 Token::Ident(cow) => {
279 for s in &["initial", "inherit", "unset", "default"] {
280 if cow.eq_ignore_ascii_case(s) {
281 return Err(loc.new_basic_unexpected_token_error(token.clone()).into());
282 }
283 }
284
285 Ok(CustomIdent(cow.as_ref().to_string()))
286 }
287
288 _ => Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
289 }
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 use markup5ever::{QualName, local_name, ns};
298
299 #[test]
300 fn parses_number_optional_number() {
301 assert_eq!(
302 NumberOptionalNumber::parse_str("1, 2").unwrap(),
303 NumberOptionalNumber(1.0, 2.0)
304 );
305 assert_eq!(
306 NumberOptionalNumber::parse_str("1 2").unwrap(),
307 NumberOptionalNumber(1.0, 2.0)
308 );
309 assert_eq!(
310 NumberOptionalNumber::parse_str("1").unwrap(),
311 NumberOptionalNumber(1.0, 1.0)
312 );
313
314 assert_eq!(
315 NumberOptionalNumber::parse_str("-1, -2").unwrap(),
316 NumberOptionalNumber(-1.0, -2.0)
317 );
318 assert_eq!(
319 NumberOptionalNumber::parse_str("-1 -2").unwrap(),
320 NumberOptionalNumber(-1.0, -2.0)
321 );
322 assert_eq!(
323 NumberOptionalNumber::parse_str("-1").unwrap(),
324 NumberOptionalNumber(-1.0, -1.0)
325 );
326 }
327
328 #[test]
329 fn invalid_number_optional_number() {
330 assert!(NumberOptionalNumber::<f64>::parse_str("").is_err());
331 assert!(NumberOptionalNumber::<f64>::parse_str("1x").is_err());
332 assert!(NumberOptionalNumber::<f64>::parse_str("x1").is_err());
333 assert!(NumberOptionalNumber::<f64>::parse_str("1 x").is_err());
334 assert!(NumberOptionalNumber::<f64>::parse_str("1 , x").is_err());
335 assert!(NumberOptionalNumber::<f64>::parse_str("1 , 2x").is_err());
336 assert!(NumberOptionalNumber::<f64>::parse_str("1 2 x").is_err());
337 }
338
339 #[test]
340 fn parses_integer() {
341 assert_eq!(i32::parse_str("0").unwrap(), 0);
342 assert_eq!(i32::parse_str("1").unwrap(), 1);
343 assert_eq!(i32::parse_str("-1").unwrap(), -1);
344
345 assert_eq!(u32::parse_str("0").unwrap(), 0);
346 assert_eq!(u32::parse_str("1").unwrap(), 1);
347 }
348
349 #[test]
350 fn invalid_integer() {
351 assert!(i32::parse_str("").is_err());
352 assert!(i32::parse_str("1x").is_err());
353 assert!(i32::parse_str("1.5").is_err());
354
355 assert!(u32::parse_str("").is_err());
356 assert!(u32::parse_str("1x").is_err());
357 assert!(u32::parse_str("1.5").is_err());
358 assert!(u32::parse_str("-1").is_err());
359 }
360
361 #[test]
362 fn parses_integer_optional_integer() {
363 assert_eq!(
364 NumberOptionalNumber::parse_str("1, 2").unwrap(),
365 NumberOptionalNumber(1, 2)
366 );
367 assert_eq!(
368 NumberOptionalNumber::parse_str("1 2").unwrap(),
369 NumberOptionalNumber(1, 2)
370 );
371 assert_eq!(
372 NumberOptionalNumber::parse_str("1").unwrap(),
373 NumberOptionalNumber(1, 1)
374 );
375
376 assert_eq!(
377 NumberOptionalNumber::parse_str("-1, -2").unwrap(),
378 NumberOptionalNumber(-1, -2)
379 );
380 assert_eq!(
381 NumberOptionalNumber::parse_str("-1 -2").unwrap(),
382 NumberOptionalNumber(-1, -2)
383 );
384 assert_eq!(
385 NumberOptionalNumber::parse_str("-1").unwrap(),
386 NumberOptionalNumber(-1, -1)
387 );
388 }
389
390 #[test]
391 fn invalid_integer_optional_integer() {
392 assert!(NumberOptionalNumber::<i32>::parse_str("").is_err());
393 assert!(NumberOptionalNumber::<i32>::parse_str("1x").is_err());
394 assert!(NumberOptionalNumber::<i32>::parse_str("x1").is_err());
395 assert!(NumberOptionalNumber::<i32>::parse_str("1 x").is_err());
396 assert!(NumberOptionalNumber::<i32>::parse_str("1 , x").is_err());
397 assert!(NumberOptionalNumber::<i32>::parse_str("1 , 2x").is_err());
398 assert!(NumberOptionalNumber::<i32>::parse_str("1 2 x").is_err());
399 assert!(NumberOptionalNumber::<i32>::parse_str("1.5").is_err());
400 assert!(NumberOptionalNumber::<i32>::parse_str("1 2.5").is_err());
401 assert!(NumberOptionalNumber::<i32>::parse_str("1, 2.5").is_err());
402 }
403
404 #[test]
405 fn parses_comma_separated_list() {
406 assert_eq!(
407 CommaSeparatedList::<f64, 1, 1>::parse_str("5").unwrap(),
408 CommaSeparatedList(vec![5.0])
409 );
410
411 assert_eq!(
412 CommaSeparatedList::<f64, 4, 4>::parse_str("1 2 3 4").unwrap(),
413 CommaSeparatedList(vec![1.0, 2.0, 3.0, 4.0])
414 );
415
416 assert_eq!(
417 CommaSeparatedList::<f64, 0, 5>::parse_str("1 2 3 4 5").unwrap(),
418 CommaSeparatedList(vec![1.0, 2.0, 3.0, 4.0, 5.0])
419 );
420
421 assert_eq!(
422 CommaSeparatedList::<f64, 0, 5>::parse_str("1 2 3").unwrap(),
423 CommaSeparatedList(vec![1.0, 2.0, 3.0])
424 );
425 }
426
427 #[test]
428 fn errors_on_invalid_comma_separated_list() {
429 assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("").is_err());
431 assert!(CommaSeparatedList::<f64, 0, 1>::parse_str("").is_err());
432
433 assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("foo").is_err());
435 assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1foo").is_err());
436 assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1 foo").is_err());
437 assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1 foo 2").is_err());
438 assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1,foo").is_err());
439
440 assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("1 2").is_err());
442
443 assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("1,").is_err());
445 assert!(CommaSeparatedList::<f64, 0, 1>::parse_str("1,").is_err());
446
447 assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1").is_err());
449 assert!(CommaSeparatedList::<f64, 3, 3>::parse_str("1 2").is_err());
450 }
451
452 #[test]
453 fn detects_too_many_numbers_bug_1138() {
454 let attribute = QualName::new(None, ns!(svg), local_name!("matrix"));
459
460 let r: Result<CommaSeparatedList<f64, 20, 20>, _> =
462 attribute.parse("1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0,500000 0 ");
463 assert!(r.is_err());
464 }
465
466 #[test]
467 fn parses_custom_ident() {
468 assert_eq!(
469 CustomIdent::parse_str("hello").unwrap(),
470 CustomIdent("hello".to_string())
471 );
472 }
473
474 #[test]
475 fn invalid_custom_ident_yields_error() {
476 assert!(CustomIdent::parse_str("initial").is_err());
477 assert!(CustomIdent::parse_str("inherit").is_err());
478 assert!(CustomIdent::parse_str("unset").is_err());
479 assert!(CustomIdent::parse_str("default").is_err());
480 assert!(CustomIdent::parse_str("").is_err());
481 }
482}