1
//! The `Parse` trait for CSS properties, and utilities for parsers.
2

            
3
use cssparser::{Parser, ParserInput, Token};
4
use markup5ever::QualName;
5
use std::str;
6

            
7
use crate::error::*;
8

            
9
/// Trait to parse values using `cssparser::Parser`.
10
pub trait Parse: Sized {
11
    /// Parses a value out of the `parser`.
12
    ///
13
    /// All value types should implement this for composability.
14
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>>;
15

            
16
    /// Convenience function to parse a value out of a `&str`.
17
    ///
18
    /// This is useful mostly for tests which want to avoid creating a
19
    /// `cssparser::Parser` by hand.  Property types do not need to reimplement this.
20
18103258
    fn parse_str(s: &str) -> Result<Self, ParseError<'_>> {
21
18103398
        let mut input = ParserInput::new(s);
22
18103398
        let mut parser = Parser::new(&mut input);
23

            
24
18046580
        let res = Self::parse(&mut parser)?;
25
36135966
        parser.expect_exhausted()?;
26

            
27
18028287
        Ok(res)
28
18028550
    }
29
}
30

            
31
/// Consumes a comma if it exists, or does nothing.
32
16203
pub fn optional_comma(parser: &mut Parser<'_, '_>) {
33
32404
    let _ = parser.try_parse(|p| p.expect_comma());
34
16203
}
35

            
36
/// Parses an `f32` and ensures that it is not an infinity or NaN.
37
16915814
pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
38
16915814
    if n.is_finite() {
39
16915813
        Ok(n)
40
    } else {
41
1
        Err(ValueErrorKind::Value("expected finite number".to_string()))
42
    }
43
16915814
}
44

            
45
pub trait ParseValue<T: Parse> {
46
    /// Parses a `value` string into a type `T`.
47
    fn parse(&self, value: &str) -> Result<T, ElementError>;
48
}
49

            
50
impl<T: Parse> ParseValue<T> for QualName {
51
    /// Parse a value from an XML attribute.
52
    ///
53
    /// Say we have an attribute `bar` like in `<foo bar="42"/>`.  If `attr` is a [`QualName`]
54
    /// for the attribute `bar`, then we'll parse it like `attr.parse("42")`.
55
    ///
56
    /// The reason for doing things that way is so that, in case of a parse error, this
57
    /// function can annotate the error result with the attribute's name.
58
    ///
59
    /// Note that attribute values are parsed entirely, thus the call to
60
    /// `expect_exhausted()` below.  We don't want to allow garbage in the string after
61
    /// the initial value has been parsed.
62
7567
    fn parse(&self, value: &str) -> Result<T, ElementError> {
63
7589
        let mut input = ParserInput::new(value);
64
7589
        let mut parser = Parser::new(&mut input);
65

            
66
15155
        T::parse(&mut parser)
67
7574
            .and_then(|v| {
68
7574
                parser.expect_exhausted()?;
69
7569
                Ok(v)
70
15163
            })
71
15132
            .attribute(self.clone())
72
7565
    }
73
}
74

            
75
impl<T: Parse> Parse for Option<T> {
76
1928
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
77
1928
        T::parse(parser).map(Some)
78
1928
    }
79
}
80

            
81
impl Parse for f64 {
82
18406
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
83
18406
        let loc = parser.current_source_location();
84
18406
        let n = parser.expect_number()?;
85
17698
        if n.is_finite() {
86
17696
            Ok(f64::from(n))
87
        } else {
88
2
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
89
        }
90
18406
    }
91
}
92

            
93
/// Non-Negative number
94
90
#[derive(Debug, Copy, Clone, PartialEq)]
95
pub struct NonNegative(pub f64);
96

            
97
impl Parse for NonNegative {
98
69
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
99
69
        let loc = parser.current_source_location();
100
69
        let n = Parse::parse(parser)?;
101
69
        if n >= 0.0 {
102
69
            Ok(NonNegative(n))
103
        } else {
104
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected non negative number")))
105
        }
106
69
    }
107
}
108

            
109
/// CSS number-optional-number
110
///
111
/// SVG1.1: <https://www.w3.org/TR/SVG11/types.html#DataTypeNumberOptionalNumber>
112
122
#[derive(Debug, Copy, Clone, PartialEq)]
113
110
pub struct NumberOptionalNumber<T: Parse>(pub T, pub T);
114

            
115
impl<T: Parse + Copy> Parse for NumberOptionalNumber<T> {
116
112
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
117
112
        let x = Parse::parse(parser)?;
118

            
119
131
        if !parser.is_exhausted() {
120
34
            optional_comma(parser);
121
34
            let y = Parse::parse(parser)?;
122
26
            Ok(NumberOptionalNumber(x, y))
123
        } else {
124
71
            Ok(NumberOptionalNumber(x, x))
125
        }
126
112
    }
127
}
128

            
129
/// CSS number-percentage
130
///
131
/// CSS Values and Units 3: <https://www.w3.org/TR/css3-values/#typedef-number-percentage>
132
#[derive(Debug, Copy, Clone, PartialEq)]
133
pub struct NumberOrPercentage {
134
    pub value: f64,
135
}
136

            
137
impl Parse for NumberOrPercentage {
138
35
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
139
35
        let loc = parser.current_source_location();
140

            
141
63
        let value = match parser.next()? {
142
12
            Token::Number { value, .. } => Ok(*value),
143
9
            Token::Percentage { unit_value, .. } => Ok(*unit_value),
144
7
            tok => Err(loc.new_unexpected_token_error(tok.clone())),
145
7
        }?;
146

            
147
21
        let v = finite_f32(value).map_err(|e| parser.new_custom_error(e))?;
148
21
        Ok(NumberOrPercentage {
149
21
            value: f64::from(v),
150
        })
151
35
    }
152
}
153

            
154
impl Parse for i32 {
155
    /// CSS integer
156
    ///
157
    /// SVG1.1: <https://www.w3.org/TR/SVG11/types.html#DataTypeInteger>
158
41
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
159
41
        Ok(parser.expect_integer()?)
160
41
    }
161
}
162

            
163
impl Parse for u32 {
164
33
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
165
33
        let loc = parser.current_source_location();
166
33
        let n = parser.expect_integer()?;
167
30
        if n >= 0 {
168
29
            Ok(n as u32)
169
        } else {
170
1
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected unsigned number")))
171
        }
172
33
    }
173
}
174

            
175
/// List separated by optional commas, with bounds for the required and maximum number of items.
176
72
#[derive(Clone, Debug, PartialEq)]
177
36
pub struct CommaSeparatedList<T: Parse, const REQUIRED: usize, const MAX: usize>(pub Vec<T>);
178

            
179
impl<T: Parse, const REQUIRED: usize, const MAX: usize> Parse
180
    for CommaSeparatedList<T, REQUIRED, MAX>
181
{
182
2346
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
183
2346
        let loc = parser.current_source_location();
184
2346
        let mut v = Vec::<T>::with_capacity(MAX);
185
4329
        for i in 0..MAX {
186
4287
            if i != 0 {
187
1974
                optional_comma(parser);
188
            }
189

            
190
4288
            v.push(T::parse(parser)?);
191

            
192
4261
            if parser.is_exhausted() {
193
                break;
194
            }
195
        }
196

            
197
4597
        if REQUIRED > 0 && v.len() < REQUIRED {
198
2
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected more values")))
199
        } else {
200
2298
            v.shrink_to_fit();
201
2297
            Ok(CommaSeparatedList(v))
202
        }
203
2314
    }
204
}
205

            
206
/// Parses a list of identifiers from a `cssparser::Parser`
207
///
208
/// # Example
209
///
210
/// ```
211
/// # use cssparser::{ParserInput, Parser};
212
/// # use rsvg::parse_identifiers;
213
/// # fn main() -> Result<(), cssparser::BasicParseError<'static>> {
214
/// # let mut input = ParserInput::new("true");
215
/// # let mut parser = Parser::new(&mut input);
216
/// let my_boolean = parse_identifiers!(
217
///     parser,
218
///     "true" => true,
219
///     "false" => false,
220
/// )?;
221
/// # Ok(())
222
/// # }
223
/// ```
224
#[doc(hidden)]
225
#[macro_export]
226
macro_rules! parse_identifiers {
227
    ($parser:expr,
228
     $($str:expr => $val:expr,)+) => {
229
        {
230
            let loc = $parser.current_source_location();
231
            let token = $parser.next()?;
232

            
233
            match token {
234
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+
235

            
236
                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
237
            }
238
        }
239
    };
240
}
241

            
242
/// CSS Custom identifier.
243
///
244
/// CSS Values and Units 4: <https://www.w3.org/TR/css-values-4/#custom-idents>
245
1354
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
246
677
pub struct CustomIdent(pub String);
247

            
248
impl Parse for CustomIdent {
249
178
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
250
178
        let loc = parser.current_source_location();
251
178
        let token = parser.next()?;
252

            
253
177
        match token {
254
            // CSS-wide keywords and "default" are errors here
255
            // https://www.w3.org/TR/css-values-4/#css-wide-keywords
256
177
            Token::Ident(ref cow) => {
257
875
                for s in &["initial", "inherit", "unset", "default"] {
258
702
                    if cow.eq_ignore_ascii_case(s) {
259
4
                        return Err(loc.new_basic_unexpected_token_error(token.clone()).into());
260
                    }
261
                }
262

            
263
173
                Ok(CustomIdent(cow.as_ref().to_string()))
264
173
            }
265

            
266
            _ => Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
267
        }
268
178
    }
269
}
270

            
271
#[cfg(test)]
272
mod tests {
273
    use super::*;
274

            
275
    use markup5ever::{local_name, namespace_url, ns, QualName};
276

            
277
    #[test]
278
2
    fn parses_number_optional_number() {
279
1
        assert_eq!(
280
1
            NumberOptionalNumber::parse_str("1, 2").unwrap(),
281
            NumberOptionalNumber(1.0, 2.0)
282
        );
283
1
        assert_eq!(
284
1
            NumberOptionalNumber::parse_str("1 2").unwrap(),
285
            NumberOptionalNumber(1.0, 2.0)
286
        );
287
1
        assert_eq!(
288
1
            NumberOptionalNumber::parse_str("1").unwrap(),
289
            NumberOptionalNumber(1.0, 1.0)
290
        );
291

            
292
1
        assert_eq!(
293
1
            NumberOptionalNumber::parse_str("-1, -2").unwrap(),
294
            NumberOptionalNumber(-1.0, -2.0)
295
        );
296
1
        assert_eq!(
297
1
            NumberOptionalNumber::parse_str("-1 -2").unwrap(),
298
            NumberOptionalNumber(-1.0, -2.0)
299
        );
300
1
        assert_eq!(
301
1
            NumberOptionalNumber::parse_str("-1").unwrap(),
302
            NumberOptionalNumber(-1.0, -1.0)
303
        );
304
2
    }
305

            
306
    #[test]
307
2
    fn invalid_number_optional_number() {
308
1
        assert!(NumberOptionalNumber::<f64>::parse_str("").is_err());
309
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1x").is_err());
310
1
        assert!(NumberOptionalNumber::<f64>::parse_str("x1").is_err());
311
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 x").is_err());
312
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , x").is_err());
313
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , 2x").is_err());
314
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 2 x").is_err());
315
2
    }
316

            
317
    #[test]
318
2
    fn parses_integer() {
319
1
        assert_eq!(i32::parse_str("0").unwrap(), 0);
320
1
        assert_eq!(i32::parse_str("1").unwrap(), 1);
321
1
        assert_eq!(i32::parse_str("-1").unwrap(), -1);
322

            
323
1
        assert_eq!(u32::parse_str("0").unwrap(), 0);
324
1
        assert_eq!(u32::parse_str("1").unwrap(), 1);
325
2
    }
326

            
327
    #[test]
328
2
    fn invalid_integer() {
329
1
        assert!(i32::parse_str("").is_err());
330
1
        assert!(i32::parse_str("1x").is_err());
331
1
        assert!(i32::parse_str("1.5").is_err());
332

            
333
1
        assert!(u32::parse_str("").is_err());
334
1
        assert!(u32::parse_str("1x").is_err());
335
1
        assert!(u32::parse_str("1.5").is_err());
336
1
        assert!(u32::parse_str("-1").is_err());
337
2
    }
338

            
339
    #[test]
340
2
    fn parses_integer_optional_integer() {
341
1
        assert_eq!(
342
1
            NumberOptionalNumber::parse_str("1, 2").unwrap(),
343
            NumberOptionalNumber(1, 2)
344
        );
345
1
        assert_eq!(
346
1
            NumberOptionalNumber::parse_str("1 2").unwrap(),
347
            NumberOptionalNumber(1, 2)
348
        );
349
1
        assert_eq!(
350
1
            NumberOptionalNumber::parse_str("1").unwrap(),
351
            NumberOptionalNumber(1, 1)
352
        );
353

            
354
1
        assert_eq!(
355
1
            NumberOptionalNumber::parse_str("-1, -2").unwrap(),
356
            NumberOptionalNumber(-1, -2)
357
        );
358
1
        assert_eq!(
359
1
            NumberOptionalNumber::parse_str("-1 -2").unwrap(),
360
            NumberOptionalNumber(-1, -2)
361
        );
362
1
        assert_eq!(
363
1
            NumberOptionalNumber::parse_str("-1").unwrap(),
364
            NumberOptionalNumber(-1, -1)
365
        );
366
2
    }
367

            
368
    #[test]
369
2
    fn invalid_integer_optional_integer() {
370
1
        assert!(NumberOptionalNumber::<i32>::parse_str("").is_err());
371
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1x").is_err());
372
1
        assert!(NumberOptionalNumber::<i32>::parse_str("x1").is_err());
373
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 x").is_err());
374
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , x").is_err());
375
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , 2x").is_err());
376
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2 x").is_err());
377
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1.5").is_err());
378
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2.5").is_err());
379
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1, 2.5").is_err());
380
2
    }
381

            
382
    #[test]
383
2
    fn parses_comma_separated_list() {
384
2
        assert_eq!(
385
1
            CommaSeparatedList::<f64, 1, 1>::parse_str("5").unwrap(),
386
1
            CommaSeparatedList(vec![5.0])
387
        );
388

            
389
2
        assert_eq!(
390
1
            CommaSeparatedList::<f64, 4, 4>::parse_str("1 2 3 4").unwrap(),
391
1
            CommaSeparatedList(vec![1.0, 2.0, 3.0, 4.0])
392
        );
393

            
394
2
        assert_eq!(
395
1
            CommaSeparatedList::<f64, 0, 5>::parse_str("1 2 3 4 5").unwrap(),
396
1
            CommaSeparatedList(vec![1.0, 2.0, 3.0, 4.0, 5.0])
397
        );
398

            
399
2
        assert_eq!(
400
1
            CommaSeparatedList::<f64, 0, 5>::parse_str("1 2 3").unwrap(),
401
1
            CommaSeparatedList(vec![1.0, 2.0, 3.0])
402
        );
403
2
    }
404

            
405
    #[test]
406
2
    fn errors_on_invalid_comma_separated_list() {
407
        // empty
408
1
        assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("").is_err());
409
1
        assert!(CommaSeparatedList::<f64, 0, 1>::parse_str("").is_err());
410

            
411
        // garbage
412
1
        assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("foo").is_err());
413
1
        assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1foo").is_err());
414
1
        assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1 foo").is_err());
415
1
        assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1 foo 2").is_err());
416
1
        assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1,foo").is_err());
417

            
418
        // too many
419
1
        assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("1 2").is_err());
420

            
421
        // extra token
422
1
        assert!(CommaSeparatedList::<f64, 1, 1>::parse_str("1,").is_err());
423
1
        assert!(CommaSeparatedList::<f64, 0, 1>::parse_str("1,").is_err());
424

            
425
        // too few
426
1
        assert!(CommaSeparatedList::<f64, 2, 2>::parse_str("1").is_err());
427
1
        assert!(CommaSeparatedList::<f64, 3, 3>::parse_str("1 2").is_err());
428
2
    }
429

            
430
    #[test]
431
2
    fn detects_too_many_numbers_bug_1138() {
432
        // The root cause of this bug is that we didn't check for token exhaustion when
433
        // parsing attribute values in `impl<T: Parse> ParseValue<T> for QualName`.  So,
434
        // for this test, we actually invoke the parser for CommaSeparatedList via that impl.
435

            
436
1
        let attribute = QualName::new(None, ns!(svg), local_name!("matrix"));
437

            
438
        // should parse 20 numbers and error out on the 21st
439
        let r: Result<CommaSeparatedList<f64, 20, 20>, _> =
440
1
            attribute.parse("1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0,500000 0 ");
441
1
        assert!(r.is_err());
442
2
    }
443

            
444
    #[test]
445
2
    fn parses_custom_ident() {
446
2
        assert_eq!(
447
1
            CustomIdent::parse_str("hello").unwrap(),
448
1
            CustomIdent("hello".to_string())
449
        );
450
2
    }
451

            
452
    #[test]
453
2
    fn invalid_custom_ident_yields_error() {
454
1
        assert!(CustomIdent::parse_str("initial").is_err());
455
1
        assert!(CustomIdent::parse_str("inherit").is_err());
456
1
        assert!(CustomIdent::parse_str("unset").is_err());
457
1
        assert!(CustomIdent::parse_str("default").is_err());
458
1
        assert!(CustomIdent::parse_str("").is_err());
459
2
    }
460
}