1use crate::{Decode, Encode, EncodedSize, Result};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct Angle(pub u8);
18
19impl Angle {
20 pub fn from_degrees(degrees: f32) -> Self {
26 Self((degrees / 360.0 * 256.0) as u8)
27 }
28
29 pub fn to_degrees(self) -> f32 {
35 self.0 as f32 / 256.0 * 360.0
36 }
37}
38
39impl Encode for Angle {
44 fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
46 self.0.encode(buf)
47 }
48}
49
50impl Decode for Angle {
55 fn decode(buf: &mut &[u8]) -> Result<Self> {
59 Ok(Self(u8::decode(buf)?))
60 }
61}
62
63impl EncodedSize for Angle {
65 fn encoded_size(&self) -> usize {
66 1
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 fn roundtrip(value: u8) {
75 let angle = Angle(value);
76 let mut buf = Vec::with_capacity(angle.encoded_size());
77 angle.encode(&mut buf).unwrap();
78 assert_eq!(buf.len(), 1);
79
80 let mut cursor = buf.as_slice();
81 let decoded = Angle::decode(&mut cursor).unwrap();
82 assert!(cursor.is_empty());
83 assert_eq!(decoded, angle);
84 }
85
86 #[test]
87 fn zero() {
88 roundtrip(0);
89 }
90
91 #[test]
92 fn max() {
93 roundtrip(255);
94 }
95
96 #[test]
97 fn midpoint() {
98 roundtrip(128);
99 }
100
101 #[test]
102 fn from_degrees_zero() {
103 let angle = Angle::from_degrees(0.0);
104 assert_eq!(angle.0, 0);
105 }
106
107 #[test]
108 fn from_degrees_90() {
109 let angle = Angle::from_degrees(90.0);
110 assert_eq!(angle.0, 64);
111 }
112
113 #[test]
114 fn from_degrees_180() {
115 let angle = Angle::from_degrees(180.0);
116 assert_eq!(angle.0, 128);
117 }
118
119 #[test]
120 fn from_degrees_270() {
121 let angle = Angle::from_degrees(270.0);
122 assert_eq!(angle.0, 192);
123 }
124
125 #[test]
126 fn from_degrees_360_saturates() {
127 let angle = Angle::from_degrees(360.0);
130 assert_eq!(angle.0, 255);
131 }
132
133 #[test]
134 fn to_degrees_zero() {
135 assert!((Angle(0).to_degrees() - 0.0).abs() < f32::EPSILON);
136 }
137
138 #[test]
139 fn to_degrees_90() {
140 assert!((Angle(64).to_degrees() - 90.0).abs() < f32::EPSILON);
141 }
142
143 #[test]
144 fn to_degrees_180() {
145 assert!((Angle(128).to_degrees() - 180.0).abs() < f32::EPSILON);
146 }
147
148 #[test]
149 fn to_degrees_270() {
150 assert!((Angle(192).to_degrees() - 270.0).abs() < f32::EPSILON);
151 }
152
153 #[test]
154 fn to_degrees_255() {
155 let degrees = Angle(255).to_degrees();
157 assert!((degrees - 358.59375).abs() < 0.001);
158 }
159
160 #[test]
161 fn encoded_size_is_1() {
162 assert_eq!(Angle(0).encoded_size(), 1);
163 assert_eq!(Angle(255).encoded_size(), 1);
164 }
165
166 #[test]
167 fn underflow() {
168 let mut cursor: &[u8] = &[];
169 assert!(Angle::decode(&mut cursor).is_err());
170 }
171
172 mod proptests {
173 use super::*;
174 use proptest::prelude::*;
175
176 proptest! {
177 #[test]
178 fn angle_roundtrip(v: u8) {
179 roundtrip(v);
180 }
181 }
182 }
183}