1use std::io::Write;
2
3use Callsign;
4use DecodeError;
5use EncodeError;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct AprsMessage {
9 pub to: Callsign,
10
11 pub addressee: Vec<u8>,
12 pub text: Vec<u8>,
13 pub id: Option<Vec<u8>>,
14}
15
16impl AprsMessage {
17 pub fn decode(b: &[u8], to: Callsign) -> Result<Self, DecodeError> {
18 let mut splitter = b.splitn(2, |x| *x == b':');
19
20 let mut addressee = match splitter.next() {
21 Some(x) => x.to_vec(),
22 None => {
23 return Err(DecodeError::InvalidMessageDestination(vec![]));
24 }
25 };
26
27 if addressee.len() != 9 {
28 return Err(DecodeError::InvalidMessageDestination(addressee.to_owned()));
29 }
30
31 trim_spaces_end(&mut addressee);
32
33 let text = splitter.next().unwrap_or(&[]);
34 let mut text_splitter = text.splitn(2, |x| *x == b'{');
35 let text = text_splitter.next().unwrap_or(&[]).to_vec();
36 let id = text_splitter.next().map(|x| x.to_vec());
37
38 Ok(Self {
39 to,
40
41 addressee,
42 text,
43 id,
44 })
45 }
46
47 pub fn encode<W: Write>(&self, buf: &mut W) -> Result<(), EncodeError> {
48 if self.addressee.len() > 9 {
49 return Err(EncodeError::InvalidMessageAddressee(
50 self.addressee.to_owned(),
51 ));
52 }
53
54 buf.write_all(b":")?;
55 buf.write_all(&self.addressee)?;
56 for _ in self.addressee.len()..9 {
57 buf.write_all(b" ")?;
58 }
59
60 buf.write_all(b":")?;
61 buf.write_all(&self.text)?;
62
63 if let Some(id) = &self.id {
64 buf.write_all(b"{")?;
65 buf.write_all(id)?;
66 }
67
68 Ok(())
69 }
70}
71
72fn trim_spaces_end(arr: &mut Vec<u8>) {
73 let space_count = arr.iter().rev().take_while(|&&b| b == b' ').count();
74
75 arr.truncate(arr.len() - space_count);
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 fn default_callsign() -> Callsign {
83 Callsign::new_no_ssid("VE9")
84 }
85
86 #[test]
87 fn parse_message_invalid_dest() {
88 let result = AprsMessage::decode(
90 &b"DEST :Hello World! This msg has a : colon {32975"[..],
91 default_callsign(),
92 );
93
94 assert_eq!(
95 result,
96 Err(DecodeError::InvalidMessageDestination(b"DEST ".to_vec()))
97 );
98 }
99
100 #[test]
101 fn parse_message_id() {
102 let result = AprsMessage::decode(
103 r"DESTINATI:Hello World! This msg has a : colon {329A7D5Z4".as_bytes(),
104 default_callsign(),
105 );
106
107 assert_eq!(
108 result,
109 Ok(AprsMessage {
110 to: default_callsign(),
111 addressee: b"DESTINATI".to_vec(),
112 id: Some(b"329A7D5Z4".to_vec()),
113 text: b"Hello World! This msg has a : colon ".to_vec()
114 })
115 );
116 }
117
118 #[test]
119 fn parse_message_empty_id() {
120 let result = AprsMessage::decode(
121 r"DESTINATI:Hello World! This msg has a : colon {".as_bytes(),
122 default_callsign(),
123 );
124
125 assert_eq!(
126 result,
127 Ok(AprsMessage {
128 to: default_callsign(),
129 addressee: b"DESTINATI".to_vec(),
130 id: Some(vec![]),
131 text: b"Hello World! This msg has a : colon ".to_vec()
132 })
133 );
134 }
135
136 #[test]
137 fn parse_message_no_id() {
138 let result = AprsMessage::decode(
139 r"DESTINATI:Hello World! This msg has a : colon ".as_bytes(),
140 default_callsign(),
141 );
142
143 assert_eq!(
144 result,
145 Ok(AprsMessage {
146 to: default_callsign(),
147 addressee: b"DESTINATI".to_vec(),
148 id: None,
149 text: b"Hello World! This msg has a : colon ".to_vec()
150 })
151 );
152 }
153}