1use crate::{Error, Stream};
5
6#[derive(Clone, Copy, PartialEq, Eq, Debug)]
8#[allow(missing_docs)]
9pub enum LengthUnit {
10 None,
11 Em,
12 Ex,
13 Px,
14 In,
15 Cm,
16 Mm,
17 Pt,
18 Pc,
19 Percent,
20}
21
22#[derive(Clone, Copy, PartialEq, Debug)]
26#[allow(missing_docs)]
27pub struct Length {
28 pub number: f64,
29 pub unit: LengthUnit,
30}
31
32impl Length {
33 #[inline]
35 pub fn new(number: f64, unit: LengthUnit) -> Self {
36 Self { number, unit }
37 }
38
39 #[inline]
41 pub fn new_number(number: f64) -> Self {
42 Self {
43 number,
44 unit: LengthUnit::None,
45 }
46 }
47
48 #[inline]
52 pub fn zero() -> Self {
53 Self {
54 number: 0.0,
55 unit: LengthUnit::None,
56 }
57 }
58}
59
60impl Default for Length {
61 #[inline]
62 fn default() -> Self {
63 Self::zero()
64 }
65}
66
67impl core::str::FromStr for Length {
68 type Err = Error;
69
70 #[inline]
71 fn from_str(text: &str) -> Result<Self, Error> {
72 let mut s = Stream::from(text);
73 let l = s.parse_length()?;
74
75 if !s.at_end() {
76 return Err(Error::UnexpectedData(s.calc_char_pos()));
77 }
78
79 Ok(Self::new(l.number, l.unit))
80 }
81}
82
83impl Stream<'_> {
84 pub fn parse_length(&mut self) -> Result<Length, Error> {
92 self.skip_spaces();
93
94 let n = self.parse_number()?;
95
96 if self.at_end() {
97 return Ok(Length::new(n, LengthUnit::None));
98 }
99
100 let u = if self.starts_with(b"%") {
101 LengthUnit::Percent
102 } else if self.starts_with(b"em") {
103 LengthUnit::Em
104 } else if self.starts_with(b"ex") {
105 LengthUnit::Ex
106 } else if self.starts_with(b"px") {
107 LengthUnit::Px
108 } else if self.starts_with(b"in") {
109 LengthUnit::In
110 } else if self.starts_with(b"cm") {
111 LengthUnit::Cm
112 } else if self.starts_with(b"mm") {
113 LengthUnit::Mm
114 } else if self.starts_with(b"pt") {
115 LengthUnit::Pt
116 } else if self.starts_with(b"pc") {
117 LengthUnit::Pc
118 } else {
119 LengthUnit::None
120 };
121
122 match u {
123 LengthUnit::Percent => self.advance(1),
124 LengthUnit::None => {}
125 _ => self.advance(2),
126 }
127
128 Ok(Length::new(n, u))
129 }
130
131 pub fn parse_list_length(&mut self) -> Result<Length, Error> {
133 if self.at_end() {
134 return Err(Error::UnexpectedEndOfStream);
135 }
136
137 let l = self.parse_length()?;
138 self.skip_spaces();
139 self.parse_list_separator();
140 Ok(l)
141 }
142}
143
144#[derive(Clone, Copy, PartialEq, Eq, Debug)]
160pub struct LengthListParser<'a>(Stream<'a>);
161
162impl<'a> From<&'a str> for LengthListParser<'a> {
163 #[inline]
164 fn from(v: &'a str) -> Self {
165 LengthListParser(Stream::from(v))
166 }
167}
168
169impl Iterator for LengthListParser<'_> {
170 type Item = Result<Length, Error>;
171
172 fn next(&mut self) -> Option<Self::Item> {
173 if self.0.at_end() {
174 None
175 } else {
176 let v = self.0.parse_list_length();
177 if v.is_err() {
178 self.0.jump_to_end();
179 }
180
181 Some(v)
182 }
183 }
184}
185
186#[rustfmt::skip]
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use alloc::string::ToString;
191 use core::str::FromStr;
192
193 macro_rules! test_p {
194 ($name:ident, $text:expr, $result:expr) => (
195 #[test]
196 fn $name() {
197 assert_eq!(Length::from_str($text).unwrap(), $result);
198 }
199 )
200 }
201
202 test_p!(parse_1, "1", Length::new(1.0, LengthUnit::None));
203 test_p!(parse_2, "1em", Length::new(1.0, LengthUnit::Em));
204 test_p!(parse_3, "1ex", Length::new(1.0, LengthUnit::Ex));
205 test_p!(parse_4, "1px", Length::new(1.0, LengthUnit::Px));
206 test_p!(parse_5, "1in", Length::new(1.0, LengthUnit::In));
207 test_p!(parse_6, "1cm", Length::new(1.0, LengthUnit::Cm));
208 test_p!(parse_7, "1mm", Length::new(1.0, LengthUnit::Mm));
209 test_p!(parse_8, "1pt", Length::new(1.0, LengthUnit::Pt));
210 test_p!(parse_9, "1pc", Length::new(1.0, LengthUnit::Pc));
211 test_p!(parse_10, "1%", Length::new(1.0, LengthUnit::Percent));
212 test_p!(parse_11, "1e0", Length::new(1.0, LengthUnit::None));
213 test_p!(parse_12, "1.0e0", Length::new(1.0, LengthUnit::None));
214 test_p!(parse_13, "1.0e0em", Length::new(1.0, LengthUnit::Em));
215
216 #[test]
217 fn parse_14() {
218 let mut s = Stream::from("1,");
219 assert_eq!(s.parse_length().unwrap(), Length::new(1.0, LengthUnit::None));
220 }
221
222 #[test]
223 fn parse_15() {
224 let mut s = Stream::from("1 ,");
225 assert_eq!(s.parse_length().unwrap(), Length::new(1.0, LengthUnit::None));
226 }
227
228 #[test]
229 fn parse_16() {
230 let mut s = Stream::from("1 1");
231 assert_eq!(s.parse_length().unwrap(), Length::new(1.0, LengthUnit::None));
232 }
233
234 #[test]
235 fn err_1() {
236 let mut s = Stream::from("1q");
237 assert_eq!(s.parse_length().unwrap(), Length::new(1.0, LengthUnit::None));
238 assert_eq!(s.parse_length().unwrap_err().to_string(),
239 "invalid number at position 2");
240 }
241
242 #[test]
243 fn err_2() {
244 assert_eq!(Length::from_str("1mmx").unwrap_err().to_string(),
245 "unexpected data at position 4");
246 }
247}