diff --git a/geo-types/CHANGES.md b/geo-types/CHANGES.md index 58f97e400..87fd0be6c 100644 --- a/geo-types/CHANGES.md +++ b/geo-types/CHANGES.md @@ -4,6 +4,8 @@ * Implement `Default` on `Coordinate` and `Point` structs (defaults to `(x: 0, y: 0)`) * +* Add specific details about conversion failures in the newly public `geo_types::Error` + * ## 0.7.0 diff --git a/geo-types/src/error.rs b/geo-types/src/error.rs new file mode 100644 index 000000000..1763331c9 --- /dev/null +++ b/geo-types/src/error.rs @@ -0,0 +1,44 @@ +use std::fmt; + +#[derive(Debug)] +pub enum Error { + MismatchedGeometry { + expected: &'static str, + found: &'static str, + }, +} + +impl std::error::Error for Error {} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::MismatchedGeometry { expected, found } => { + write!(f, "Expected a {}, but found a {}", expected, found) + } + } + } +} + +#[cfg(test)] +mod test { + use crate::{Geometry, Point, Rect}; + use std::convert::TryFrom; + + #[test] + fn error_output() { + let point = Point::new(1.0, 2.0); + let point_geometry = Geometry::from(point); + + let rect = Rect::new(Point::new(1.0, 2.0), Point::new(3.0, 4.0)); + let rect_geometry = Geometry::from(rect); + + Point::try_from(point_geometry).expect("failed to unwrap inner enum Point"); + + let failure = Point::try_from(rect_geometry).unwrap_err(); + assert_eq!( + format!("{}", failure), + "Expected a geo_types::point::Point, but found a geo_types::rect::Rect" + ); + } +} diff --git a/geo-types/src/geometry.rs b/geo-types/src/geometry.rs index 324962e2d..fa2dba775 100644 --- a/geo-types/src/geometry.rs +++ b/geo-types/src/geometry.rs @@ -1,10 +1,9 @@ use crate::{ - CoordNum, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, - Point, Polygon, Rect, Triangle, + CoordNum, Error, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, + MultiPolygon, Point, Polygon, Rect, Triangle, }; +use core::any::type_name; use std::convert::TryFrom; -use std::error::Error; -use std::fmt; /// An enum representing any possible geometry type. /// @@ -182,94 +181,56 @@ impl Geometry { } } -#[derive(Debug)] -pub struct FailedToConvertError; - -impl fmt::Display for FailedToConvertError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Could not convert from enum member to concrete type") - } -} - -impl Error for FailedToConvertError { - fn description(&self) -> &str { - "Could not convert from enum member to concrete type" - } -} - -impl TryFrom> for Point { - type Error = FailedToConvertError; - - fn try_from(geom: Geometry) -> Result, Self::Error> { - match geom { - Geometry::Point(p) => Ok(p), - _ => Err(FailedToConvertError), +#[macro_use] +macro_rules! try_from_geometry_impl { + ($($type: ident),+) => { + $( + /// Convert a Geometry enum into its inner type. + /// + /// Fails if the enum case does not match the type you are trying to convert it to. + impl TryFrom> for $type { + type Error = Error; + + fn try_from(geom: Geometry) -> Result { + match geom { + Geometry::$type(g) => Ok(g), + other => Err(Error::MismatchedGeometry { + expected: type_name::<$type>(), + found: inner_type_name(other) + }) + } + } } + )+ } } -impl TryFrom> for Line { - type Error = FailedToConvertError; +try_from_geometry_impl!( + Point, + Line, + LineString, + Polygon, + MultiPoint, + MultiLineString, + MultiPolygon, + Rect, + Triangle +); - fn try_from(geom: Geometry) -> Result, Self::Error> { - match geom { - Geometry::Line(l) => Ok(l), - _ => Err(FailedToConvertError), - } - } -} - -impl TryFrom> for LineString { - type Error = FailedToConvertError; - - fn try_from(geom: Geometry) -> Result, Self::Error> { - match geom { - Geometry::LineString(ls) => Ok(ls), - _ => Err(FailedToConvertError), - } - } -} - -impl TryFrom> for Polygon { - type Error = FailedToConvertError; - - fn try_from(geom: Geometry) -> Result, Self::Error> { - match geom { - Geometry::Polygon(ls) => Ok(ls), - _ => Err(FailedToConvertError), - } - } -} - -impl TryFrom> for MultiPoint { - type Error = FailedToConvertError; - - fn try_from(geom: Geometry) -> Result, Self::Error> { - match geom { - Geometry::MultiPoint(mp) => Ok(mp), - _ => Err(FailedToConvertError), - } - } -} - -impl TryFrom> for MultiLineString { - type Error = FailedToConvertError; - - fn try_from(geom: Geometry) -> Result, Self::Error> { - match geom { - Geometry::MultiLineString(mls) => Ok(mls), - _ => Err(FailedToConvertError), - } - } -} - -impl TryFrom> for MultiPolygon { - type Error = FailedToConvertError; - - fn try_from(geom: Geometry) -> Result, Self::Error> { - match geom { - Geometry::MultiPolygon(mp) => Ok(mp), - _ => Err(FailedToConvertError), - } +fn inner_type_name(geometry: Geometry) -> &'static str +where + T: CoordNum, +{ + match geometry { + Geometry::Point(_) => type_name::>(), + Geometry::Line(_) => type_name::>(), + Geometry::LineString(_) => type_name::>(), + Geometry::Polygon(_) => type_name::>(), + Geometry::MultiPoint(_) => type_name::>(), + Geometry::MultiLineString(_) => type_name::>(), + Geometry::MultiPolygon(_) => type_name::>(), + Geometry::GeometryCollection(_) => type_name::>(), + Geometry::Rect(_) => type_name::>(), + Geometry::Triangle(_) => type_name::>(), } } diff --git a/geo-types/src/lib.rs b/geo-types/src/lib.rs index db9a6d28f..86fd7e0ea 100644 --- a/geo-types/src/lib.rs +++ b/geo-types/src/lib.rs @@ -117,6 +117,9 @@ mod rect; #[allow(deprecated)] pub use crate::rect::{InvalidRectCoordinatesError, Rect}; +mod error; +pub use error::Error; + #[macro_use] mod macros;