use crate::{Decode, Encode, EncodedSize, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Angle(pub u8);
impl Angle {
pub fn from_degrees(degrees: f32) -> Self {
Self((degrees / 360.0 * 256.0) as u8)
}
pub fn to_degrees(self) -> f32 {
self.0 as f32 / 256.0 * 360.0
}
}
impl Encode for Angle {
fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
self.0.encode(buf)
}
}
impl Decode for Angle {
fn decode(buf: &mut &[u8]) -> Result<Self> {
Ok(Self(u8::decode(buf)?))
}
}
impl EncodedSize for Angle {
fn encoded_size(&self) -> usize {
1
}
}
#[cfg(test)]
mod tests {
use super::*;
fn roundtrip(value: u8) {
let angle = Angle(value);
let mut buf = Vec::with_capacity(angle.encoded_size());
angle.encode(&mut buf).unwrap();
assert_eq!(buf.len(), 1);
let mut cursor = buf.as_slice();
let decoded = Angle::decode(&mut cursor).unwrap();
assert!(cursor.is_empty());
assert_eq!(decoded, angle);
}
#[test]
fn zero() {
roundtrip(0);
}
#[test]
fn max() {
roundtrip(255);
}
#[test]
fn midpoint() {
roundtrip(128);
}
#[test]
fn from_degrees_zero() {
let angle = Angle::from_degrees(0.0);
assert_eq!(angle.0, 0);
}
#[test]
fn from_degrees_90() {
let angle = Angle::from_degrees(90.0);
assert_eq!(angle.0, 64);
}
#[test]
fn from_degrees_180() {
let angle = Angle::from_degrees(180.0);
assert_eq!(angle.0, 128);
}
#[test]
fn from_degrees_270() {
let angle = Angle::from_degrees(270.0);
assert_eq!(angle.0, 192);
}
#[test]
fn from_degrees_360_saturates() {
let angle = Angle::from_degrees(360.0);
assert_eq!(angle.0, 255);
}
#[test]
fn to_degrees_zero() {
assert!((Angle(0).to_degrees() - 0.0).abs() < f32::EPSILON);
}
#[test]
fn to_degrees_90() {
assert!((Angle(64).to_degrees() - 90.0).abs() < f32::EPSILON);
}
#[test]
fn to_degrees_180() {
assert!((Angle(128).to_degrees() - 180.0).abs() < f32::EPSILON);
}
#[test]
fn to_degrees_270() {
assert!((Angle(192).to_degrees() - 270.0).abs() < f32::EPSILON);
}
#[test]
fn to_degrees_255() {
let degrees = Angle(255).to_degrees();
assert!((degrees - 358.59375).abs() < 0.001);
}
#[test]
fn encoded_size_is_1() {
assert_eq!(Angle(0).encoded_size(), 1);
assert_eq!(Angle(255).encoded_size(), 1);
}
#[test]
fn underflow() {
let mut cursor: &[u8] = &[];
assert!(Angle::decode(&mut cursor).is_err());
}
mod proptests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn angle_roundtrip(v: u8) {
roundtrip(v);
}
}
}
}