css_colors/
angle.rs

1use std::fmt;
2use std::ops;
3
4/// Construct an angle from degrees. Angles outside of the 0-359° range will be
5/// normalized accordingly.
6///
7/// # Example
8/// ```
9/// use css_colors::{deg};
10///
11/// assert_eq!(deg(0).to_string(), "0deg");
12/// assert_eq!(deg(90).to_string(), "90deg");
13/// assert_eq!(deg(540).to_string(), "180deg");
14/// assert_eq!(deg(-90).to_string(), "270deg");
15/// ```
16pub fn deg(mut degrees: i32) -> Angle {
17    while degrees < 0 {
18        degrees += 360;
19    }
20
21    while degrees >= 360 {
22        degrees -= 360;
23    }
24
25    Angle::new(degrees as u16)
26}
27
28#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
29/// A struct that represents the number of degrees in a circle.
30/// Legal values range from `0-359`. Anything else is unused.
31pub struct Angle {
32    degrees: u16,
33}
34
35impl Angle {
36    pub fn new(degrees: u16) -> Self {
37        assert!(degrees < 360, "invalid angle");
38
39        Angle { degrees }
40    }
41
42    pub fn degrees(self) -> u16 {
43        self.degrees
44    }
45}
46
47impl fmt::Display for Angle {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        write!(f, "{}deg", self.degrees)
50    }
51}
52
53impl ops::Neg for Angle {
54    type Output = Angle;
55
56    fn neg(self) -> Angle {
57        Angle {
58            degrees: (360 - self.degrees) % 360,
59        }
60    }
61}
62
63impl ops::Add for Angle {
64    type Output = Angle;
65
66    fn add(self, other: Angle) -> Angle {
67        let temp: u32 = self.degrees as u32 + other.degrees as u32;
68        let degrees: u16 = (temp % 360) as u16;
69
70        Angle { degrees }
71    }
72}
73
74impl ops::Sub for Angle {
75    type Output = Angle;
76
77    fn sub(self, other: Angle) -> Angle {
78        self + (-other)
79    }
80}
81
82impl ops::Mul for Angle {
83    type Output = Angle;
84
85    fn mul(self, other: Angle) -> Angle {
86        let temp: u32 = self.degrees as u32 * other.degrees as u32;
87        let degrees: u16 = (temp % 360) as u16;
88
89        Angle { degrees }
90    }
91}
92
93impl ops::Div for Angle {
94    type Output = Angle;
95
96    fn div(self, other: Angle) -> Angle {
97        if other.degrees == 0 {
98            panic!("Cannot divide by zero-valued `Angle`!");
99        }
100
101        let temp: u32 = self.degrees as u32 / other.degrees as u32;
102        let degrees: u16 = (temp % 360) as u16;
103
104        Angle { degrees }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use Angle;
111
112    #[test]
113    fn can_have_degrees() {
114        assert_eq!(Angle::new(30).degrees(), 30);
115        assert_eq!(Angle::new(47).degrees(), 47);
116    }
117
118    #[test]
119    fn can_display_angles() {
120        assert_eq!("30deg", format!("{}", Angle::new(30)));
121        assert_eq!("30deg", Angle::new(30).to_string());
122    }
123
124    #[test]
125    fn can_eq_angles() {
126        assert_eq!(Angle::new(30), Angle::new(30));
127        assert_ne!(Angle::new(30), Angle::new(47));
128    }
129
130    #[test]
131    fn can_ord_angles() {
132        assert_eq!(Angle::new(30) < Angle::new(47), true);
133        assert_eq!(Angle::new(47) < Angle::new(30), false);
134        assert_eq!(Angle::new(30) < Angle::new(30), false);
135
136        assert_eq!(Angle::new(30) <= Angle::new(47), true);
137        assert_eq!(Angle::new(47) <= Angle::new(30), false);
138        assert_eq!(Angle::new(30) <= Angle::new(30), true);
139
140        assert_eq!(Angle::new(30) > Angle::new(47), false);
141        assert_eq!(Angle::new(47) > Angle::new(30), true);
142        assert_eq!(Angle::new(30) > Angle::new(30), false);
143
144        assert_eq!(Angle::new(30) >= Angle::new(47), false);
145        assert_eq!(Angle::new(47) >= Angle::new(30), true);
146        assert_eq!(Angle::new(30) >= Angle::new(30), true);
147    }
148
149    #[test]
150    fn can_add_angles() {
151        assert_eq!(Angle::new(30) + Angle::new(47), Angle::new(77));
152        assert_eq!(Angle::new(47) + Angle::new(30), Angle::new(77));
153        assert_eq!(Angle::new(359) + Angle::new(1), Angle::new(0));
154        assert_eq!(
155            Angle::new(359) + Angle::new(359) + Angle::new(359),
156            Angle::new(357)
157        );
158    }
159
160    #[test]
161    fn can_sub_angles() {
162        assert_eq!(Angle::new(30) - Angle::new(47), Angle::new(343));
163        assert_eq!(Angle::new(47) - Angle::new(30), Angle::new(17));
164        assert_eq!(Angle::new(0) - Angle::new(1), Angle::new(359));
165        assert_eq!(
166            Angle::new(0) - Angle::new(359) - Angle::new(359) - Angle::new(359),
167            Angle::new(3)
168        );
169    }
170
171    #[test]
172    fn test_mul_angles() {
173        assert_eq!(Angle::new(30) * Angle::new(0), Angle::new(0));
174        assert_eq!(Angle::new(30) * Angle::new(1), Angle::new(30));
175        assert_eq!(Angle::new(30) * Angle::new(2), Angle::new(60));
176
177        assert_eq!(Angle::new(30) * Angle::new(12), Angle::new(0));
178        assert_eq!(Angle::new(30) * Angle::new(13), Angle::new(30));
179        assert_eq!(Angle::new(30) * Angle::new(100), Angle::new(120));
180
181        assert_eq!(Angle::new(47) * Angle::new(0), Angle::new(0));
182        assert_eq!(Angle::new(47) * Angle::new(1), Angle::new(47));
183        assert_eq!(Angle::new(47) * Angle::new(2), Angle::new(94));
184
185        assert_eq!(Angle::new(47) * Angle::new(8), Angle::new(16));
186        assert_eq!(Angle::new(47) * Angle::new(100), Angle::new(20));
187    }
188
189    #[test]
190    fn test_divide_angles() {
191        assert_eq!(Angle::new(30) / Angle::new(1), Angle::new(30));
192        assert_eq!(Angle::new(30) / Angle::new(2), Angle::new(15));
193
194        assert_eq!(Angle::new(180) / Angle::new(12), Angle::new(15));
195        assert_eq!(Angle::new(180) / Angle::new(2), Angle::new(90));
196        assert_eq!(Angle::new(180) / Angle::new(5), Angle::new(36));
197
198        assert_eq!(Angle::new(47) / Angle::new(2), Angle::new(23));
199    }
200}