zerometry/
coord.rs

1use core::fmt;
2
3pub(crate) const COORD_SIZE_IN_BYTES: usize = std::mem::size_of::<f64>() * 2;
4pub(crate) const COORD_SIZE_IN_FLOATS: usize = 2;
5
6#[repr(transparent)]
7pub struct Coord {
8    data: [f64],
9}
10
11impl<'a> Coord {
12    pub fn from_bytes(data: &'a [u8]) -> &'a Self {
13        debug_assert_eq!(
14            data.len(),
15            COORD_SIZE_IN_BYTES,
16            "Bad number of bytes: `{}`, expected `{COORD_SIZE_IN_BYTES}`",
17            data.len()
18        );
19        debug_assert!(
20            data.as_ptr() as usize % std::mem::align_of::<f64>() == 0,
21            "data is not aligned"
22        );
23        unsafe { std::mem::transmute(data) }
24    }
25
26    pub fn from_slice(data: &[f64]) -> &Self {
27        debug_assert_eq!(data.len(), 2);
28        unsafe { std::mem::transmute(data) }
29    }
30
31    pub fn from_slice_mut(data: &mut [f64]) -> &mut Self {
32        debug_assert_eq!(data.len(), 2);
33        unsafe { std::mem::transmute(data) }
34    }
35
36    pub fn lng(&self) -> f64 {
37        self.data[0]
38    }
39
40    pub fn lng_mut(&mut self) -> &mut f64 {
41        &mut self.data[0]
42    }
43
44    pub fn lat(&self) -> f64 {
45        self.data[1]
46    }
47
48    pub fn lat_mut(&mut self) -> &mut f64 {
49        &mut self.data[1]
50    }
51
52    pub fn x(&self) -> f64 {
53        self.lng()
54    }
55
56    pub fn y(&self) -> f64 {
57        self.lat()
58    }
59
60    pub fn to_geo(&self) -> geo_types::Coord<f64> {
61        geo_types::Coord {
62            x: self.lng(),
63            y: self.lat(),
64        }
65    }
66}
67
68impl fmt::Debug for Coord {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.debug_struct("Coord")
71            .field("x", &self.lng())
72            .field("y", &self.lat())
73            .finish()
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use bytemuck::cast_slice;
80
81    use super::*;
82
83    #[test]
84    fn test_basic_create_coord_from_bytes() {
85        let data = [1.0, 2.0];
86        let coord = Coord::from_bytes(cast_slice(&data));
87        assert_eq!(coord.lng(), 1.0);
88        assert_eq!(coord.lat(), 2.0);
89    }
90
91    #[test]
92    #[should_panic]
93    fn test_coord_panic_on_too_short_bytes() {
94        let data = [1.0];
95        Coord::from_bytes(cast_slice(&data));
96    }
97    #[test]
98    #[should_panic]
99    fn test_coord_panic_on_too_long_bytes() {
100        let data = [1.0, 2.0, 3.0];
101        Coord::from_bytes(cast_slice(&data));
102    }
103
104    #[test]
105    #[should_panic]
106    fn test_coord_panic_on_unaligned_bytes() {
107        let data = [1.0, 2.0, 3.0];
108        Coord::from_bytes(&cast_slice(&data)[1..]);
109    }
110
111    #[test]
112    #[should_panic]
113    fn test_coord_panic_on_too_short_slice() {
114        let data = [1.0];
115        Coord::from_bytes(cast_slice(&data));
116    }
117
118    #[test]
119    #[should_panic]
120    fn test_coord_panic_on_too_long_slice() {
121        let data = [1.0, 2.0, 3.0];
122        Coord::from_bytes(cast_slice(&data));
123    }
124
125    #[test]
126    fn debug_impl_support_precision_settings() {
127        let data = [1.123456789, 2.987654321];
128        let coord = Coord::from_bytes(cast_slice(&data));
129        insta::assert_snapshot!(format!("{:.2?}", coord), @"Coord { x: 1.12, y: 2.99 }");
130    }
131}