#![forbid(unsafe_code)]
#![deny(
missing_docs,
clippy::missing_docs_in_private_items
)]
use std::{
fmt,
fmt::{Display, Formatter},
};
pub mod formats;
pub mod resolvers;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "format_any")]
use std::str::FromStr;
#[cfg(any(feature = "format_dd", feature = "format_dms", feature = "resolve_osm"))]
use std::num::ParseFloatError;
use thiserror::Error;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct Coordinate {
pub lat: f64,
pub lng: f64,
}
impl Coordinate {
pub fn new(lat: f64, lng: f64) -> Self {
Self { lat, lng }
}
}
impl Display for Coordinate {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{},{}", self.lat, self.lng)
}
}
#[derive(Debug, Error)]
pub enum CoordinateError {
#[error("No parser available - enable them via features")]
MissingParser,
#[error("Value can't be converted into a coordinate")]
InvalidValue,
#[cfg(any(
feature = "format_dd",
feature = "format_dms",
feature = "format_geohash"
))]
#[error("String passed into from_str was malformed")]
Malformed,
#[cfg(any(feature = "format_dd", feature = "format_dms", feature = "resolve_osm"))]
#[error("String passed into from_str contained invalid floats")]
ParseFloatError(#[from] ParseFloatError),
#[cfg(feature = "resolve_osm")]
#[error("Location not resolvable")]
Unresolveable,
#[cfg(feature = "resolve_osm")]
#[error("There was a problem connecting to the API")]
ReqwestError(#[from] reqwest::Error),
}
impl TryFrom<(f64, f64)> for Coordinate {
type Error = CoordinateError;
fn try_from(tupl_coord: (f64, f64)) -> Result<Self, Self::Error> {
match tupl_coord {
(lat, lng) if (-90.0..=90.0).contains(&lat) && (-180.0..=180.0).contains(&lng) => {
Ok(Self { lat, lng })
}
_ => Err(CoordinateError::InvalidValue),
}
}
}
#[cfg(feature = "format_any")]
impl FromStr for Coordinate {
type Err = CoordinateError;
fn from_str(str_coords: &str) -> Result<Self, Self::Err> {
let mut result: Result<Coordinate, CoordinateError> = Err(CoordinateError::MissingParser);
#[cfg(feature = "format_dd")]
{
result = result
.or_else(|_| formats::dd::DDCoordinate::from_str(str_coords).map(Coordinate::from));
}
#[cfg(feature = "format_dms")]
{
result = result.or_else(|_| {
formats::dms::DMSCoordinate::from_str(str_coords).map(Coordinate::from)
});
}
#[cfg(feature = "format_geohash")]
{
result = result
.or_else(|_| formats::geohash::Geohash::from_str(str_coords).map(Coordinate::from));
}
result
}
}