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
use std::fmt;

use crate::util::ParseError;

#[derive(Debug, PartialEq, Eq)]
pub enum GpsQualifier {
    Gps,
    DGps,
}

/// Differential GPS record - indicates that Differential GPS is being used.
#[derive(Debug, PartialEq, Eq)]
pub struct DRecord<'a> {
    pub qualifier: GpsQualifier,
    pub station_id: &'a str,
}

impl<'a> DRecord<'a> {
    pub fn parse(line: &'a str) -> Result<Self, ParseError> {
        if line.len() != 6 {
            return Err(ParseError::SyntaxError);
        }

        let bytes = line.as_bytes();
        assert_eq!(bytes[0], b'D');

        let qualifier = match bytes[1] {
            b'1' => GpsQualifier::Gps,
            b'2' => GpsQualifier::DGps,
            _ => return Err(ParseError::SyntaxError),
        };

        let station_id = &line[2..6];

        Ok(DRecord {
            qualifier,
            station_id,
        })
    }
}

impl<'a> fmt::Display for DRecord<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let qual_str = match self.qualifier {
            GpsQualifier::Gps => '1',
            GpsQualifier::DGps => '2',
        };

        write!(f, "D{}{}", qual_str, self.station_id)
    }
}

#[cfg(test)]
mod tests {
    use super::{DRecord, GpsQualifier};

    #[test]
    fn drecord_parse() {
        let example_line = "D1ABCD";
        let parsed = DRecord::parse(example_line).unwrap();
        let expected = DRecord {
            qualifier: GpsQualifier::Gps,
            station_id: "ABCD",
        };

        assert_eq!(parsed, expected);
    }

    #[test]
    fn drecord_format() {
        let expected_string = "D1ABCD";
        let record = DRecord {
            qualifier: GpsQualifier::Gps,
            station_id: "ABCD",
        };

        assert_eq!(format!("{}", record), expected_string);
    }

    proptest! {
        #[test]
        #[allow(unused_must_use)]
        fn parse_doesnt_crash(s in "D\\PC*") {
            DRecord::parse(&s);
        }
    }
}