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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! A parser crate for WTE MT-RX-3 AIS, 406 + 121.5 device messages.
//! See [`ParsedMessage`] for available formats.
//!

use std::num::ParseIntError;

use mt_raw::MtRaw;
use mt_structured::MtStructured;
use rss::Rss;
use thiserror::Error;

pub mod mt_raw;
pub mod mt_structured;
pub mod rss;

/// Represents an error when parsing a message went wrong.
#[derive(Error, Clone, Debug, PartialEq)]
pub enum ParseError {
    #[error("failed to parse number")]
    ParseIntError(#[from] ParseIntError),

    #[error("invalid message size (expected {expected:?}, found {found:?})")]
    SizeNotMatch { expected: usize, found: usize },

    #[error("invalid sentence, not parsable")]
    Invalid(),
}

/// Represents the parsed message.
#[derive(Clone, Debug, PartialEq)]
pub enum ParsedMessage {
    /// RSS “Received Signal Strength".
    Rss(Rss),

    /// MT Serial Out Packet Format
    MtStructured(MtStructured),

    /// MT Raw Data Serial Out Packet Format.
    MtRaw(MtRaw),

    /// Invalid message.
    Invalid(),
}

/// Tries to parses `message` into one of [`ParsedMessage`] types.
/// Returns [`ParsedMessage::Invalid`] if it's an invalid message, or [`ParseError`] if parsing went wrong.
pub fn parse(message: &str) -> Result<ParsedMessage, ParseError> {
    let parsed = match message.trim() {
        msg if Rss::is_rss(msg) => ParsedMessage::Rss(Rss::parse(msg)?),
        msg if MtStructured::is_mt(msg) => ParsedMessage::MtStructured(MtStructured::parse(msg)?),
        msg if MtRaw::is_mt(msg) => ParsedMessage::MtRaw(MtRaw::parse(msg)?),
        _ => ParsedMessage::Invalid(),
    };
    Ok(parsed)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn mt_structured() {
        assert!(parse("MT1001000AL400C592753572B323433212S1723756E4706").is_ok());
        assert!(parse("MT1001000AL400C592753572B323433212S1723756E4706").is_ok());
    }

    #[test]
    fn mt_raw() {
        assert!(parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79F84B").is_ok());
        assert!(parse("MT6001001FFFE2FA0062C93A9AB959E55EE7788C71B791131").is_ok());
    }

    #[test]
    fn invalid_mt() {
        // invalid headers
        let invalid_samples = vec![
            "MT2001000AL400C592753572B323433212S1723756E4706",
            "MT3001000AL400C592753572B323433212S1723756E4706",
            "MT4001000AL400C592753572B323433212S1723756E4706",
            "MT5001000AL400C592753572B323433212S1723756E4706",
            "MT8001000AL400C592753572B323433212S1723756E4706",
            "MT9001000AL400C592753572B323433212S1723756E4706",
            "MT0001000AL400C592753572B323433212S1723756E4706",
        ];
        for s in invalid_samples {
            assert_eq!(parse(s).unwrap(), ParsedMessage::Invalid());
        }

        // smaller than expected
        assert!(parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B").is_err());
        assert!(parse("MT1001000AL400C592753572B323433212S172375").is_err());

        // MT1 UUU NNN T F HHHHHHHHHHHHHHH SS 11 22 33 N 444 55 66 W YYYY
        // MT6 UUU NNN RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR YYYY

        // not parsable numbers

        // unparsable NNN
        assert!(parse("MT1001aaaAL400C592753572B323433212S1723756E4706").is_err());
        assert!(parse("MT6001aaaFFFE2FA00E0000CBAB959DB0903788C71B79F84B").is_err());

        // unparsable 11 22 33
        assert!(parse("MT1001000AL400C592753572B323aa3212S1723756E4706").is_err());
        assert!(parse("MT1001000AL400C592753572B32343bb12S1723756E4706").is_err());
        assert!(parse("MT1001000AL400C592753572B3234332ccS1723756E4706").is_err());

        // unparsable 444 55 66
        assert!(parse("MT1001000AL400C592753572B323433212Saaa3756E4706").is_err());
        assert!(parse("MT1001000AL400C592753572B323433212S172bb56E4706").is_err());
        assert!(parse("MT1001000AL400C592753572B323433212S17237ccE4706").is_err());

        // unparsable checksum
        assert!(parse("MT1001000AL400C592753572B323433212S1723756EZZZZ").is_err());
        assert!(parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79ZZZZ").is_err());
    }

    #[test]
    fn rss() {
        assert!(parse("SS,A,123\n").is_ok());
        assert!(parse("SS,1,123\n").is_ok());
    }

    #[test]
    fn invalid_rss() {
        assert!(parse("SS,1,666\n").is_err()); // nnn too big
        assert!(parse("SS,1,12367\n").is_err()); // nnn too big
        assert!(parse("SS,2,123\n").is_err()); // invalid type
        assert!(parse("SS,A,1234\n").is_err()); // nnn too big
        assert!(parse("SS,X,123\n").is_err()); // invalid type
    }

    #[test]
    fn hardcoded_checksum() {
        assert_eq!(
            MtRaw::calculate_checksum("FFFE2FA00E0000CBAB959DB0903788C71B79".as_bytes()),
            0xf84b
        );
    }

    #[test]
    fn mt_raw_checksum() {
        if let Ok(ParsedMessage::MtRaw(v)) =
            parse("MT6001001FFFE2FA00E0000CBAB959DB0903788C71B79F84B")
        {
            assert_eq!(MtRaw::calculate_checksum(&v.data), v.checksum);
        }
    }
}