Skip to main content

aprs_decode/
capabilities.rs

1/// An APRS Station Capabilities report.
2///
3/// DTI: `<`
4///
5/// Lists the capabilities of the station, typically as a comma-separated
6/// list of `KEY=VALUE` pairs or bare tokens (e.g. `IGATE,MSG_CNT=0`).
7/// The content is stored verbatim — the spec doesn't define a normative format.
8#[derive(Debug, Clone, PartialEq, Eq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct AprsCapabilities {
11    /// Raw capability string (everything after the `<` DTI byte).
12    pub raw: Vec<u8>,
13}
14
15impl AprsCapabilities {
16    /// Decode from the information field (including the leading `<` DTI byte).
17    pub(crate) fn parse(info: &[u8]) -> Self {
18        Self {
19            raw: info.get(1..).unwrap_or_default().to_vec(),
20        }
21    }
22
23    pub fn encode(&self) -> Vec<u8> {
24        let mut out = vec![b'<'];
25        out.extend_from_slice(&self.raw);
26        out
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn round_trip() {
36        let raw = b"<IGATE,MSG_CNT=10,LOC_CNT=20";
37        let cap = AprsCapabilities::parse(raw);
38        assert_eq!(cap.raw, b"IGATE,MSG_CNT=10,LOC_CNT=20");
39        assert_eq!(cap.encode().as_slice(), raw.as_slice());
40    }
41
42    #[test]
43    fn empty_capabilities() {
44        let cap = AprsCapabilities::parse(b"<");
45        assert!(cap.raw.is_empty());
46    }
47}