location_parser/
parse.rs

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}