1use crate::*;
2use pest::{iterators::Pair, Parser};
3use std::ops::RangeBounds;
4
5#[derive(Parser)]
6#[grammar = "location_formats.pest"]
7struct PestParser;
8
9fn check_range<R: RangeBounds<f64>>(value: f64, range: R) -> Result<(), Error> {
10 if value.is_nan() || !range.contains(&value) {
11 Err(Error::OutOfRange)
12 } else {
13 Ok(())
14 }
15}
16
17fn signed_decimal_degrees_to_f64(pair: Pair<Rule>) -> f64 {
18 let signed_decimal_degrees = pair.into_inner().next().unwrap();
19 let signed_float = signed_decimal_degrees.into_inner();
20
21 let mut sign = 1.0;
22
23 for sign_float in signed_float {
24 match sign_float.as_rule() {
25 Rule::sign => {
26 if "-" == sign_float.as_str() {
27 sign = -1.0
28 }
29 }
30 Rule::float => {
31 let f: f64 = sign_float.as_str().parse().unwrap();
32 return sign * f;
33 }
34 _ => unreachable!(),
35 }
36 }
37 unreachable!();
38}
39
40fn parse(rule: Rule, s: &str) -> Result<Pair<Rule>, Error> {
41 Ok(PestParser::parse(rule, s)
42 .map_err(|_| Error::InvalidFormat)?
43 .next()
44 .unwrap())
45}
46
47pub fn parse_latitude(s: &str) -> Result<f64, Error> {
48 let parse = parse(Rule::latitude, s)?;
49
50 let lat = match parse.as_rule() {
51 Rule::signed_decimal_degrees => signed_decimal_degrees_to_f64(parse),
52 _ => unreachable!(),
53 };
54
55 check_range(lat, -90.0..=90.0)?;
56 Ok(lat)
57}
58
59pub fn parse_longitude(s: &str) -> Result<f64, Error> {
60 let parse = parse(Rule::longitude, s)?;
61
62 let lon = match parse.as_rule() {
63 Rule::signed_decimal_degrees => signed_decimal_degrees_to_f64(parse),
64 _ => unreachable!(),
65 };
66
67 check_range(lon, -180.0..=180.0)?;
68 Ok(lon)
69}
70
71pub fn parse_location(s: &str) -> Result<(f64, f64), Error> {
72 let parse = parse(Rule::location, s)?;
73
74 let (lat, lon) = match parse.as_rule() {
75 Rule::location_signed_decimal_degrees => {
76 let mut location_sdd = parse.into_inner();
77 let lat_sdd = location_sdd.next().unwrap();
78 let lon_sdd = location_sdd.next().unwrap();
79 (
80 signed_decimal_degrees_to_f64(lat_sdd),
81 signed_decimal_degrees_to_f64(lon_sdd),
82 )
83 }
84 _ => unreachable!(),
85 };
86
87 check_range(lon, -180.0..=180.0)?;
88 check_range(lat, -90.0..=90.0)?;
89
90 Ok((lat, lon))
91}