svgtypes/
points.rs

1// Copyright 2021 the SVG Types Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::Stream;
5
6/// A pull-based [`<list-of-points>`] parser.
7///
8/// Use it for the `points` attribute of the `polygon` and `polyline` elements.
9///
10/// # Errors
11///
12/// - Stops on a first invalid character. Follows the same rules as paths parser.
13///
14/// # Notes
15///
16/// - If data contains an odd number of coordinates - the last one will be ignored.
17///   As SVG spec states.
18/// - It doesn't validate that there are more than two coordinate pairs,
19///   which is required by the SVG spec.
20///
21/// # Examples
22///
23/// ```
24/// use svgtypes::PointsParser;
25///
26/// let mut p = PointsParser::from("10 20 30 40");
27/// assert_eq!(p.next(), Some((10.0, 20.0)));
28/// assert_eq!(p.next(), Some((30.0, 40.0)));
29/// assert_eq!(p.next(), None);
30/// ```
31///
32/// [`<list-of-points>`]: https://www.w3.org/TR/SVG11/shapes.html#PointsBNF
33#[derive(Clone, Copy, PartialEq, Eq, Debug)]
34pub struct PointsParser<'a>(Stream<'a>);
35
36impl<'a> From<&'a str> for PointsParser<'a> {
37    #[inline]
38    fn from(v: &'a str) -> Self {
39        PointsParser(Stream::from(v))
40    }
41}
42
43impl Iterator for PointsParser<'_> {
44    type Item = (f64, f64);
45
46    fn next(&mut self) -> Option<Self::Item> {
47        if self.0.at_end() {
48            None
49        } else {
50            let x = match self.0.parse_list_number() {
51                Ok(x) => x,
52                Err(_) => return None,
53            };
54
55            let y = match self.0.parse_list_number() {
56                Ok(y) => y,
57                Err(_) => return None,
58            };
59
60            Some((x, y))
61        }
62    }
63}
64
65#[rustfmt::skip]
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn parse_1() {
72        let mut parser = PointsParser::from("10 20 30 40");
73        assert_eq!(parser.next().unwrap(), (10.0, 20.0));
74        assert_eq!(parser.next().unwrap(), (30.0, 40.0));
75        assert!(parser.next().is_none());
76    }
77
78    #[test]
79    fn parse_2() {
80        let mut parser = PointsParser::from("10 20 30 40 50");
81        assert_eq!(parser.next().unwrap(), (10.0, 20.0));
82        assert_eq!(parser.next().unwrap(), (30.0, 40.0));
83        assert!(parser.next().is_none());
84    }
85}