use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Point {
latitude: f64,
longitude: f64,
}
impl Point {
pub fn new(latitude: f64, longitude: f64) -> Result<Self, InvalidPointError> {
if (-90.0..=90.0).contains(&latitude) && (-180.0..=180.0).contains(&longitude) {
Ok(Self {
latitude,
longitude,
})
} else {
Err(InvalidPointError::CoordinatesOutOfRange {
latitude,
longitude,
})
}
}
pub fn latitude(&self) -> f64 {
self.latitude
}
pub fn longitude(&self) -> f64 {
self.longitude
}
}
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{},{}", self.latitude, self.longitude)
}
}
impl TryFrom<(f64, f64)> for Point {
type Error = InvalidPointError;
fn try_from((latitude, longitude): (f64, f64)) -> Result<Self, Self::Error> {
Self::new(latitude, longitude)
}
}
#[derive(thiserror::Error, Debug)]
pub enum InvalidPointError {
#[error("bad format: {0:?}")]
BadFormat(String),
#[error("coordinates out of range: {latitude} latitude, {longitude} longitude")]
CoordinatesOutOfRange {
latitude: f64,
longitude: f64,
},
}
impl FromStr for Point {
type Err = InvalidPointError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match {
let mut i = s.split(',').map(str::trim).map(f64::from_str);
(i.next(), i.next(), i.next())
} {
(Some(Ok(latitude)), Some(Ok(longitude)), None) => Point::new(latitude, longitude),
_ => Err(InvalidPointError::BadFormat(s.into())),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Polygon(Vec<Point>);
impl IntoIterator for Polygon {
type Item = Point;
type IntoIter = std::vec::IntoIter<Point>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a Polygon {
type Item = &'a Point;
type IntoIter = std::slice::Iter<'a, Point>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl Polygon {
pub fn iter(&self) -> impl Iterator<Item = &Point> {
self.0.iter()
}
pub(crate) fn deserialize_optional<'de, D>(deserializer: D) -> Result<Vec<Polygon>, D::Error>
where
D: Deserializer<'de>,
{
let strs = <Vec<std::borrow::Cow<str>>>::deserialize(deserializer)?;
strs.into_iter()
.filter(|s| !s.is_empty())
.map(|s| Polygon::from_str(&s))
.collect::<Result<Vec<_>, _>>()
.map_err(D::Error::custom)
}
}
impl std::fmt::Display for Polygon {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
for (i, point) in self.0.iter().enumerate() {
if i > 0 {
write!(f, " {}", point)?;
} else {
write!(f, "{}", point)?;
}
}
Ok(())
}
}
impl Serialize for Polygon {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Polygon {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <std::borrow::Cow<str>>::deserialize(deserializer)?;
str.parse().map_err(D::Error::custom)
}
}
impl TryFrom<Vec<Point>> for Polygon {
type Error = InvalidPolygonError;
fn try_from(value: Vec<Point>) -> Result<Self, Self::Error> {
if value.len() <= 3 {
Err(InvalidPolygonError::TooFewPoints(value.len()))
} else if !(value.first() == value.last()) {
Err(InvalidPolygonError::ShapeNotClosed(
*value.first().unwrap(),
*value.last().unwrap(),
))
} else {
Ok(Self(value))
}
}
}
#[derive(thiserror::Error, Debug)]
pub enum InvalidPolygonError {
#[error("polygon contains too few points: got {0} vs 4 minimum")]
TooFewPoints(
usize,
),
#[error("shape not closed: first point {0} != last point {0}")]
ShapeNotClosed(
Point,
Point,
),
#[error("polygon contains invalid point: {0}")]
InvalidPoint(#[from] InvalidPointError),
}
impl FromStr for Polygon {
type Err = InvalidPolygonError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(
s.split_whitespace()
.map(Point::from_str)
.collect::<Result<Vec<Point>, _>>()?,
)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Circle {
pub center: Point,
pub radius: f64,
}
impl Circle {
pub fn new(center: Point, radius: f64) -> Result<Self, InvalidCircleError> {
if (0.0..20000.0).contains(&radius) {
Ok(Self { center, radius })
} else {
Err(InvalidCircleError::RadiusTooLarge(radius))
}
}
}
impl std::fmt::Display for Circle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{},{} {}",
self.center.latitude(),
self.center.longitude(),
self.radius
)
}
}
impl Serialize for Circle {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Circle {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let str = <std::borrow::Cow<str>>::deserialize(deserializer)?;
str.parse().map_err(D::Error::custom)
}
}
#[derive(thiserror::Error, Debug)]
pub enum InvalidCircleError {
#[error("unparseable circle string: {0:?}")]
UnparseableString(String),
#[error("circle center point is invalid: {0}")]
InvalidCenterPoint(#[from] InvalidPointError),
#[error("circle radius is too large: {0} km")]
RadiusTooLarge(f64),
}
impl FromStr for Circle {
type Err = InvalidCircleError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (center, radius) = match {
let mut i = s.split_whitespace();
(
i.next(),
i.next().and_then(|s| s.trim().parse().ok()),
i.next(),
)
} {
(Some(center), Some(radius), None) => Ok((center, radius)),
_ => Err(InvalidCircleError::UnparseableString(s.into())),
}?;
let center = center.parse()?;
Self::new(center, radius)
}
}