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) -> Self {
30 Self { 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 core::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(Self::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 alloc::string::ToString;
103 use core::str::FromStr;
104
105 macro_rules! test_p {
106 ($name:ident, $text:expr, $result:expr) => (
107 #[test]
108 fn $name() {
109 assert_eq!(Angle::from_str($text).unwrap(), $result);
110 }
111 )
112 }
113
114 test_p!(parse_1, "1", Angle::new(1.0, AngleUnit::Degrees));
115 test_p!(parse_2, "1deg", Angle::new(1.0, AngleUnit::Degrees));
116 test_p!(parse_3, "1grad", Angle::new(1.0, AngleUnit::Gradians));
117 test_p!(parse_4, "1rad", Angle::new(1.0, AngleUnit::Radians));
118 test_p!(parse_5, "1turn", Angle::new(1.0, AngleUnit::Turns));
119
120 #[test]
121 fn err_1() {
122 let mut s = Stream::from("1q");
123 assert_eq!(s.parse_angle().unwrap(), Angle::new(1.0, AngleUnit::Degrees));
124 assert_eq!(s.parse_angle().unwrap_err().to_string(),
125 "invalid number at position 2");
126 }
127
128 #[test]
129 fn err_2() {
130 assert_eq!(Angle::from_str("1degq").unwrap_err().to_string(),
131 "unexpected data at position 5");
132 }
133}