Select Git revision
Forked from
CATS / ham-cats
Source project has a limited visibility.
mod.rs 12.03 KiB
mod arbitrary;
mod comment;
mod destination;
mod gps;
mod identification;
mod route;
mod timestamp;
mod unknown;
use crate::error::DecodeError;
pub use self::{
arbitrary::Arbitrary,
comment::Comment,
destination::Destination,
gps::Gps,
identification::Identification,
route::{Route, RouteIter, RouteNode},
timestamp::Timestamp,
unknown::Unknown,
};
pub(crate) const IDENTIFICATION_TYPE: u8 = 0x00;
pub(crate) const TIMESTAMP_TYPE: u8 = 0x01;
pub(crate) const GPS_TYPE: u8 = 0x02;
pub(crate) const COMMENT_TYPE: u8 = 0x03;
pub(crate) const ROUTE_TYPE: u8 = 0x04;
pub(crate) const DESTINATION_TYPE: u8 = 0x05;
pub(crate) const ARBITRARY_TYPE: u8 = 0x06;
#[derive(Debug)]
pub enum Whisker {
Identification(Identification),
Timestamp(Timestamp),
Gps(Gps),
Comment(Comment),
Route(Route),
Destination(Destination),
Arbitrary(Arbitrary),
Unknown(Unknown),
}
pub struct WhiskerIter<'a> {
data: &'a [u8],
i: usize,
// for detecting duplicates
has_ident: bool,
has_timestamp: bool,
has_gps: bool,
has_route: bool,
}
impl<'a> WhiskerIter<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self {
data,
i: 0,
has_ident: false,
has_timestamp: false,
has_gps: false,
has_route: false,
}
}
fn decode(&mut self) -> Result<Whisker, DecodeError> {
let position = self.i;
let t = *self
.data
.get(self.i)
.ok_or(DecodeError::UnexpectedEndOfInput)?;
let len = usize::from(
*self
.data
.get(self.i + 1)
.ok_or(DecodeError::UnexpectedEndOfInput)?,
);
let data = self
.data
.get((self.i + 1)..(self.i + 2 + len))
.ok_or(DecodeError::UnexpectedEndOfInput)?;
self.i += len + 2;
let out = match t {
IDENTIFICATION_TYPE => {
if self.has_ident {
return Err(DecodeError::DuplicateWhisker { position });
}
self.has_ident = true;
Whisker::Identification(
Identification::decode(data)
.ok_or(DecodeError::MalformedWhisker { position })?,
)
}
TIMESTAMP_TYPE => {
if self.has_timestamp {
return Err(DecodeError::DuplicateWhisker { position });
}
self.has_timestamp = true;
Whisker::Timestamp(
Timestamp::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
)
}
GPS_TYPE => {
if self.has_gps {
return Err(DecodeError::DuplicateWhisker { position });
}
self.has_gps = true;
Whisker::Gps(Gps::decode(data).ok_or(DecodeError::MalformedWhisker { position })?)
}
COMMENT_TYPE => Whisker::Comment(
Comment::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
),
ROUTE_TYPE => {
if self.has_route {
return Err(DecodeError::DuplicateWhisker { position });
}
self.has_route = true;
Whisker::Route(
Route::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
)
}
DESTINATION_TYPE => Whisker::Destination(
Destination::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
),
ARBITRARY_TYPE => Whisker::Arbitrary(
Arbitrary::decode(data).ok_or(DecodeError::MalformedWhisker { position })?,
),
// safe to unwrap because we know len has to be 255 or less, since it's a u8
whisker_type => {
Whisker::Unknown(Unknown::new(whisker_type, data[1..].try_into().unwrap()))
}
};
Ok(out)
}
}
impl<'a> Iterator for WhiskerIter<'a> {
type Item = Result<Whisker, DecodeError>;
fn next(&mut self) -> Option<Self::Item> {
if self.i == self.data.len() {
return None;
}
Some(self.decode())
}
}
pub struct ValidatedWhiskerIter<'a> {
iter: WhiskerIter<'a>,
}
impl<'a> ValidatedWhiskerIter<'a> {
pub(crate) fn new(data: &'a [u8]) -> Self {
Self {
iter: WhiskerIter::new(data),
}
}
}
impl<'a> Iterator for ValidatedWhiskerIter<'a> {
type Item = Whisker;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|w| w.unwrap())
}
}
#[cfg(test)]
mod tests {
use half::f16;
use crate::whisker::route::RouteNode;
use super::*;
#[test]
fn ident_e2e() {
let icon = 2738;
let call = "VE9ABCDEFGZZ4839-???";
let ssid = 17;
let ident = Identification::new(icon, call, ssid).unwrap();
let mut buf = [0; 256];
let encoded = ident.encode(&mut buf).unwrap();
let decoded = Identification::decode(encoded).unwrap();
assert_eq!(ident, decoded);
assert_eq!(call, &decoded.callsign);
assert_eq!(ssid, decoded.ssid);
}
#[test]
fn new_timestamp() {
let t = 34894;
assert_eq!(t, Timestamp::new(t).unwrap().unix_time());
let t = 1 << (5 * 8);
assert_eq!(None, Timestamp::new(t));
}
#[test]
fn timestamp_e2e() {
let t = 34894;
let timestamp = Timestamp::new(t).unwrap();
let mut buf = [0; 256];
let encoded = timestamp.encode(&mut buf).unwrap();
let decoded = Timestamp::decode(encoded).unwrap();
assert_eq!(timestamp, decoded);
assert_eq!(t, timestamp.unix_time());
}
#[test]
fn new_gps() {
let gps = Gps::new(
90.0,
-180.0,
f16::from_f32(45.02),
24,
123.45,
f16::from_f32(12.3),
);
assert_eq!(89.99899999704212, gps.latitude());
assert_eq!(-179.9989999551326, gps.longitude());
assert_eq!(120.9375, gps.heading());
}
#[test]
fn gps_max_heading() {
let gps = Gps::new(
4.0,
23.45,
f16::from_f32(45.02),
24,
360.0,
f16::from_f32(12.3),
);
assert_eq!(357.1875, gps.heading());
}
#[test]
fn gps_e2e() {
let gps = Gps::new(
90.0,
-180.0,
f16::from_f32(45.02),
24,
123.45,
f16::from_f32(12.3),
);
let mut buf = [0; 256];
let encoded = gps.encode(&mut buf).unwrap();
let decoded = Gps::decode(encoded).unwrap();
assert_eq!(gps, decoded);
}
#[test]
fn comment_e2e() {
let data = b"Hello world! This is an example comment";
let comment = Comment::new(data).unwrap();
let mut buf = [0; 256];
let encoded = comment.encode(&mut buf).unwrap();
let decoded = Comment::decode(encoded).unwrap();
let decoded2 = Comment::decode(&buf).unwrap();
assert_eq!(data[..], decoded.internal_data()[..]);
assert_eq!(data[..], decoded2.internal_data()[..]);
}
#[test]
fn route_push_and_iter() {
let mut route = Route::new(34);
route.push_callsign("VE2XYZ", 23, false).unwrap();
route.push_internet().unwrap();
route.push_internet().unwrap();
route.push_internet().unwrap();
route.push_callsign("VE9AAAAA", 94, true).unwrap();
assert!(route.push_callsign("VE9AAAAA", 94, false).is_none());
// too long
assert!(route
.push_callsign(
"lsdfjslkdfjlksdjflksfjsdklfjsdklfjsdklfjsdklfjsklfsef;jklsdfjkl;sdf;klsdf;klsjdfJSDJFSKL:DFJDSL:KFskldfj;slkdfjsdkl;fjdskl;fjsdfl;kjsdfl;ksdjfkl;ssdfl;kjsdfl;ksdjf;sdklsd;lfkjsdlfk;jsdl;fkjsd;klfjsd;fljsf;oidfjgwper0tujdfgndfjkl;gjnergjol;kehfgo;dijge;oghdfkl;gjdfkl;gjdeior;lgjedr;ioghjdorighndeklo;grjiop[",
20,
true,
).is_none());
route
.push_callsign("This is the last callsign", 0, true)
.unwrap();
route.push_internet().unwrap();
let mut iter = route.iter();
assert_eq!(
RouteNode::Identity("VE2XYZ", 23, false),
iter.next().unwrap()
);
assert_eq!(RouteNode::Internet, iter.next().unwrap());
assert_eq!(RouteNode::Internet, iter.next().unwrap());
assert_eq!(RouteNode::Internet, iter.next().unwrap());
assert_eq!(
RouteNode::Identity("VE9AAAAA", 94, true),
iter.next().unwrap()
);
assert_eq!(
RouteNode::Identity("This is the last callsign", 0, true),
iter.next().unwrap()
);
assert_eq!(RouteNode::Internet, iter.next().unwrap());
assert_eq!(None, iter.next());
assert_eq!(34, route.max_hops);
}
#[test]
fn route_e2e() {
let mut route = Route::new(34);
route.push_callsign("VE2XYZ", 23, false).unwrap();
route.push_internet().unwrap();
route.push_internet().unwrap();
route.push_internet().unwrap();
route.push_callsign("VE9AAAAA", 94, true).unwrap();
route
.push_callsign("This is the last callsign", 0, true)
.unwrap();
route.push_internet().unwrap();
let mut buf = [0; 256];
let encoded = route.encode(&mut buf).unwrap();
let decoded = Route::decode(encoded).unwrap();
assert_eq!(route, decoded);
}
// verify examples in the standard doc
#[test]
fn route_doc_examples() {
let mut ex1 = Route::new(4);
ex1.push_callsign("VE1ABC", 0, false);
ex1.push_callsign("VE2DEF", 234, false);
ex1.push_callsign("VE3XYZ", 14, false);
let mut buf = [0; 256];
let encoded = ex1.encode(&mut buf).unwrap();
assert_eq!(
&[
0x19, 0x04, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x56, 0x45, 0x32, 0x44,
0x45, 0x46, 0xFF, 0xEA, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFF, 0x0E
],
&encoded
);
let mut ex2 = Route::new(3);
ex2.push_callsign("VE1ABC", 0, false);
ex2.push_internet();
ex2.push_callsign("VE2DEF", 234, false);
ex2.push_internet();
ex2.push_callsign("VE3XYZ", 14, false);
let encoded = ex2.encode(&mut buf).unwrap();
assert_eq!(
&[
0x1B, 0x03, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0xFE, 0x56, 0x45, 0x32,
0x44, 0x45, 0x46, 0xFF, 0xEA, 0xFE, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFF, 0x0E
],
&encoded
);
let mut ex3 = Route::new(0);
ex3.push_callsign("VE1ABC", 0, false);
ex3.push_internet();
ex3.push_internet();
let encoded = ex3.encode(&mut buf).unwrap();
assert_eq!(
&[0x0B, 0x00, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0xFE, 0xFE],
&encoded
);
}
#[test]
fn dest_e2e() {
assert_eq!(None, Destination::new(true, 0, "dest", 36));
assert_eq!(None, Destination::new(false, 128, "abc", 0));
assert!(Destination::new(false, 0, "anc", 8).is_some());
let dest = Destination::new(true, 64, "mrow", 31).unwrap();
let mut buf = [0; 256];
let encoded = dest.encode(&mut buf).unwrap();
let decoded = Destination::decode(encoded).unwrap();
assert_eq!(dest, decoded);
}
#[test]
fn arbitrary_e2e() {
let data = b"Hello world! This is an example comment";
let arbitrary = Arbitrary::new(data).unwrap();
let mut buf = [0; 256];
let encoded = arbitrary.encode(&mut buf).unwrap();
let decoded = Arbitrary::decode(encoded).unwrap();
let decoded2 = Arbitrary::decode(&buf).unwrap();
assert_eq!(data[..], decoded.0[..]);
assert_eq!(data[..], decoded2.0[..]);
}
}