use crate::ParseError;
#[derive(Clone, Debug, PartialEq)]
pub enum CardinalDirection {
North,
South,
West,
East,
Unknown,
}
impl std::convert::From<char> for CardinalDirection {
fn from(c: char) -> Self {
match c {
'W' => CardinalDirection::West,
'E' => CardinalDirection::East,
'N' => CardinalDirection::North,
'S' => CardinalDirection::South,
_ => CardinalDirection::Unknown,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum MtMessageType {
Test,
Alert,
Unknown,
}
impl std::convert::From<char> for MtMessageType {
fn from(c: char) -> Self {
match c {
'T' => MtMessageType::Test,
'A' => MtMessageType::Alert,
_ => MtMessageType::Unknown,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct MtStructured {
pub header: String,
pub id: String,
pub sequence_number: usize,
pub message_type: MtMessageType,
pub format_flag: char,
pub beacon: String,
pub signal_strength: String,
pub lat_degrees: Option<u8>,
pub lat_minutes: Option<u8>,
pub lat_seconds: Option<u8>,
pub lat_direction: CardinalDirection,
pub long_degrees: Option<u16>,
pub long_minutes: Option<u8>,
pub long_seconds: Option<u8>,
pub long_direction: CardinalDirection,
pub checksum: u16,
}
pub fn is_mt(message: &str) -> bool {
return message.starts_with("MT1");
}
pub fn parse(message: &str) -> Result<MtStructured, ParseError> {
const MT1_LEN: usize = 47;
if message.len() != MT1_LEN {
return Err(ParseError::SizeNotMatch {
expected: MT1_LEN,
found: message.len(),
});
}
let header = message[0..3].to_string();
let id = message[3..6].to_string();
let sequence_number = message[6..9].parse::<usize>()?;
let message_type = (message.as_bytes()[9] as char).into();
let format_flag = message.as_bytes()[10] as char;
let beacon = message[11..26].to_string();
let signal_strength = message[26..28].to_string();
let lat_degrees = message[28..30].parse::<u8>().ok();
let lat_minutes = message[30..32].parse::<u8>().ok();
let lat_seconds = message[32..34].parse::<u8>().ok();
let lat_direction = (message.as_bytes()[34] as char).into();
let long_degrees = message[35..38].parse::<u16>().ok();
let long_minutes = message[38..40].parse::<u8>().ok();
let long_seconds = message[40..42].parse::<u8>().ok();
let long_direction = (message.as_bytes()[42] as char).into();
let checksum = u16::from_str_radix(&message[43..47], 16).unwrap_or(0);
let result = MtStructured {
header,
id,
sequence_number,
message_type,
format_flag,
beacon,
signal_strength,
lat_degrees,
lat_minutes,
lat_seconds,
lat_direction,
long_degrees,
long_minutes,
long_seconds,
long_direction,
checksum,
};
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fields() {
let parsed = parse("MT1001000AL400C592753572B323433212S1723756E4706").unwrap();
assert_eq!(parsed.id, "001");
assert_eq!(parsed.sequence_number, 0);
assert_eq!(parsed.message_type, MtMessageType::Alert);
assert_eq!(parsed.format_flag, 'L');
assert_eq!(parsed.beacon, "400C592753572B3");
assert_eq!(parsed.signal_strength, "23");
}
#[test]
fn valid_location_and_direction() {
let parsed = parse("MT1001000AL400C592753572B323433212S1723756E4706").unwrap();
assert_eq!(parsed.lat_degrees.unwrap(), 43);
assert_eq!(parsed.lat_minutes.unwrap(), 32);
assert_eq!(parsed.lat_seconds.unwrap(), 12);
assert_eq!(parsed.lat_direction, CardinalDirection::South);
assert_eq!(parsed.long_degrees.unwrap(), 172);
assert_eq!(parsed.long_minutes.unwrap(), 37);
assert_eq!(parsed.long_seconds.unwrap(), 56);
assert_eq!(parsed.long_direction, CardinalDirection::East);
}
#[test]
fn unknown_location() {
let parsed = parse("MT1001000AL400C592753572B323433212S-------E4706").unwrap();
assert!(parsed.long_degrees.is_none());
assert!(parsed.long_minutes.is_none());
assert!(parsed.long_seconds.is_none());
let parsed = parse("MT1001000AL400C592753572B323------S1723756E4706").unwrap();
assert!(parsed.lat_degrees.is_none());
assert!(parsed.lat_minutes.is_none());
assert!(parsed.lat_seconds.is_none());
}
}