Skip to main content

dtn7_plus/location/
loc.rs

1use core::convert::TryFrom;
2use core::fmt;
3use derive_try_from_primitive::TryFromPrimitive;
4use serde::de::{SeqAccess, Visitor};
5use serde::ser::{SerializeSeq, Serializer};
6use serde::{de, Deserialize, Deserializer, Serialize};
7
8#[derive(Debug, Clone, PartialEq, TryFromPrimitive)]
9#[repr(u8)]
10enum LocationType {
11    LatLon = 1,
12    Human = 2,
13    WFW = 3,
14    XY = 4,
15}
16
17/// Represents an location in various addressing schemes.
18///
19#[derive(Debug, Clone, PartialEq)]
20pub enum Location {
21    /// GPS coordinates
22    LatLon((f32, f32)),
23    /// Human-readable address
24    Human(String),
25    /// 3 word code geocode: https://3geonames.org/
26    WFW(String),
27    /// XY coordinates
28    XY((f32, f32)),
29}
30
31impl Serialize for Location {
32    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
33    where
34        S: Serializer,
35    {
36        let mut seq = serializer.serialize_seq(Some(2))?;
37        match self {
38            Location::LatLon(coords) => {
39                seq.serialize_element(&(LocationType::LatLon as u8))?;
40                seq.serialize_element(&coords)?;
41            }
42            Location::Human(address) => {
43                seq.serialize_element(&(LocationType::Human as u8))?;
44                seq.serialize_element(&address)?;
45            }
46            Location::WFW(address) => {
47                seq.serialize_element(&(LocationType::WFW as u8))?;
48                seq.serialize_element(&address)?;
49            }
50            Location::XY(coords) => {
51                seq.serialize_element(&(LocationType::XY as u8))?;
52                seq.serialize_element(&coords)?;
53            }
54        }
55        seq.end()
56    }
57}
58
59impl<'de> Deserialize<'de> for Location {
60    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
61    where
62        D: Deserializer<'de>,
63    {
64        struct LocationVisitor;
65
66        impl<'de> Visitor<'de> for LocationVisitor {
67            type Value = Location;
68
69            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
70                formatter.write_str("Location")
71            }
72
73            fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
74            where
75                V: SeqAccess<'de>,
76            {
77                let loc_type: u8 = seq
78                    .next_element()?
79                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
80                let loc = LocationType::try_from(loc_type).map_err(|_err| {
81                    de::Error::invalid_value(
82                        serde::de::Unexpected::Unsigned(loc_type.into()),
83                        &self,
84                    )
85                })?;
86                match loc {
87                    LocationType::LatLon => {
88                        let coords: (f32, f32) = seq
89                            .next_element()?
90                            .ok_or_else(|| de::Error::invalid_length(1, &self))?;
91                        Ok(Location::LatLon(coords))
92                    }
93                    LocationType::Human => {
94                        let address: String = seq
95                            .next_element()?
96                            .ok_or_else(|| de::Error::invalid_length(1, &self))?;
97                        Ok(Location::Human(address))
98                    }
99                    LocationType::WFW => {
100                        let address: String = seq
101                            .next_element()?
102                            .ok_or_else(|| de::Error::invalid_length(1, &self))?;
103                        Ok(Location::WFW(address))
104                    }
105                    LocationType::XY => {
106                        let coords: (f32, f32) = seq
107                            .next_element()?
108                            .ok_or_else(|| de::Error::invalid_length(1, &self))?;
109                        Ok(Location::XY(coords))
110                    }
111                }
112            }
113        }
114
115        deserializer.deserialize_any(LocationVisitor)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use crate::location::Location;
122    #[test]
123    fn test_loc_lonlat_roundtrip() {
124        let loc = Location::LatLon((23.0, 42.0));
125        let buf = serde_cbor::to_vec(&loc).unwrap();
126        let loc2 = serde_cbor::from_slice(&buf).unwrap();
127        assert_eq!(loc, loc2);
128    }
129    #[test]
130    fn test_loc_xy_roundtrip() {
131        let loc = Location::XY((23.0, 42.0));
132        let buf = serde_cbor::to_vec(&loc).unwrap();
133        let loc2 = serde_cbor::from_slice(&buf).unwrap();
134        assert_eq!(loc, loc2);
135    }
136
137    #[test]
138    fn test_loc_human_roundtrip() {
139        let loc = Location::Human("Bahnhofstr 23, 12345 Nirgendwo".into());
140        let buf = serde_cbor::to_vec(&loc).unwrap();
141        let loc2 = serde_cbor::from_slice(&buf).unwrap();
142        assert_eq!(loc, loc2);
143    }
144
145    #[test]
146    fn test_loc_wfw_roundtrip() {
147        let loc = Location::WFW("SINKUT-MEIJER-BETSUKAI".into());
148        let buf = serde_cbor::to_vec(&loc).unwrap();
149        let loc2 = serde_cbor::from_slice(&buf).unwrap();
150        assert_eq!(loc, loc2);
151    }
152}