use arrayvec::ArrayVec; #[derive(Debug, PartialEq, Eq, Clone)] pub struct Route { pub max_hops: u8, path: ArrayVec<u8, 254>, has_future: bool, } impl Route { pub fn new(max_hops: u8) -> Self { let path = ArrayVec::new(); Self { max_hops, path, has_future: false, } } /// Returns `None` if there isn't enough space for the callsign /// Returns `None` if attempting to pushed is_used=true after an existing is_used=false /// is_future=false when this callsign has digipeated /// is_future=true when this callsign is expected to be part of the route, but isn't yet /// see the specs for more information pub fn push_callsign(&mut self, callsign: &str, ssid: u8, is_future: bool) -> Option<()> { let len = callsign.as_bytes().len() + 2; let free_space = self.path.capacity() - self.path.len(); if len > free_space { return None; } self.has_future = self.has_future || is_future; if self.has_future && !is_future { return None; } // safe to unwrap since we already did a length check self.path .try_extend_from_slice(callsign.as_bytes()) .unwrap(); self.path.push(if is_future { 0xFD } else { 0xFF }); self.path.push(ssid); Some(()) } /// Returns `None` if there isn't enough space pub fn push_internet(&mut self) -> Option<()> { let free_space = self.path.capacity() - self.path.len(); if free_space < 1 { return None; } self.path.push(0xFE); Some(()) } pub fn iter(&'_ self) -> RouteIter<'_> { RouteIter::new(self) } pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> { let packet_len = self.path.len() + 1; let buf = buf.get_mut(0..(packet_len + 1))?; buf[0] = packet_len.try_into().unwrap(); buf[1] = self.max_hops; buf[2..(packet_len + 1)].copy_from_slice(&self.path); Some(buf) } pub fn decode(data: &[u8]) -> Option<Self> { let len: usize = (*data.first()?).into(); let data = data.get(1..(len + 1))?; let max_hops = *data.first()?; let mut path = ArrayVec::new(); path.try_extend_from_slice(&data[1..]).unwrap(); let has_future = data[1..].iter().any(|x| *x == 0xFD); let s = Self { max_hops, path, has_future, }; if UntrustedRouteIter::new(&s).any(|v| v.is_none()) { return None; } Some(s) } } #[derive(Debug, Eq, PartialEq)] pub enum RouteNode<'a> { Internet, Identity(&'a str, u8, bool), } enum UntrustedRoute<'a> { Some(RouteNode<'a>), None, Invalid, } #[derive(Debug)] struct UntrustedRouteIter<'a> { route: &'a Route, i: usize, seen_future: bool, dead: bool, } impl<'a> UntrustedRouteIter<'a> { fn new(route: &'a Route) -> Self { Self { route, i: 0, seen_future: false, dead: false, } } fn maybe_next(&mut self) -> UntrustedRoute<'a> { if self.i == self.route.path.len() { return UntrustedRoute::None; } let i_start = self.i; self.i += 1; if self.route.path[i_start] == 0xFE { return UntrustedRoute::Some(RouteNode::Internet); } while self.route.path[self.i] != 0xFD && self.route.path[self.i] != 0xFF { self.i += 1; } let callsign = match core::str::from_utf8(&self.route.path[i_start..self.i]) { Ok(x) => x, Err(_) => return UntrustedRoute::Invalid, }; let is_future = self.route.path[self.i] == 0xFD; if self.seen_future && !is_future { // past after future - not allowed return UntrustedRoute::Invalid; } self.seen_future |= is_future; self.i += 1; let ssid = self.route.path[self.i]; self.i += 1; UntrustedRoute::Some(RouteNode::Identity(callsign, ssid, is_future)) } } impl<'a> Iterator for UntrustedRouteIter<'a> { type Item = Option<RouteNode<'a>>; fn next(&mut self) -> Option<Self::Item> { if self.dead { return None; } match self.maybe_next() { UntrustedRoute::Some(v) => Some(Some(v)), UntrustedRoute::None => None, UntrustedRoute::Invalid => Some(None), } } } pub struct RouteIter<'a> { iter: UntrustedRouteIter<'a>, } impl<'a> RouteIter<'a> { fn new(route: &'a Route) -> Self { Self { iter: UntrustedRouteIter::new(route), } } } impl<'a> Iterator for RouteIter<'a> { type Item = RouteNode<'a>; fn next(&mut self) -> Option<Self::Item> { Some(self.iter.next()?.unwrap()) } }