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
18070621
    fn parse_str(s: &str) -> Result<Self, ParseError<'_>> {
21
18070691
        let mut input = ParserInput::new(s);
22
18070691
        let mut parser = Parser::new(&mut input);
23

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

            
27
17980788
        Ok(res)
28
17980980
    }
29
}
30

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

            
36
/// Parses an `f32` and ensures that it is not an infinity or NaN.
37
16863245
pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
38
16863245
    if n.is_finite() {
39
16863244
        Ok(n)
40
    } else {
41
1
        Err(ValueErrorKind::Value("expected finite number".to_string()))
42
    }
43
16863245
}
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
7570
    fn parse(&self, value: &str) -> Result<T, ElementError> {
52
7582
        let mut input = ParserInput::new(value);
53
7582
        let mut parser = Parser::new(&mut input);
54

            
55
7576
        T::parse(&mut parser).attribute(self.clone())
56
7568
    }
57
}
58

            
59
impl<T: Parse> Parse for Option<T> {
60
1955
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
61
1955
        T::parse(parser).map(Some)
62
1955
    }
63
}
64

            
65
impl Parse for f64 {
66
18236
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
67
18236
        let loc = parser.current_source_location();
68
18236
        let n = parser.expect_number()?;
69
17600
        if n.is_finite() {
70
17598
            Ok(f64::from(n))
71
        } else {
72
2
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected finite number")))
73
        }
74
18236
    }
75
}
76

            
77
/// Non-Negative number
78
90
#[derive(Debug, Copy, Clone, PartialEq)]
79
pub struct NonNegative(pub f64);
80

            
81
impl Parse for NonNegative {
82
69
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
83
69
        let loc = parser.current_source_location();
84
69
        let n = Parse::parse(parser)?;
85
69
        if n >= 0.0 {
86
69
            Ok(NonNegative(n))
87
        } else {
88
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected non negative number")))
89
        }
90
69
    }
91
}
92

            
93
/// CSS number-optional-number
94
///
95
/// SVG1.1: <https://www.w3.org/TR/SVG11/types.html#DataTypeNumberOptionalNumber>
96
121
#[derive(Debug, Copy, Clone, PartialEq)]
97
109
pub struct NumberOptionalNumber<T: Parse>(pub T, pub T);
98

            
99
impl<T: Parse + Copy> Parse for NumberOptionalNumber<T> {
100
111
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
101
111
        let x = Parse::parse(parser)?;
102

            
103
130
        if !parser.is_exhausted() {
104
34
            optional_comma(parser);
105
34
            let y = Parse::parse(parser)?;
106
26
            Ok(NumberOptionalNumber(x, y))
107
        } else {
108
70
            Ok(NumberOptionalNumber(x, x))
109
        }
110
111
    }
111
}
112

            
113
/// CSS number-percentage
114
///
115
/// CSS Values and Units 3: <https://www.w3.org/TR/css3-values/#typedef-number-percentage>
116
#[derive(Debug, Copy, Clone, PartialEq)]
117
pub struct NumberOrPercentage {
118
    pub value: f64,
119
}
120

            
121
impl Parse for NumberOrPercentage {
122
35
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
123
35
        let loc = parser.current_source_location();
124

            
125
63
        let value = match parser.next()? {
126
12
            Token::Number { value, .. } => Ok(*value),
127
9
            Token::Percentage { unit_value, .. } => Ok(*unit_value),
128
7
            tok => Err(loc.new_unexpected_token_error(tok.clone())),
129
7
        }?;
130

            
131
21
        let v = finite_f32(value).map_err(|e| parser.new_custom_error(e))?;
132
21
        Ok(NumberOrPercentage {
133
21
            value: f64::from(v),
134
        })
135
35
    }
136
}
137

            
138
impl Parse for i32 {
139
    /// CSS integer
140
    ///
141
    /// SVG1.1: <https://www.w3.org/TR/SVG11/types.html#DataTypeInteger>
142
40
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
143
40
        Ok(parser.expect_integer()?)
144
40
    }
145
}
146

            
147
impl Parse for u32 {
148
33
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
149
33
        let loc = parser.current_source_location();
150
33
        let n = parser.expect_integer()?;
151
30
        if n >= 0 {
152
29
            Ok(n as u32)
153
        } else {
154
1
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected unsigned number")))
155
        }
156
33
    }
157
}
158

            
159
/// Number lists with bounds for the required and maximum number of items.
160
72
#[derive(Clone, Debug, PartialEq)]
161
36
pub struct NumberList<const REQUIRED: usize, const MAX: usize>(pub Vec<f64>);
162

            
163
impl<const REQUIRED: usize, const MAX: usize> Parse for NumberList<REQUIRED, MAX> {
164
589
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
165
589
        let loc = parser.current_source_location();
166
589
        let mut v = Vec::<f64>::with_capacity(MAX);
167
2516
        for i in 0..MAX {
168
2498
            if i != 0 {
169
1924
                optional_comma(parser);
170
            }
171

            
172
2498
            v.push(f64::parse(parser)?);
173

            
174
2481
            if parser.is_exhausted() {
175
                break;
176
            }
177
        }
178

            
179
564
        if REQUIRED > 0 && v.len() < REQUIRED {
180
2
            Err(loc.new_custom_error(ValueErrorKind::value_error("expected more numbers")))
181
        } else {
182
559
            Ok(NumberList(v))
183
        }
184
575
    }
185
}
186

            
187
/// Parses a list of identifiers from a `cssparser::Parser`
188
///
189
/// # Example
190
///
191
/// ```
192
/// # use cssparser::{ParserInput, Parser};
193
/// # use rsvg::parse_identifiers;
194
/// # fn main() -> Result<(), cssparser::BasicParseError<'static>> {
195
/// # let mut input = ParserInput::new("true");
196
/// # let mut parser = Parser::new(&mut input);
197
/// let my_boolean = parse_identifiers!(
198
///     parser,
199
///     "true" => true,
200
///     "false" => false,
201
/// )?;
202
/// # Ok(())
203
/// # }
204
/// ```
205
#[doc(hidden)]
206
#[macro_export]
207
macro_rules! parse_identifiers {
208
    ($parser:expr,
209
     $($str:expr => $val:expr,)+) => {
210
        {
211
            let loc = $parser.current_source_location();
212
            let token = $parser.next()?;
213

            
214
            match token {
215
                $(cssparser::Token::Ident(ref cow) if cow.eq_ignore_ascii_case($str) => Ok($val),)+
216

            
217
                _ => Err(loc.new_basic_unexpected_token_error(token.clone()))
218
            }
219
        }
220
    };
221
}
222

            
223
/// CSS Custom identifier.
224
///
225
/// CSS Values and Units 4: <https://www.w3.org/TR/css-values-4/#custom-idents>
226
1350
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
227
675
pub struct CustomIdent(pub String);
228

            
229
impl Parse for CustomIdent {
230
178
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
231
178
        let loc = parser.current_source_location();
232
178
        let token = parser.next()?;
233

            
234
177
        match token {
235
            // CSS-wide keywords and "default" are errors here
236
            // https://www.w3.org/TR/css-values-4/#css-wide-keywords
237
177
            Token::Ident(ref cow) => {
238
875
                for s in &["initial", "inherit", "unset", "default"] {
239
702
                    if cow.eq_ignore_ascii_case(s) {
240
4
                        return Err(loc.new_basic_unexpected_token_error(token.clone()).into());
241
                    }
242
                }
243

            
244
173
                Ok(CustomIdent(cow.as_ref().to_string()))
245
173
            }
246

            
247
            _ => Err(loc.new_basic_unexpected_token_error(token.clone()).into()),
248
        }
249
178
    }
250
}
251

            
252
#[cfg(test)]
253
mod tests {
254
    use super::*;
255

            
256
    #[test]
257
2
    fn parses_number_optional_number() {
258
1
        assert_eq!(
259
1
            NumberOptionalNumber::parse_str("1, 2").unwrap(),
260
            NumberOptionalNumber(1.0, 2.0)
261
        );
262
1
        assert_eq!(
263
1
            NumberOptionalNumber::parse_str("1 2").unwrap(),
264
            NumberOptionalNumber(1.0, 2.0)
265
        );
266
1
        assert_eq!(
267
1
            NumberOptionalNumber::parse_str("1").unwrap(),
268
            NumberOptionalNumber(1.0, 1.0)
269
        );
270

            
271
1
        assert_eq!(
272
1
            NumberOptionalNumber::parse_str("-1, -2").unwrap(),
273
            NumberOptionalNumber(-1.0, -2.0)
274
        );
275
1
        assert_eq!(
276
1
            NumberOptionalNumber::parse_str("-1 -2").unwrap(),
277
            NumberOptionalNumber(-1.0, -2.0)
278
        );
279
1
        assert_eq!(
280
1
            NumberOptionalNumber::parse_str("-1").unwrap(),
281
            NumberOptionalNumber(-1.0, -1.0)
282
        );
283
2
    }
284

            
285
    #[test]
286
2
    fn invalid_number_optional_number() {
287
1
        assert!(NumberOptionalNumber::<f64>::parse_str("").is_err());
288
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1x").is_err());
289
1
        assert!(NumberOptionalNumber::<f64>::parse_str("x1").is_err());
290
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 x").is_err());
291
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , x").is_err());
292
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 , 2x").is_err());
293
1
        assert!(NumberOptionalNumber::<f64>::parse_str("1 2 x").is_err());
294
2
    }
295

            
296
    #[test]
297
2
    fn parses_integer() {
298
1
        assert_eq!(i32::parse_str("0").unwrap(), 0);
299
1
        assert_eq!(i32::parse_str("1").unwrap(), 1);
300
1
        assert_eq!(i32::parse_str("-1").unwrap(), -1);
301

            
302
1
        assert_eq!(u32::parse_str("0").unwrap(), 0);
303
1
        assert_eq!(u32::parse_str("1").unwrap(), 1);
304
2
    }
305

            
306
    #[test]
307
2
    fn invalid_integer() {
308
1
        assert!(i32::parse_str("").is_err());
309
1
        assert!(i32::parse_str("1x").is_err());
310
1
        assert!(i32::parse_str("1.5").is_err());
311

            
312
1
        assert!(u32::parse_str("").is_err());
313
1
        assert!(u32::parse_str("1x").is_err());
314
1
        assert!(u32::parse_str("1.5").is_err());
315
1
        assert!(u32::parse_str("-1").is_err());
316
2
    }
317

            
318
    #[test]
319
2
    fn parses_integer_optional_integer() {
320
1
        assert_eq!(
321
1
            NumberOptionalNumber::parse_str("1, 2").unwrap(),
322
            NumberOptionalNumber(1, 2)
323
        );
324
1
        assert_eq!(
325
1
            NumberOptionalNumber::parse_str("1 2").unwrap(),
326
            NumberOptionalNumber(1, 2)
327
        );
328
1
        assert_eq!(
329
1
            NumberOptionalNumber::parse_str("1").unwrap(),
330
            NumberOptionalNumber(1, 1)
331
        );
332

            
333
1
        assert_eq!(
334
1
            NumberOptionalNumber::parse_str("-1, -2").unwrap(),
335
            NumberOptionalNumber(-1, -2)
336
        );
337
1
        assert_eq!(
338
1
            NumberOptionalNumber::parse_str("-1 -2").unwrap(),
339
            NumberOptionalNumber(-1, -2)
340
        );
341
1
        assert_eq!(
342
1
            NumberOptionalNumber::parse_str("-1").unwrap(),
343
            NumberOptionalNumber(-1, -1)
344
        );
345
2
    }
346

            
347
    #[test]
348
2
    fn invalid_integer_optional_integer() {
349
1
        assert!(NumberOptionalNumber::<i32>::parse_str("").is_err());
350
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1x").is_err());
351
1
        assert!(NumberOptionalNumber::<i32>::parse_str("x1").is_err());
352
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 x").is_err());
353
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , x").is_err());
354
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 , 2x").is_err());
355
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2 x").is_err());
356
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1.5").is_err());
357
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1 2.5").is_err());
358
1
        assert!(NumberOptionalNumber::<i32>::parse_str("1, 2.5").is_err());
359
2
    }
360

            
361
    #[test]
362
2
    fn parses_number_list() {
363
2
        assert_eq!(
364
1
            NumberList::<1, 1>::parse_str("5").unwrap(),
365
1
            NumberList(vec![5.0])
366
        );
367

            
368
2
        assert_eq!(
369
1
            NumberList::<4, 4>::parse_str("1 2 3 4").unwrap(),
370
1
            NumberList(vec![1.0, 2.0, 3.0, 4.0])
371
        );
372

            
373
2
        assert_eq!(
374
1
            NumberList::<0, 5>::parse_str("1 2 3 4 5").unwrap(),
375
1
            NumberList(vec![1.0, 2.0, 3.0, 4.0, 5.0])
376
        );
377

            
378
2
        assert_eq!(
379
1
            NumberList::<0, 5>::parse_str("1 2 3").unwrap(),
380
1
            NumberList(vec![1.0, 2.0, 3.0])
381
        );
382
2
    }
383

            
384
    #[test]
385
2
    fn errors_on_invalid_number_list() {
386
        // empty
387
1
        assert!(NumberList::<1, 1>::parse_str("").is_err());
388
1
        assert!(NumberList::<0, 1>::parse_str("").is_err());
389

            
390
        // garbage
391
1
        assert!(NumberList::<1, 1>::parse_str("foo").is_err());
392
1
        assert!(NumberList::<2, 2>::parse_str("1foo").is_err());
393
1
        assert!(NumberList::<2, 2>::parse_str("1 foo").is_err());
394
1
        assert!(NumberList::<2, 2>::parse_str("1 foo 2").is_err());
395
1
        assert!(NumberList::<2, 2>::parse_str("1,foo").is_err());
396

            
397
        // too many
398
1
        assert!(NumberList::<1, 1>::parse_str("1 2").is_err());
399

            
400
        // extra token
401
1
        assert!(NumberList::<1, 1>::parse_str("1,").is_err());
402
1
        assert!(NumberList::<0, 1>::parse_str("1,").is_err());
403

            
404
        // too few
405
1
        assert!(NumberList::<2, 2>::parse_str("1").is_err());
406
1
        assert!(NumberList::<3, 3>::parse_str("1 2").is_err());
407
2
    }
408

            
409
    #[test]
410
2
    fn parses_custom_ident() {
411
2
        assert_eq!(
412
1
            CustomIdent::parse_str("hello").unwrap(),
413
1
            CustomIdent("hello".to_string())
414
        );
415
2
    }
416

            
417
    #[test]
418
2
    fn invalid_custom_ident_yields_error() {
419
1
        assert!(CustomIdent::parse_str("initial").is_err());
420
1
        assert!(CustomIdent::parse_str("inherit").is_err());
421
1
        assert!(CustomIdent::parse_str("unset").is_err());
422
1
        assert!(CustomIdent::parse_str("default").is_err());
423
1
        assert!(CustomIdent::parse_str("").is_err());
424
2
    }
425
}