svgtypes/
angle.rs
1use crate::{Error, Stream};
5
6#[derive(Clone, Copy, PartialEq, Eq, Debug)]
8#[allow(missing_docs)]
9pub enum AngleUnit {
10 Degrees,
11 Gradians,
12 Radians,
13 Turns,
14}
15
16#[derive(Clone, Copy, PartialEq, Debug)]
20#[allow(missing_docs)]
21pub struct Angle {
22 pub number: f64,
23 pub unit: AngleUnit,
24}
25
26impl Angle {
27 #[inline]
29 pub fn new(number: f64, unit: AngleUnit) -> Angle {
30 Angle { number, unit }
31 }
32
33 #[inline]
35 pub fn to_degrees(&self) -> f64 {
36 match self.unit {
37 AngleUnit::Degrees => self.number,
38 AngleUnit::Gradians => self.number * 180.0 / 200.0,
39 AngleUnit::Radians => self.number.to_degrees(),
40 AngleUnit::Turns => self.number * 360.0,
41 }
42 }
43}
44
45impl std::str::FromStr for Angle {
46 type Err = Error;
47
48 #[inline]
49 fn from_str(text: &str) -> Result<Self, Error> {
50 let mut s = Stream::from(text);
51 let l = s.parse_angle()?;
52
53 if !s.at_end() {
54 return Err(Error::UnexpectedData(s.calc_char_pos()));
55 }
56
57 Ok(Angle::new(l.number, l.unit))
58 }
59}
60
61impl Stream<'_> {
62 pub fn parse_angle(&mut self) -> Result<Angle, Error> {
70 self.skip_spaces();
71
72 let n = self.parse_number()?;
73
74 if self.at_end() {
75 return Ok(Angle::new(n, AngleUnit::Degrees));
76 }
77
78 let u = if self.starts_with(b"deg") {
79 self.advance(3);
80 AngleUnit::Degrees
81 } else if self.starts_with(b"grad") {
82 self.advance(4);
83 AngleUnit::Gradians
84 } else if self.starts_with(b"rad") {
85 self.advance(3);
86 AngleUnit::Radians
87 } else if self.starts_with(b"turn") {
88 self.advance(4);
89 AngleUnit::Turns
90 } else {
91 AngleUnit::Degrees
92 };
93
94 Ok(Angle::new(n, u))
95 }
96}
97
98#[rustfmt::skip]
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use std::str::FromStr;
103
104 macro_rules! test_p {
105 ($name:ident, $text:expr, $result:expr) => (
106 #[test]
107 fn $name() {
108 assert_eq!(Angle::from_str($text).unwrap(), $result);
109 }
110 )
111 }
112
113 test_p!(parse_1, "1", Angle::new(1.0, AngleUnit::Degrees));
114 test_p!(parse_2, "1deg", Angle::new(1.0, AngleUnit::Degrees));
115 test_p!(parse_3, "1grad", Angle::new(1.0, AngleUnit::Gradians));
116 test_p!(parse_4, "1rad", Angle::new(1.0, AngleUnit::Radians));
117 test_p!(parse_5, "1turn", Angle::new(1.0, AngleUnit::Turns));
118
119 #[test]
120 fn err_1() {
121 let mut s = Stream::from("1q");
122 assert_eq!(s.parse_angle().unwrap(), Angle::new(1.0, AngleUnit::Degrees));
123 assert_eq!(s.parse_angle().unwrap_err().to_string(),
124 "invalid number at position 2");
125 }
126
127 #[test]
128 fn err_2() {
129 assert_eq!(Angle::from_str("1degq").unwrap_err().to_string(),
130 "unexpected data at position 5");
131 }
132}