dtn7_plus/location/
block.rs

1use super::{Location, NodeTypeFlags};
2use bp7::canonical::{new_canonical_block, CanonicalBlockType, CanonicalData};
3use bp7::{CanonicalBlock, EndpointID};
4use derive_try_from_primitive::TryFromPrimitive;
5use serde::de::{SeqAccess, Visitor};
6use serde::ser::{SerializeSeq, Serializer};
7use serde::{de, Deserialize, Deserializer, Serialize};
8use std::convert::TryFrom;
9use std::fmt;
10use thiserror::Error;
11
12#[derive(Error, Debug)]
13pub enum LocationError {
14    #[error("serde cbor error: {0}")]
15    Cbor(#[from] serde_cbor::Error),
16    #[error("failed to create endpoint: {0}")]
17    EndpointIdInvalid(#[from] bp7::eid::EndpointIdError),
18    #[error("invalid endpoint supplied")]
19    InvalidEndpoint,
20    #[error("payload missing")]
21    PayloadMissing,
22    #[error("invalid location block")]
23    InvalidLocationBlock,
24}
25
26#[derive(Debug, Clone, PartialEq, TryFromPrimitive, Serialize, Deserialize)]
27#[repr(u8)]
28pub enum LocationBlockType {
29    Position = 1,
30    FenceEllipse = 2,
31    FenceRect = 3,
32    Trace = 4,
33}
34
35// HOP_COUNT_BLOCK is a BlockType for a Hop Count block as defined in
36// section 4.3.3.
37pub const LOCATION_BLOCK: CanonicalBlockType = 223;
38
39#[derive(Debug, Clone, PartialEq)]
40pub enum LocationBlockData {
41    Position(NodeTypeFlags, Location),
42    FenceEllipse(Location, u64, u64),
43    FenceRect(Location, Location),
44    Trace(NodeTypeFlags, EndpointID, Location),
45}
46
47impl Serialize for LocationBlockData {
48    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
49    where
50        S: Serializer,
51    {
52        match self {
53            LocationBlockData::Position(info, coords) => {
54                let mut seq = serializer.serialize_seq(Some(3))?;
55                seq.serialize_element(&(LocationBlockType::Position as u8))?;
56                seq.serialize_element(&info.bits())?;
57                seq.serialize_element(&coords)?;
58                seq.end()
59            }
60            LocationBlockData::FenceEllipse(coords, r1, r2) => {
61                let mut seq = serializer.serialize_seq(Some(4))?;
62                seq.serialize_element(&(LocationBlockType::FenceEllipse as u8))?;
63                seq.serialize_element(&coords)?;
64                seq.serialize_element(r1)?;
65                seq.serialize_element(r2)?;
66                seq.end()
67            }
68            LocationBlockData::FenceRect(topleft, bottomright) => {
69                let mut seq = serializer.serialize_seq(Some(3))?;
70                seq.serialize_element(&(LocationBlockType::FenceRect as u8))?;
71                seq.serialize_element(&topleft)?;
72                seq.serialize_element(&bottomright)?;
73                seq.end()
74            }
75            LocationBlockData::Trace(info, node, coords) => {
76                let mut seq = serializer.serialize_seq(Some(4))?;
77                seq.serialize_element(&(LocationBlockType::Trace as u8))?;
78                seq.serialize_element(&info.bits())?;
79                seq.serialize_element(&node)?;
80                seq.serialize_element(&coords)?;
81                seq.end()
82            }
83        }
84    }
85}
86impl<'de> Deserialize<'de> for LocationBlockData {
87    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
88    where
89        D: Deserializer<'de>,
90    {
91        struct LocationBlockDataVisitor;
92
93        impl<'de> Visitor<'de> for LocationBlockDataVisitor {
94            type Value = LocationBlockData;
95
96            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
97                formatter.write_str("LocationBlockData")
98            }
99
100            fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
101            where
102                V: SeqAccess<'de>,
103            {
104                let loc_type: u8 = seq
105                    .next_element()?
106                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;
107                let loc = LocationBlockType::try_from(loc_type).map_err(|_err| {
108                    de::Error::invalid_value(
109                        serde::de::Unexpected::Unsigned(loc_type.into()),
110                        &self,
111                    )
112                })?;
113                match loc {
114                    LocationBlockType::Position => {
115                        let info: NodeTypeFlags = NodeTypeFlags::from_bits(
116                            seq.next_element()?
117                                .ok_or_else(|| de::Error::invalid_length(1, &self))?,
118                        )
119                        .unwrap_or_default();
120                        let coords: Location = seq
121                            .next_element()?
122                            .ok_or_else(|| de::Error::invalid_length(2, &self))?;
123                        Ok(LocationBlockData::Position(info, coords))
124                    }
125                    LocationBlockType::FenceEllipse => {
126                        let coords: Location = seq
127                            .next_element()?
128                            .ok_or_else(|| de::Error::invalid_length(1, &self))?;
129                        let r1: u64 = seq
130                            .next_element()?
131                            .ok_or_else(|| de::Error::invalid_length(2, &self))?;
132                        let r2: u64 = seq
133                            .next_element()?
134                            .ok_or_else(|| de::Error::invalid_length(3, &self))?;
135
136                        Ok(LocationBlockData::FenceEllipse(coords, r1, r2))
137                    }
138                    LocationBlockType::FenceRect => {
139                        let topleft: Location = seq
140                            .next_element()?
141                            .ok_or_else(|| de::Error::invalid_length(1, &self))?;
142                        let bottomright: Location = seq
143                            .next_element()?
144                            .ok_or_else(|| de::Error::invalid_length(2, &self))?;
145                        Ok(LocationBlockData::FenceRect(topleft, bottomright))
146                    }
147                    LocationBlockType::Trace => {
148                        let info: NodeTypeFlags = NodeTypeFlags::from_bits(
149                            seq.next_element()?
150                                .ok_or_else(|| de::Error::invalid_length(1, &self))?,
151                        )
152                        .unwrap_or_default();
153                        let node: EndpointID = seq
154                            .next_element()?
155                            .ok_or_else(|| de::Error::invalid_length(2, &self))?;
156                        let coords: Location = seq
157                            .next_element()?
158                            .ok_or_else(|| de::Error::invalid_length(3, &self))?;
159                        Ok(LocationBlockData::Trace(info, node, coords))
160                    }
161                }
162            }
163        }
164
165        deserializer.deserialize_any(LocationBlockDataVisitor)
166    }
167}
168
169pub fn new_location_block(block_number: u64, data: LocationBlockData) -> CanonicalBlock {
170    new_canonical_block(
171        LOCATION_BLOCK,
172        block_number,
173        0,
174        CanonicalData::Unknown(serde_cbor::to_vec(&data).unwrap_or_default()),
175    )
176}
177
178pub fn get_location_data(cblock: &CanonicalBlock) -> Result<LocationBlockData, LocationError> {
179    if cblock.block_type == LOCATION_BLOCK {
180        if let CanonicalData::Unknown(data) = cblock.data() {
181            serde_cbor::from_slice(data).map_err(|_err| LocationError::InvalidLocationBlock)
182        } else {
183            Err(LocationError::InvalidLocationBlock)
184        }
185    } else {
186        Err(LocationError::InvalidLocationBlock)
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use crate::location::{
193        get_location_data, new_location_block, Location, LocationBlockData, NodeTypeFlags,
194    };
195    use bp7::bundle::Block;
196    use bp7::EndpointID;
197    use std::convert::TryFrom;
198
199    #[test]
200    fn test_locblock_data_position_roundtrip() {
201        let loc = Location::LatLon((23.0, 42.0));
202        let data = LocationBlockData::Position(NodeTypeFlags::MOBILE, loc);
203        let buf = serde_cbor::to_vec(&data).unwrap();
204        let data2 = serde_cbor::from_slice(&buf).unwrap();
205        assert_eq!(data, data2);
206    }
207
208    #[test]
209    fn test_locblock_data_fence_ellipse_roundtrip() {
210        let loc = Location::LatLon((23.0, 42.0));
211        let data = LocationBlockData::FenceEllipse(loc, 10, 5);
212        let buf = serde_cbor::to_vec(&data).unwrap();
213        let data2 = serde_cbor::from_slice(&buf).unwrap();
214        assert_eq!(data, data2);
215    }
216
217    #[test]
218    fn test_locblock_data_fence_rect_roundtrip() {
219        let loc = Location::LatLon((23.0, 42.0));
220        let loc2 = Location::LatLon((42.0, 66.0));
221        let data = LocationBlockData::FenceRect(loc, loc2);
222        let buf = serde_cbor::to_vec(&data).unwrap();
223        let data2 = serde_cbor::from_slice(&buf).unwrap();
224        assert_eq!(data, data2);
225    }
226    #[test]
227    fn test_locblock_data_trace_roundtrip() {
228        let loc = Location::LatLon((23.0, 42.0));
229        let data = LocationBlockData::Trace(
230            NodeTypeFlags::MOBILE,
231            EndpointID::try_from("dtn://node1").unwrap(),
232            loc,
233        );
234        let buf = serde_cbor::to_vec(&data).unwrap();
235        let data2 = serde_cbor::from_slice(&buf).unwrap();
236        assert_eq!(data, data2);
237    }
238
239    #[test]
240    fn test_cblock_location_roundtrip() {
241        let loc = Location::LatLon((23.0, 42.0));
242        let data = LocationBlockData::Position(NodeTypeFlags::MOBILE, loc);
243
244        let cblock = new_location_block(1, data.clone());
245        let buf = cblock.to_cbor();
246        let cblock2 = serde_cbor::from_slice(&buf).unwrap();
247        assert_eq!(cblock, cblock2);
248        let data2 = get_location_data(&cblock2).unwrap();
249        assert_eq!(data, data2);
250    }
251}