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
35pub 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}