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}