Skip to content
Snippets Groups Projects
mod.rs 14.8 KiB
Newer Older
mod arbitrary;
mod comment;
mod destination;
mod gps;
mod identification;
Stephen D's avatar
Stephen D committed
mod node_info;
mod route;
mod timestamp;
mod unknown;

use crate::error::DecodeError;

pub use self::{
    arbitrary::Arbitrary,
    comment::Comment,
    destination::Destination,
    gps::Gps,
    identification::Identification,
Stephen D's avatar
Stephen D committed
    node_info::{NodeInfo, NodeInfoBuilder},
    route::{PastHop, Route, RouteHop, RouteIter},
    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;
Stephen D's avatar
Stephen D committed
pub(crate) const NODE_INFO_TYPE: u8 = 0x09;

#[derive(Debug)]
pub enum Whisker {
    Identification(Identification),
    Timestamp(Timestamp),
    Gps(Gps),
    Comment(Comment),
    Route(Route),
    Destination(Destination),
    Arbitrary(Arbitrary),
Stephen D's avatar
Stephen D committed
    NodeInfo(NodeInfo),
    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 })?,
            ),
Stephen D's avatar
Stephen D committed
            NODE_INFO_TYPE => Whisker::NodeInfo(
                NodeInfo::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
Stephen D's avatar
Stephen D committed
            whisker_type => {
                Whisker::Unknown(Unknown::new(whisker_type, data[1..].try_into().unwrap()))
            }
Stephen D's avatar
Stephen D committed
impl Iterator for WhiskerIter<'_> {
    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),
        }
    }
}

Stephen D's avatar
Stephen D committed
impl Iterator for ValidatedWhiskerIter<'_> {
    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::{
        identity::Identity,
        whisker::route::{PastHop, RouteHop},
    };

    use super::*;

    #[test]
    fn ident_e2e() {
        let icon = 2738;
        let call = "VE9ABCDEFGZZ4839-???";
        let ssid = 17;

        let ident = Identification::new(call, ssid, icon).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!(123.75, gps.heading());
    }

    #[test]
    fn gps_max_heading() {
        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            f16::from_f32(12.3),
        );

        assert_eq!(358.59375, gps.heading());

        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            957.47,
            f16::from_f32(12.3),
        );

        assert_eq!(957.65625 - 360.0 * 2.0, gps.heading());
    }

    #[test]
    fn gps_min_heading() {
        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            0.0,
            f16::from_f32(12.3),
        );

        assert_eq!(0.0, gps.heading());

        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            -22.0,
            f16::from_f32(12.3),
        );

        assert_eq!(337.5, gps.heading());

        let gps = Gps::new(
            4.0,
            23.45,
            f16::from_f32(45.02),
            24,
            -1206.0,
            f16::from_f32(12.3),
        );

        assert_eq!(233.4375, 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);
Stephen D's avatar
Stephen D committed
        route
            .push_past(PastHop::new(Identity::new("VE2XYZ", 23), None))
Stephen D's avatar
Stephen D committed
            .unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_future(Identity::new("VE9AAAAA", 94)).unwrap();
Stephen D's avatar
Stephen D committed
        // past after future - not allowed
        assert!(route
            .push_past(PastHop::new(Identity::new("VE9AAAAA", 94), None))
Stephen D's avatar
Stephen D committed
            .is_none());

        // too long
        assert!(route
            .push_future(Identity::new(
                "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,
Stephen D's avatar
Stephen D committed
            )).is_none());
            .push_future(Identity::new("This is the last callsign", 0))
            .unwrap();
        route.push_internet().unwrap();

        let mut iter = route.iter();
        assert_eq!(
            RouteHop::Past(PastHop::new(Identity::new("VE2XYZ", 23), None)),
            iter.next().unwrap()
        );
        assert_eq!(RouteHop::Internet, iter.next().unwrap());
        assert_eq!(RouteHop::Internet, iter.next().unwrap());
        assert_eq!(RouteHop::Internet, iter.next().unwrap());
        assert_eq!(
            RouteHop::Future(Identity::new("VE9AAAAA", 94)),
            iter.next().unwrap()
        );
        assert_eq!(
            RouteHop::Future(Identity::new("This is the last callsign", 0)),
            iter.next().unwrap()
        );
        assert_eq!(RouteHop::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);
Stephen D's avatar
Stephen D committed
        route
            .push_past(PastHop::new(Identity::new("VE2XYZ", 23), Some(0.0)))
Stephen D's avatar
Stephen D committed
            .unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_internet().unwrap();
        route.push_future(Identity::new("VE9AAAAA", 94)).unwrap();
            .push_future(Identity::new("This is the last callsign", 0))
            .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_past(PastHop::new(Identity::new("VE1ABC", 0), Some(-96.0)))
            .unwrap();
        ex1.push_past(PastHop::new(Identity::new("VE2DEF", 234), Some(-13.0)))
            .unwrap();
        ex1.push_past(PastHop::new(Identity::new("VE3XYZ", 14), Some(-106.0)))
            .unwrap();

        let mut buf = [0; 256];
        let encoded = ex1.encode(&mut buf).unwrap();
        assert_eq!(
            &[
Stephen D's avatar
Stephen D committed
                0x1C, 0x04, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x60, 0x56, 0x45, 0x32,
                0x44, 0x45, 0x46, 0xFF, 0xEA, 0xDC, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFF, 0x0E,
                0x51
        let mut ex1_5 = Route::new(4);
        ex1_5
            .push_past(PastHop::new(Identity::new("VE1ABC", 0), Some(-96.0)))
            .unwrap();
        ex1_5.push_future(Identity::new("VE2DEF", 234)).unwrap();
        ex1_5.push_future(Identity::new("VE3XYZ", 14)).unwrap();

        let mut buf = [0; 256];
        let encoded = ex1_5.encode(&mut buf).unwrap();
        assert_eq!(
            &[
                0x1A, 0x04, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x60, 0x56, 0x45, 0x32,
                0x44, 0x45, 0x46, 0xFD, 0xEA, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFD, 0x0E,
            ],
            &encoded
        );

        let mut ex2 = Route::new(3);
        ex2.push_past(PastHop::new(Identity::new("VE1ABC", 0), None))
            .unwrap();
        ex2.push_internet();
        ex2.push_past(PastHop::new(Identity::new("VE2DEF", 234), Some(-86.5)))
            .unwrap();
        ex2.push_internet();
        ex2.push_past(PastHop::new(Identity::new("VE3XYZ", 14), Some(-65.0)))
            .unwrap();

        let encoded = ex2.encode(&mut buf).unwrap();
        assert_eq!(
            &[
Stephen D's avatar
Stephen D committed
                0x1E, 0x03, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x00, 0xFE, 0x56, 0x45,
                0x32, 0x44, 0x45, 0x46, 0xFF, 0xEA, 0x6E, 0xFE, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A,
                0xFF, 0x0E, 0x8E
            ],
            &encoded
        );

        let mut ex3 = Route::new(0);
        ex3.push_past(PastHop::new(Identity::new("VE1ABC", 0), Some(-42.5)))
            .unwrap();
        ex3.push_internet();
        ex3.push_internet();

        let encoded = ex3.encode(&mut buf).unwrap();
        assert_eq!(
Stephen D's avatar
Stephen D committed
            &[0x0C, 0x00, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0xB0, 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 dest_ack() {
        let dest = Destination::new(true, 84, "abc", 17).unwrap();
        assert_eq!(84, dest.ack_num());
        assert!(dest.is_ack());
        assert_eq!("abc", dest.callsign());
        assert_eq!(17, dest.ssid());
    }

    #[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[..]);
    }
}