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
use crate::*; use pest::{iterators::Pair, Parser}; use std::ops::RangeBounds; #[derive(Parser)] #[grammar = "location_formats.pest"] struct PestParser; fn check_range<R: RangeBounds<f64>>(value: f64, range: R) -> Result<(), Error> { if value.is_nan() || !range.contains(&value) { Err(Error::OutOfRange) } else { Ok(()) } } fn signed_decimal_degrees_to_f64(pair: Pair<Rule>) -> f64 { let signed_decimal_degrees = pair.into_inner().next().unwrap(); let signed_float = signed_decimal_degrees.into_inner(); let mut sign = 1.0; for sign_float in signed_float { match sign_float.as_rule() { Rule::sign => { if "-" == sign_float.as_str() { sign = -1.0 } } Rule::float => { let f: f64 = sign_float.as_str().parse().unwrap(); return sign * f; } _ => unreachable!(), } } unreachable!(); } fn parse(rule: Rule, s: &str) -> Result<Pair<Rule>, Error> { Ok(PestParser::parse(rule, s) .map_err(|_| Error::InvalidFormat)? .next() .unwrap()) } pub fn parse_latitude(s: &str) -> Result<f64, Error> { let parse = parse(Rule::latitude, s)?; let lat = match parse.as_rule() { Rule::signed_decimal_degrees => signed_decimal_degrees_to_f64(parse), _ => unreachable!(), }; check_range(lat, -90.0..=90.0)?; Ok(lat) } pub fn parse_longitude(s: &str) -> Result<f64, Error> { let parse = parse(Rule::longitude, s)?; let lon = match parse.as_rule() { Rule::signed_decimal_degrees => signed_decimal_degrees_to_f64(parse), _ => unreachable!(), }; check_range(lon, -180.0..=180.0)?; Ok(lon) } pub fn parse_location(s: &str) -> Result<(f64, f64), Error> { let parse = parse(Rule::location, s)?; let (lat, lon) = match parse.as_rule() { Rule::location_signed_decimal_degrees => { let mut location_sdd = parse.into_inner(); let lat_sdd = location_sdd.next().unwrap(); let lon_sdd = location_sdd.next().unwrap(); ( signed_decimal_degrees_to_f64(lat_sdd), signed_decimal_degrees_to_f64(lon_sdd), ) } _ => unreachable!(), }; check_range(lon, -180.0..=180.0)?; check_range(lat, -90.0..=90.0)?; Ok((lat, lon)) }