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