use std::io::Write;
use Callsign;
use DecodeError;
use EncodeError;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AprsMessage {
pub to: Callsign,
pub addressee: Vec<u8>,
pub text: Vec<u8>,
pub id: Option<Vec<u8>>,
}
impl AprsMessage {
pub fn decode(b: &[u8], to: Callsign) -> Result<Self, DecodeError> {
let mut splitter = b.splitn(2, |x| *x == b':');
let mut addressee = match splitter.next() {
Some(x) => x.to_vec(),
None => {
return Err(DecodeError::InvalidMessageDestination(vec![]));
}
};
if addressee.len() != 9 {
return Err(DecodeError::InvalidMessageDestination(addressee.to_owned()));
}
trim_spaces_end(&mut addressee);
let text = splitter.next().unwrap_or(&[]);
let mut text_splitter = text.splitn(2, |x| *x == b'{');
let text = text_splitter.next().unwrap_or(&[]).to_vec();
let id = text_splitter.next().map(|x| x.to_vec());
Ok(Self {
to,
addressee,
text,
id,
})
}
pub fn encode<W: Write>(&self, buf: &mut W) -> Result<(), EncodeError> {
if self.addressee.len() > 9 {
return Err(EncodeError::InvalidMessageAddressee(
self.addressee.to_owned(),
));
}
buf.write_all(b":")?;
buf.write_all(&self.addressee)?;
for _ in self.addressee.len()..9 {
buf.write_all(b" ")?;
}
buf.write_all(b":")?;
buf.write_all(&self.text)?;
if let Some(id) = &self.id {
buf.write_all(b"{")?;
buf.write_all(id)?;
}
Ok(())
}
}
fn trim_spaces_end(arr: &mut Vec<u8>) {
let space_count = arr.iter().rev().take_while(|&&b| b == b' ').count();
arr.truncate(arr.len() - space_count);
}
#[cfg(test)]
mod tests {
use super::*;
fn default_callsign() -> Callsign {
Callsign::new_no_ssid("VE9")
}
#[test]
fn parse_message_invalid_dest() {
let result = AprsMessage::decode(
&b"DEST :Hello World! This msg has a : colon {32975"[..],
default_callsign(),
);
assert_eq!(
result,
Err(DecodeError::InvalidMessageDestination(b"DEST ".to_vec()))
);
}
#[test]
fn parse_message_id() {
let result = AprsMessage::decode(
r"DESTINATI:Hello World! This msg has a : colon {329A7D5Z4".as_bytes(),
default_callsign(),
);
assert_eq!(
result,
Ok(AprsMessage {
to: default_callsign(),
addressee: b"DESTINATI".to_vec(),
id: Some(b"329A7D5Z4".to_vec()),
text: b"Hello World! This msg has a : colon ".to_vec()
})
);
}
#[test]
fn parse_message_empty_id() {
let result = AprsMessage::decode(
r"DESTINATI:Hello World! This msg has a : colon {".as_bytes(),
default_callsign(),
);
assert_eq!(
result,
Ok(AprsMessage {
to: default_callsign(),
addressee: b"DESTINATI".to_vec(),
id: Some(vec![]),
text: b"Hello World! This msg has a : colon ".to_vec()
})
);
}
#[test]
fn parse_message_no_id() {
let result = AprsMessage::decode(
r"DESTINATI:Hello World! This msg has a : colon ".as_bytes(),
default_callsign(),
);
assert_eq!(
result,
Ok(AprsMessage {
to: default_callsign(),
addressee: b"DESTINATI".to_vec(),
id: None,
text: b"Hello World! This msg has a : colon ".to_vec()
})
);
}
}