1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use std::io::{self, Write};

use Callsign;

#[derive(Eq, PartialEq, Clone, Debug)]
pub enum Via {
    Callsign(Callsign, bool),
    QConstruct(QConstruct),
}

impl Via {
    pub fn decode_textual(bytes: &[u8]) -> Option<Self> {
        if let Some(q) = QConstruct::decode_textual(bytes) {
            return Some(Self::QConstruct(q));
        }

        if let Some((c, heard)) = Callsign::decode_textual(bytes) {
            return Some(Self::Callsign(c, heard));
        }

        None
    }

    pub fn encode_textual<W: Write>(&self, w: &mut W) -> io::Result<()> {
        match self {
            Self::Callsign(c, heard) => {
                c.encode_textual(*heard, w)?;
            }
            Self::QConstruct(q) => {
                write!(w, "{}", q.as_textual())?;
            }
        }

        Ok(())
    }

    pub fn callsign(&self) -> Option<(&Callsign, bool)> {
        match self {
            Self::Callsign(c, heard) => Some((c, *heard)),
            Self::QConstruct(_) => None,
        }
    }

    pub fn callsign_mut(&mut self) -> Option<(&mut Callsign, &mut bool)> {
        match self {
            Self::Callsign(c, heard) => Some((c, heard)),
            Self::QConstruct(_) => None,
        }
    }
}

// Can't be encoded/decoded as ax.25
// These should never go on the air
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum QConstruct {
    AC,
    AX,
    AU,
    Ao,
    AO,
    AS,
    Ar,
    AR,
    AZ,
    AI,
}

impl QConstruct {
    pub fn decode_textual(bytes: &[u8]) -> Option<Self> {
        let q = match bytes {
            b"qAC" => QConstruct::AC,
            b"qAX" => QConstruct::AX,
            b"qAU" => QConstruct::AU,
            b"qAo" => QConstruct::Ao,
            b"qAO" => QConstruct::AO,
            b"qAS" => QConstruct::AS,
            b"qAr" => QConstruct::Ar,
            b"qAR" => QConstruct::AR,
            b"qAZ" => QConstruct::AZ,
            b"qAI" => QConstruct::AI,
            _ => return None,
        };

        Some(q)
    }

    pub fn as_textual(&self) -> &'static str {
        match self {
            QConstruct::AC => "qAC",
            QConstruct::AX => "qAX",
            QConstruct::AU => "qAU",
            QConstruct::Ao => "qAo",
            QConstruct::AO => "qAO",
            QConstruct::AS => "qAS",
            QConstruct::Ar => "qAr",
            QConstruct::AR => "qAR",
            QConstruct::AZ => "qAZ",
            QConstruct::AI => "qAI",
        }
    }
}