Skip to main content

svgtypes/
paint_order.rs

1// Copyright 2022 the SVG Types Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::stream::Stream;
5use alloc::vec;
6use alloc::vec::Vec;
7
8/// [`paint-order`] property variants.
9///
10/// [`paint-order`]: https://www.w3.org/TR/SVG2/painting.html#PaintOrder
11#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12#[allow(missing_docs)]
13pub enum PaintOrderKind {
14    Fill,
15    Stroke,
16    Markers,
17}
18
19/// Representation of the [`paint-order`] property.
20///
21/// [`paint-order`]: https://www.w3.org/TR/SVG2/painting.html#PaintOrder
22#[derive(Clone, Copy, PartialEq, Eq, Debug)]
23pub struct PaintOrder {
24    /// The order.
25    ///
26    /// Guarantee to not have duplicates.
27    pub order: [PaintOrderKind; 3],
28}
29
30impl Default for PaintOrder {
31    #[inline]
32    fn default() -> Self {
33        Self {
34            order: [
35                PaintOrderKind::Fill,
36                PaintOrderKind::Stroke,
37                PaintOrderKind::Markers,
38            ],
39        }
40    }
41}
42
43impl From<[PaintOrderKind; 3]> for PaintOrder {
44    #[inline]
45    fn from(order: [PaintOrderKind; 3]) -> Self {
46        Self { order }
47    }
48}
49
50impl core::str::FromStr for PaintOrder {
51    type Err = ();
52
53    /// Parses `PaintOrder` from a string.
54    ///
55    /// Never returns an error and fallbacks to the default value instead.
56    fn from_str(text: &str) -> Result<Self, Self::Err> {
57        let mut order = Vec::new();
58
59        let mut left = vec![
60            PaintOrderKind::Fill,
61            PaintOrderKind::Stroke,
62            PaintOrderKind::Markers,
63        ];
64
65        let mut s = Stream::from(text);
66        while !s.at_end() && order.len() < 3 {
67            s.skip_spaces();
68            let name = s.consume_ascii_ident();
69            s.skip_spaces();
70            let name = match name {
71                // `normal` is the special value that should short-circuit.
72                "normal" => return Ok(Self::default()),
73                "fill" => PaintOrderKind::Fill,
74                "stroke" => PaintOrderKind::Stroke,
75                "markers" => PaintOrderKind::Markers,
76                _ => return Ok(Self::default()),
77            };
78
79            if let Some(index) = left.iter().position(|v| *v == name) {
80                left.remove(index);
81            }
82
83            order.push(name);
84        }
85
86        s.skip_spaces();
87        if !s.at_end() {
88            // Any trailing data is an error.
89            return Ok(Self::default());
90        }
91
92        if order.is_empty() {
93            return Ok(Self::default());
94        }
95
96        // Any missing values should be added in the original order.
97        while order.len() < 3 && !left.is_empty() {
98            order.push(left.remove(0));
99        }
100
101        // Any duplicates is an error.
102        if order[0] == order[1] || order[0] == order[2] || order[1] == order[2] {
103            // Any trailing data is an error.
104            return Ok(Self::default());
105        }
106
107        Ok(Self {
108            order: [order[0], order[1], order[2]],
109        })
110    }
111}
112
113#[rustfmt::skip]
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use core::str::FromStr;
118
119    #[test]
120    fn parse_1() {
121        assert_eq!(PaintOrder::from_str("normal").unwrap(), PaintOrder::default());
122    }
123
124    #[test]
125    fn parse_2() {
126        assert_eq!(PaintOrder::from_str("qwe").unwrap(), PaintOrder::default());
127    }
128
129    #[test]
130    fn parse_3() {
131        assert_eq!(PaintOrder::from_str("").unwrap(), PaintOrder::default());
132    }
133
134    #[test]
135    fn parse_4() {
136        assert_eq!(PaintOrder::from_str("stroke qwe").unwrap(), PaintOrder::default());
137    }
138
139    #[test]
140    fn parse_5() {
141        assert_eq!(PaintOrder::from_str("stroke stroke").unwrap(), PaintOrder::default());
142    }
143
144    #[test]
145    fn parse_6() {
146        assert_eq!(PaintOrder::from_str("stroke").unwrap(), PaintOrder::from([
147            PaintOrderKind::Stroke, PaintOrderKind::Fill, PaintOrderKind::Markers
148        ]));
149    }
150
151    #[test]
152    fn parse_7() {
153        assert_eq!(PaintOrder::from_str("stroke markers").unwrap(), PaintOrder::from([
154            PaintOrderKind::Stroke, PaintOrderKind::Markers, PaintOrderKind::Fill
155        ]));
156    }
157
158    #[test]
159    fn parse_8() {
160        assert_eq!(PaintOrder::from_str("stroke markers fill").unwrap(), PaintOrder::from([
161            PaintOrderKind::Stroke, PaintOrderKind::Markers, PaintOrderKind::Fill
162        ]));
163    }
164
165    #[test]
166    fn parse_9() {
167        assert_eq!(PaintOrder::from_str("markers").unwrap(), PaintOrder::from([
168            PaintOrderKind::Markers, PaintOrderKind::Fill, PaintOrderKind::Stroke
169        ]));
170    }
171
172    #[test]
173    fn parse_10() {
174        assert_eq!(PaintOrder::from_str("  stroke\n").unwrap(), PaintOrder::from([
175            PaintOrderKind::Stroke, PaintOrderKind::Fill, PaintOrderKind::Markers
176        ]));
177    }
178
179    #[test]
180    fn parse_11() {
181        assert_eq!(PaintOrder::from_str("stroke stroke stroke stroke").unwrap(), PaintOrder::default());
182    }
183}