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