Skip to main content

aprs_decode/
third_party.rs

1use crate::error::AprsError;
2use crate::packet::AprsPacket;
3
4/// A third-party APRS packet: a complete APRS packet forwarded from one
5/// network to another (e.g. RF→APRS-IS gateway).
6///
7/// DTI: `}`
8///
9/// The payload after `}` is a full textual APRS packet that is recursively
10/// decoded. Decode failures return an error rather than silently ignoring them.
11#[derive(Debug, Clone, PartialEq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct AprsThirdParty {
14    /// The forwarded inner packet, recursively decoded.
15    pub inner: Box<AprsPacket>,
16}
17
18impl AprsThirdParty {
19    /// Decode from the information field (including the leading `}` DTI byte).
20    pub(crate) fn parse(info: &[u8]) -> Result<Self, AprsError> {
21        let inner_bytes = info.get(1..).unwrap_or_default();
22        let inner = AprsPacket::decode_textual(inner_bytes)?;
23        Ok(Self {
24            inner: Box::new(inner),
25        })
26    }
27
28    pub fn encode(&self) -> Result<Vec<u8>, AprsError> {
29        let mut out = vec![b'}'];
30        out.extend_from_slice(&self.inner.encode_textual()?);
31        Ok(out)
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use super::*;
38    use crate::AprsData;
39
40    #[test]
41    fn decode_third_party_position() {
42        let tp = AprsThirdParty::parse(b"}WB0VGI-7>APOT30,W0RO-11*,WIDE2-1:!4228.35N/09101.45Wk")
43            .unwrap();
44        assert_eq!(tp.inner.from.to_string(), "WB0VGI-7");
45        assert!(matches!(tp.inner.data, AprsData::Position(_)));
46    }
47
48    #[test]
49    fn decode_third_party_message() {
50        let tp =
51            AprsThirdParty::parse(b"}W1ABC>APRS,WIDE1-1::DEST     :Hello from third party{123")
52                .unwrap();
53        assert_eq!(tp.inner.from.to_string(), "W1ABC");
54        assert!(matches!(tp.inner.data, AprsData::Message(_)));
55    }
56
57    #[test]
58    fn encode_round_trip() {
59        let inner = b"}W1ABC>APRS::DEST     :Hello{001";
60        let tp = AprsThirdParty::parse(inner).unwrap();
61        let encoded = tp.encode().unwrap();
62        assert_eq!(encoded.as_slice(), inner.as_slice());
63    }
64}