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
use chrono::{DateTime, NaiveDateTime, Utc};
use nom::simple_errors::Context;
use nom::{
call, error_position, float, map_res, multispace, tag, take_until, take_until_and_consume, Err,
ErrorKind, IResult,
};
use std::str::from_utf8;
#[derive(Debug, PartialEq)]
pub struct GeoRecord {
lon: f32,
lat: f32,
time: i64,
max_error: u32,
}
fn parse_coord(input: &[u8]) -> IResult<&[u8], f32> {
let (input, _) = multispace(input)?;
let (input, d) = float(input)?;
let (input, _) = multispace(input)?;
let (input, m) = float(input)?;
let (input, _) = multispace(input)?;
let (input, s) = float(input)?;
Ok((input, d + m / 60.0 + s / 3600.0))
}
fn parse_date(input: &[u8]) -> IResult<&[u8], i64> {
let (input, _) = take_until_and_consume!(input, "TIME:")?;
let (input, date) = map_res!(input, take_until!("\n"), from_utf8)?;
let dt = DateTime::<Utc>::from_utc(
NaiveDateTime::parse_from_str(date, "%d %m %Y %H:%M:%S")
.or_else(|_| Err(Err::Error(Context::Code(input, ErrorKind::Tag))))?,
Utc,
);
Ok((input, dt.timestamp()))
}
impl GeoRecord {
pub fn parse(input: &[u8]) -> IResult<&[u8], GeoRecord> {
let (input, _) = take_until_and_consume!(input, "GU")?;
let (input, _) = take_until_and_consume!(input, "N:")?;
let (input, n) = parse_coord(input)?;
let (input, _) = take_until_and_consume!(input, "W:")?;
let (input, w) = parse_coord(input)?;
let (input, time) = parse_date(input)?;
let (input, _) = take_until_and_consume!(input, "ERR: < ")?;
let (input, max_error) = float(input)?;
let max_error = max_error as u32;
let (input, _) = multispace(input)?;
let (input, unit) = take_until!(input, "\n")?;
let (input, _) = multispace(input)?;
let (input, _) = tag!(input, "OK")?;
let (input, _) = multispace(input)?;
println!("unit {:?}", unit);
let max_error = match unit {
b"km" => max_error * 1000,
_ => max_error,
};
Ok((
input,
GeoRecord {
lon: -w,
lat: n,
time,
max_error,
},
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_parses() {
assert_eq!(
Ok((
&b"extra"[..],
GeoRecord {
lat: 40.482502,
lon: -85.49389,
time: 1514898642,
max_error: 5000,
},
)),
GeoRecord::parse(b"GU\nN: 040 28 57\nW: 085 29 38\nTIME: 02 01 2018 13:10:42\nERR: < 5 km\n\nOK\n extra")
)
}
}