use wbase::num;
use wresp::equals_ignore_case;
use crate::geo::{GeoDistanceUnitType, GeoHash};
#[inline]
pub fn try_parse_with_infinity(v: &[u8]) -> Option<f64> {
let mut value = 0.0;
num::try_parse_with_infinity(v, &mut value).then_some(value)
}
#[inline]
pub fn try_get_geo_distance_unit(v: &[u8]) -> Option<GeoDistanceUnitType> {
match v.len() {
1 => {
if equals_ignore_case(v, b"M") {
Some(GeoDistanceUnitType::M)
} else {
None
}
}
2 => {
if equals_ignore_case(v, b"KM") {
Some(GeoDistanceUnitType::Km)
} else if equals_ignore_case(v, b"MI") {
Some(GeoDistanceUnitType::Mi)
} else if equals_ignore_case(v, b"FT") {
Some(GeoDistanceUnitType::Ft)
} else {
None
}
}
_ => None,
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GeoLonLatError {
NotFloat,
OutOfRange(f64, f64),
}
#[inline]
pub fn try_get_geo_lon_lat(lon: &[u8], lat: &[u8]) -> Result<(f64, f64), GeoLonLatError> {
let Some(longitude) = try_parse_with_infinity(lon) else {
return Err(GeoLonLatError::NotFloat);
};
let Some(latitude) = try_parse_with_infinity(lat) else {
return Err(GeoLonLatError::NotFloat);
};
if !(geo_longitude_in_range(longitude) && geo_latitude_in_range(latitude)) {
return Err(GeoLonLatError::OutOfRange(longitude, latitude));
}
Ok((longitude, latitude))
}
#[inline]
fn geo_longitude_in_range(lon: f64) -> bool {
(GeoHash::LONGITUDE_MIN..=GeoHash::LONGITUDE_MAX).contains(&lon)
}
#[inline]
fn geo_latitude_in_range(lat: f64) -> bool {
(GeoHash::LATITUDE_MIN..=GeoHash::LATITUDE_MAX).contains(&lat)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn infinity_forms() {
assert_eq!(try_parse_with_infinity(b"inf"), Some(f64::INFINITY));
assert_eq!(try_parse_with_infinity(b"INF"), Some(f64::INFINITY));
assert_eq!(try_parse_with_infinity(b"+inf"), Some(f64::INFINITY));
assert_eq!(try_parse_with_infinity(b"-INF"), Some(f64::NEG_INFINITY));
assert_eq!(try_parse_with_infinity(b"+Inf"), Some(f64::INFINITY));
assert_eq!(try_parse_with_infinity(b"-inf"), Some(f64::NEG_INFINITY));
assert_eq!(try_parse_with_infinity(b"nan"), None);
}
#[test]
fn geo_lon_lat_reports_two_failure_kinds() {
assert_eq!(
try_get_geo_lon_lat(b"13.361389", b"38.115556"),
Ok((13.361_389, 38.115_556))
);
assert_eq!(
try_get_geo_lon_lat(b"abc", b"38.0"),
Err(GeoLonLatError::NotFloat)
);
assert_eq!(
try_get_geo_lon_lat(b"nan", b"38.0"),
Err(GeoLonLatError::NotFloat)
);
assert_eq!(
try_get_geo_lon_lat(b"181.0", b"38.0"),
Err(GeoLonLatError::OutOfRange(181.0, 38.0))
);
assert_eq!(
try_get_geo_lon_lat(b"13.0", b"-90.1"),
Err(GeoLonLatError::OutOfRange(13.0, -90.1))
);
assert_eq!(
try_get_geo_lon_lat(b"inf", b"38.0"),
Err(GeoLonLatError::OutOfRange(f64::INFINITY, 38.0))
);
}
}