diff --git a/CHANGES.md b/CHANGES.md index 2090b65..2c1bacc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ * Add support for foreign members to `FeatureWriter`. * Added conversion from `Vec` to `GeoJson`. * Changed `Serialize` impls to avoid creating intermediate `JsonObject`s. +* Add support for generic precision ## 0.24.1 diff --git a/benches/parse.rs b/benches/parse.rs index 3047898..afad532 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -21,7 +21,7 @@ fn parse_feature_collection_benchmark(c: &mut Criterion) { c.bench_function("FeatureReader::features (countries.geojson)", |b| { b.iter(|| { let feature_reader = - geojson::FeatureReader::from_reader(BufReader::new(geojson_str.as_bytes())); + geojson::FeatureReader::<_, f64>::from_reader(BufReader::new(geojson_str.as_bytes())); let mut count = 0; for feature in feature_reader.features() { let feature = feature.unwrap(); @@ -41,7 +41,7 @@ fn parse_feature_collection_benchmark(c: &mut Criterion) { name: String, } let feature_reader = - geojson::FeatureReader::from_reader(BufReader::new(geojson_str.as_bytes())); + geojson::FeatureReader::<_, f64>::from_reader(BufReader::new(geojson_str.as_bytes())); let mut count = 0; for feature in feature_reader.deserialize::().unwrap() { @@ -66,7 +66,7 @@ fn parse_feature_collection_benchmark(c: &mut Criterion) { name: String, } let feature_reader = - geojson::FeatureReader::from_reader(BufReader::new(geojson_str.as_bytes())); + geojson::FeatureReader::<_, f64>::from_reader(BufReader::new(geojson_str.as_bytes())); let mut count = 0; for feature in feature_reader.deserialize::().unwrap() { diff --git a/examples/deserialize.rs b/examples/deserialize.rs index ea08d72..b986b14 100644 --- a/examples/deserialize.rs +++ b/examples/deserialize.rs @@ -22,7 +22,7 @@ fn main() -> Result<(), Box> { // Write the structs back to GeoJSON let file_writer = BufWriter::new(File::create("example-output-countries.geojson")?); - geojson::ser::to_feature_collection_writer(file_writer, &countries)?; + geojson::ser::to_feature_collection_writer::<_, _, f64>(file_writer, &countries)?; Ok(()) } diff --git a/examples/deserialize_to_geo_types.rs b/examples/deserialize_to_geo_types.rs index 5d03065..d1638ce 100644 --- a/examples/deserialize_to_geo_types.rs +++ b/examples/deserialize_to_geo_types.rs @@ -31,7 +31,7 @@ fn main() -> Result<(), Box> { // Write the structs back to GeoJSON let file_writer = BufWriter::new(File::create("example-output-countries.geojson")?); - geojson::ser::to_feature_collection_writer(file_writer, &countries)?; + geojson::ser::to_feature_collection_writer::<_, _, f64>(file_writer, &countries)?; Ok(()) } diff --git a/examples/stream_reader_writer.rs b/examples/stream_reader_writer.rs index 4ae17fd..3fd99f8 100644 --- a/examples/stream_reader_writer.rs +++ b/examples/stream_reader_writer.rs @@ -24,7 +24,7 @@ fn main() -> Result<(), Box> { let reader = { let file_reader = BufReader::new(File::open("tests/fixtures/countries.geojson")?); - FeatureReader::from_reader(file_reader) + FeatureReader::<_, f64>::from_reader(file_reader) }; let mut writer = { diff --git a/src/conversion/from_geo_types.rs b/src/conversion/from_geo_types.rs index ec05d87..b5b99f3 100644 --- a/src/conversion/from_geo_types.rs +++ b/src/conversion/from_geo_types.rs @@ -1,4 +1,5 @@ use geo_types::{self, CoordFloat}; +use serde::Serialize; use crate::{geometry, Feature, FeatureCollection}; @@ -6,9 +7,9 @@ use crate::{LineStringType, PointType, PolygonType}; use std::convert::From; #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::Point> for geometry::Value +impl<'a, T> From<&'a geo_types::Point> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(point: &geo_types::Point) -> Self { let coords = create_point_type(point); @@ -18,9 +19,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::MultiPoint> for geometry::Value +impl<'a, T> From<&'a geo_types::MultiPoint> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(multi_point: &geo_types::MultiPoint) -> Self { let coords = multi_point @@ -34,9 +35,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::LineString> for geometry::Value +impl<'a, T> From<&'a geo_types::LineString> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(line_string: &geo_types::LineString) -> Self { let coords = create_line_string_type(line_string); @@ -46,9 +47,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::Line> for geometry::Value +impl<'a, T> From<&'a geo_types::Line> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(line: &geo_types::Line) -> Self { let coords = create_from_line_type(line); @@ -58,9 +59,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::Triangle> for geometry::Value +impl<'a, T> From<&'a geo_types::Triangle> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(triangle: &geo_types::Triangle) -> Self { let coords = create_from_triangle_type(triangle); @@ -70,9 +71,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::Rect> for geometry::Value +impl<'a, T> From<&'a geo_types::Rect> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(rect: &geo_types::Rect) -> Self { let coords = create_from_rect_type(rect); @@ -82,9 +83,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::MultiLineString> for geometry::Value +impl<'a, T> From<&'a geo_types::MultiLineString> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(multi_line_string: &geo_types::MultiLineString) -> Self { let coords = create_multi_line_string_type(multi_line_string); @@ -94,9 +95,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::Polygon> for geometry::Value +impl<'a, T> From<&'a geo_types::Polygon> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(polygon: &geo_types::Polygon) -> Self { let coords = create_polygon_type(polygon); @@ -106,9 +107,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::MultiPolygon> for geometry::Value +impl<'a, T> From<&'a geo_types::MultiPolygon> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(multi_polygon: &geo_types::MultiPolygon) -> Self { let coords = create_multi_polygon_type(multi_polygon); @@ -118,9 +119,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::GeometryCollection> for geometry::Value +impl<'a, T> From<&'a geo_types::GeometryCollection> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(geometry_collection: &geo_types::GeometryCollection) -> Self { let values = geometry_collection @@ -134,12 +135,12 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::GeometryCollection> for FeatureCollection +impl<'a, T> From<&'a geo_types::GeometryCollection> for FeatureCollection where - T: CoordFloat, + T: CoordFloat + Serialize, { fn from(geometry_collection: &geo_types::GeometryCollection) -> Self { - let values: Vec = geometry_collection + let values: Vec> = geometry_collection .0 .iter() .map(|geometry| geometry::Geometry::new(geometry::Value::from(geometry)).into()) @@ -154,9 +155,9 @@ where } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl<'a, T> From<&'a geo_types::Geometry> for geometry::Value +impl<'a, T> From<&'a geo_types::Geometry> for geometry::Value where - T: CoordFloat, + T: CoordFloat + Serialize, { /// Convert from `geo_types::Geometry` enums fn from(geometry: &'a geo_types::Geometry) -> Self { @@ -179,19 +180,19 @@ where } } -fn create_point_type(point: &geo_types::Point) -> PointType +fn create_point_type(point: &geo_types::Point) -> PointType where - T: CoordFloat, + T: CoordFloat + Serialize, { - let x: f64 = point.x().to_f64().unwrap(); - let y: f64 = point.y().to_f64().unwrap(); + let x = point.x(); + let y = point.y(); vec![x, y] } -fn create_line_string_type(line_string: &geo_types::LineString) -> LineStringType +fn create_line_string_type(line_string: &geo_types::LineString) -> LineStringType where - T: CoordFloat, + T: CoordFloat + Serialize, { line_string .points_iter() @@ -199,9 +200,9 @@ where .collect() } -fn create_from_line_type(line_string: &geo_types::Line) -> LineStringType +fn create_from_line_type(line_string: &geo_types::Line) -> LineStringType where - T: CoordFloat, + T: CoordFloat + Serialize, { vec![ create_point_type(&line_string.start_point()), @@ -209,25 +210,25 @@ where ] } -fn create_from_triangle_type(triangle: &geo_types::Triangle) -> PolygonType +fn create_from_triangle_type(triangle: &geo_types::Triangle) -> PolygonType where - T: CoordFloat, + T: CoordFloat + Serialize, { create_polygon_type(&triangle.to_polygon()) } -fn create_from_rect_type(rect: &geo_types::Rect) -> PolygonType +fn create_from_rect_type(rect: &geo_types::Rect) -> PolygonType where - T: CoordFloat, + T: CoordFloat + Serialize, { create_polygon_type(&rect.to_polygon()) } fn create_multi_line_string_type( multi_line_string: &geo_types::MultiLineString, -) -> Vec +) -> Vec> where - T: CoordFloat, + T: CoordFloat + Serialize, { multi_line_string .0 @@ -236,9 +237,9 @@ where .collect() } -fn create_polygon_type(polygon: &geo_types::Polygon) -> PolygonType +fn create_polygon_type(polygon: &geo_types::Polygon) -> PolygonType where - T: CoordFloat, + T: CoordFloat + Serialize, { let mut coords = vec![polygon .exterior() @@ -256,9 +257,9 @@ where coords } -fn create_multi_polygon_type(multi_polygon: &geo_types::MultiPolygon) -> Vec +fn create_multi_polygon_type(multi_polygon: &geo_types::MultiPolygon) -> Vec> where - T: CoordFloat, + T: CoordFloat + Serialize, { multi_polygon .0 diff --git a/src/conversion/mod.rs b/src/conversion/mod.rs index 9c3f4cf..b57d00b 100644 --- a/src/conversion/mod.rs +++ b/src/conversion/mod.rs @@ -24,8 +24,10 @@ use std::convert::TryInto; macro_rules! assert_almost_eq { ($x:expr, $y:expr, $epsilon:expr) => {{ use num_traits::Zero; - let a = $x.abs(); - let b = $y.abs(); + let x = $x as f64; + let y = $y as f64; + let a = x.abs(); + let b = y.abs(); let delta = (a - b).abs(); if a.is_infinite() || a.is_nan() || b.is_infinite() || b.is_nan() { @@ -66,10 +68,10 @@ macro_rules! assert_almost_eq { macro_rules! try_from_owned_value { ($to:ty) => { #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] - impl TryFrom for $to { - type Error = Error; + impl TryFrom> for $to { + type Error = Error; - fn try_from(value: geometry::Value) -> Result { + fn try_from(value: geometry::Value) -> Result { (&value).try_into() } } @@ -80,9 +82,9 @@ pub(crate) mod from_geo_types; pub(crate) mod to_geo_types; // Process top-level `GeoJSON` items, returning a geo_types::GeometryCollection or an Error -fn process_geojson(gj: &GeoJson) -> Result> +fn process_geojson(gj: &GeoJson) -> Result, T> where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { match gj { FeatureCollection(collection) => Ok(GeometryCollection( @@ -92,7 +94,7 @@ where // Only pass on non-empty geometries .filter_map(|feature| feature.geometry.as_ref()) .map(|geometry| geometry.clone().try_into()) - .collect::>()?, + .collect::>()?, )), Feature(feature) => { if let Some(geometry) = &feature.geometry { @@ -139,9 +141,9 @@ where /// let mut collection: GeometryCollection = quick_collection(&geojson).unwrap(); /// ``` #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -pub fn quick_collection(gj: &GeoJson) -> Result> +pub fn quick_collection(gj: &GeoJson) -> Result, T> where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { process_geojson(gj) } diff --git a/src/conversion/to_geo_types.rs b/src/conversion/to_geo_types.rs index 996a730..6a6e8b3 100644 --- a/src/conversion/to_geo_types.rs +++ b/src/conversion/to_geo_types.rs @@ -9,13 +9,13 @@ use crate::{Error, Result}; use std::convert::{TryFrom, TryInto}; #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::Point +impl TryFrom<&geometry::Value> for geo_types::Point where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result { + fn try_from(value: &geometry::Value) -> Result { match value { geometry::Value::Point(point_type) => Ok(create_geo_point(point_type)), other => Err(mismatch_geom_err("Point", other)), @@ -25,13 +25,13 @@ where try_from_owned_value!(geo_types::Point); #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::MultiPoint +impl TryFrom<&geometry::Value> for geo_types::MultiPoint where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result { + fn try_from(value: &geometry::Value) -> Result { match value { geometry::Value::MultiPoint(multi_point_type) => Ok(geo_types::MultiPoint( multi_point_type @@ -46,13 +46,13 @@ where try_from_owned_value!(geo_types::MultiPoint); #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::LineString +impl TryFrom<&geometry::Value> for geo_types::LineString where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result { + fn try_from(value: &geometry::Value) -> Result { match value { geometry::Value::LineString(multi_point_type) => { Ok(create_geo_line_string(multi_point_type)) @@ -64,13 +64,13 @@ where try_from_owned_value!(geo_types::LineString); #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::MultiLineString +impl TryFrom<&geometry::Value> for geo_types::MultiLineString where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result { + fn try_from(value: &geometry::Value) -> Result { match value { geometry::Value::MultiLineString(multi_line_string_type) => { Ok(create_geo_multi_line_string(multi_line_string_type)) @@ -82,13 +82,13 @@ where try_from_owned_value!(geo_types::MultiLineString); #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::Polygon +impl TryFrom<&geometry::Value> for geo_types::Polygon where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result { + fn try_from(value: &geometry::Value) -> Result { match value { geometry::Value::Polygon(polygon_type) => Ok(create_geo_polygon(polygon_type)), other => Err(mismatch_geom_err("Polygon", other)), @@ -98,13 +98,13 @@ where try_from_owned_value!(geo_types::Polygon); #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::MultiPolygon +impl TryFrom<&geometry::Value> for geo_types::MultiPolygon where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result> { + fn try_from(value: &geometry::Value) -> Result, T> { match value { geometry::Value::MultiPolygon(multi_polygon_type) => { Ok(create_geo_multi_polygon(multi_polygon_type)) @@ -116,13 +116,13 @@ where try_from_owned_value!(geo_types::MultiPolygon); #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::GeometryCollection +impl TryFrom<&geometry::Value> for geo_types::GeometryCollection where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result { + fn try_from(value: &geometry::Value) -> Result { match value { geometry::Value::GeometryCollection(geometries) => { let geojson_geometries = geometries @@ -139,13 +139,13 @@ where try_from_owned_value!(geo_types::GeometryCollection); #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom<&geometry::Value> for geo_types::Geometry +impl TryFrom<&geometry::Value> for geo_types::Geometry where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(value: &geometry::Value) -> Result { + fn try_from(value: &geometry::Value) -> Result { match value { geometry::Value::Point(ref point_type) => { Ok(geo_types::Geometry::Point(create_geo_point(point_type))) @@ -178,7 +178,7 @@ where .iter() .cloned() .map(|geom| geom.try_into()) - .collect::>>>()?, + .collect::>, T>>()?, )); Ok(gc) } @@ -191,37 +191,37 @@ macro_rules! impl_try_from_geom_value { ($($kind:ident),*) => { $( #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] - impl TryFrom<&$crate::Geometry> for geo_types::$kind + impl TryFrom<&$crate::Geometry> for geo_types::$kind where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(geometry: &crate::Geometry) -> Result { + fn try_from(geometry: &crate::Geometry) -> Result { Self::try_from(&geometry.value) } } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] - impl TryFrom<$crate::Geometry> for geo_types::$kind + impl TryFrom<$crate::Geometry> for geo_types::$kind where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(geometry: crate::Geometry) -> Result { + fn try_from(geometry: crate::Geometry) -> Result { Self::try_from(geometry.value) } } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] - impl TryFrom<$crate::Feature> for geo_types::$kind + impl TryFrom<$crate::Feature> for geo_types::$kind where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(val: Feature) -> Result { + fn try_from(val: Feature) -> Result { match val.geometry { None => Err(Error::FeatureHasNoGeometry(val)), Some(geom) => geom.try_into(), @@ -244,27 +244,27 @@ impl_try_from_geom_value![ ]; #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom for geo_types::Geometry +impl TryFrom> for geo_types::Geometry where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(val: FeatureCollection) -> Result> { + fn try_from(val: FeatureCollection) -> Result, T> { Ok(geo_types::Geometry::GeometryCollection(quick_collection( - &GeoJson::FeatureCollection(val), + &GeoJson::::FeatureCollection(val), )?)) } } #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] -impl TryFrom for geo_types::Geometry +impl TryFrom> for geo_types::Geometry where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { - type Error = Error; + type Error = Error; - fn try_from(val: GeoJson) -> Result> { + fn try_from(val: GeoJson) -> Result, T> { match val { GeoJson::Geometry(geom) => geom.try_into(), GeoJson::Feature(feat) => feat.try_into(), @@ -273,9 +273,9 @@ where } } -fn create_geo_coordinate(point_type: &PointType) -> geo_types::Coordinate +fn create_geo_coordinate(point_type: &PointType) -> geo_types::Coordinate where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { geo_types::Coordinate { x: T::from(point_type[0]).unwrap(), @@ -283,9 +283,9 @@ where } } -fn create_geo_point(point_type: &PointType) -> geo_types::Point +fn create_geo_point(point_type: &PointType) -> geo_types::Point where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { geo_types::Point::new( T::from(point_type[0]).unwrap(), @@ -293,9 +293,9 @@ where ) } -fn create_geo_line_string(line_type: &LineStringType) -> geo_types::LineString +fn create_geo_line_string(line_type: &LineStringType) -> geo_types::LineString where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { geo_types::LineString( line_type @@ -306,10 +306,10 @@ where } fn create_geo_multi_line_string( - multi_line_type: &[LineStringType], + multi_line_type: &[LineStringType], ) -> geo_types::MultiLineString where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { geo_types::MultiLineString( multi_line_type @@ -319,9 +319,9 @@ where ) } -fn create_geo_polygon(polygon_type: &PolygonType) -> geo_types::Polygon +fn create_geo_polygon(polygon_type: &PolygonType) -> geo_types::Polygon where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { let exterior = polygon_type .get(0) @@ -340,9 +340,9 @@ where geo_types::Polygon::new(exterior, interiors) } -fn create_geo_multi_polygon(multi_polygon_type: &[PolygonType]) -> geo_types::MultiPolygon +fn create_geo_multi_polygon(multi_polygon_type: &[PolygonType]) -> geo_types::MultiPolygon where - T: CoordFloat, + T: CoordFloat + serde::Serialize, { geo_types::MultiPolygon( multi_polygon_type @@ -352,7 +352,10 @@ where ) } -fn mismatch_geom_err(expected_type: &'static str, found: &geometry::Value) -> Error { +fn mismatch_geom_err(expected_type: &'static str, found: &geometry::Value) -> Error +where + T: CoordFloat + serde::Serialize, +{ Error::InvalidGeometryConversion { expected_type, found_type: found.type_name(), @@ -677,7 +680,7 @@ mod tests { } #[test] - fn borrowed_value_conversions_test() -> crate::Result<()> { + fn borrowed_value_conversions_test() -> crate::Result<(), f64> { let coord1 = vec![100.0, 0.2]; let coord2 = vec![101.0, 1.0]; let coord3 = vec![102.0, 0.8]; diff --git a/src/de.rs b/src/de.rs index feb318e..118c8c9 100644 --- a/src/de.rs +++ b/src/de.rs @@ -3,7 +3,7 @@ //! implement or derive [`serde::Deserialize`]: //! //! ```rust, ignore -//! #[derive(serde::Deserialize)] +//! #[derive(serde::Deserialize, serde::Serialize)] //! struct MyStruct { //! ... //! } @@ -11,7 +11,7 @@ //! //! Your type *must* have a field called `geometry` and it must be `deserialized_with` [`deserialize_geometry`](crate::de::deserialize_geometry): //! ```rust, ignore -//! #[derive(serde::Deserialize)] +//! #[derive(serde::Deserialize, serde::Serialize)] //! struct MyStruct { //! #[serde(deserialize_with = "geojson::de::deserialize_geometry")] //! geometry: geo_types::Point, @@ -25,10 +25,10 @@ //! # Examples #![cfg_attr(feature = "geo-types", doc = "```")] #![cfg_attr(not(feature = "geo-types"), doc = "```ignore")] -//! use serde::Deserialize; +//! use serde::{Deserialize, Serialize}; //! use geojson::de::deserialize_geometry; //! -//! #[derive(Deserialize)] +//! #[derive(Deserialize, Serialize)] //! struct MyStruct { //! // Deserialize from geojson, rather than expecting the type's default serialization //! #[serde(deserialize_with = "deserialize_geometry")] @@ -225,10 +225,11 @@ where /// assert_eq!(features[0].name, "Downtown"); /// assert_eq!(features[0].geometry.x(), 11.1); /// ``` -pub fn deserialize_geometry<'de, D, G>(deserializer: D) -> std::result::Result +pub fn deserialize_geometry<'de, D, G, T>(deserializer: D) -> std::result::Result where D: Deserializer<'de>, - G: TryFrom, + T: geo_types::CoordFloat + serde::Serialize, + G: TryFrom>, G::Error: std::fmt::Display, { let geojson_geometry = crate::Geometry::deserialize(deserializer)?; @@ -240,9 +241,12 @@ where /// Deserialize a GeoJSON FeatureCollection into [`Feature`] structs. /// /// If instead you'd like to deserialize your own structs from GeoJSON, see [`deserialize_feature_collection`]. -pub fn deserialize_features_from_feature_collection( +pub fn deserialize_features_from_feature_collection( feature_collection_reader: impl Read, -) -> impl Iterator> { +) -> impl Iterator, T>> +where + T: geo_types::CoordFloat + serde::Serialize, +{ FeatureReader::from_reader(feature_collection_reader).features() } @@ -276,14 +280,15 @@ pub fn deserialize_features_from_feature_collection( /// let reader = feature_str.as_bytes(); /// /// // build your struct from GeoJSON -/// let my_struct = geojson::de::deserialize_single_feature::(reader).expect("valid geojson for MyStruct"); +/// let my_struct = geojson::de::deserialize_single_feature::(reader).expect("valid geojson for MyStruct"); /// /// assert_eq!(my_struct.name, "Downtown"); /// assert_eq!(my_struct.geometry.x(), 11.1); /// ``` -pub fn deserialize_single_feature<'de, T>(feature_reader: impl Read) -> Result +pub fn deserialize_single_feature<'de, T, U>(feature_reader: impl Read) -> Result where T: Deserialize<'de>, + U: geo_types::CoordFloat + serde::Serialize, { let feature_value: JsonValue = serde_json::from_reader(feature_reader)?; let deserializer = feature_value.into_deserializer(); diff --git a/src/errors.rs b/src/errors.rs index b5e7c5b..8937c56 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -5,7 +5,7 @@ use thiserror::Error; /// Errors which can occur when encoding, decoding, and converting GeoJSON #[derive(Error, Debug)] -pub enum Error { +pub enum Error { #[error("Encountered non-array value for a 'bbox' object: `{0}`")] BboxExpectedArray(Value), #[error("Encountered non-numeric value within 'bbox' array")] @@ -30,7 +30,7 @@ pub enum Error { #[error( "Attempted to a convert a feature without a geometry into a geo_types::Geometry: `{0}`" )] - FeatureHasNoGeometry(Feature), + FeatureHasNoGeometry(Feature), #[error("Encountered an unknown 'geometry' object type: `{0}`")] GeometryUnknownType(String), #[error("Error while deserializing JSON: {0}")] @@ -50,7 +50,7 @@ pub enum Error { #[error("Expected a GeoJSON property for `{0}`, but got None")] ExpectedProperty(String), #[error("Expected a floating-point value, but got None")] - ExpectedF64Value, + ExpectedFloatValue, #[error("Expected an Array value, but got `{0}`")] ExpectedArrayValue(String), #[error("Expected an owned Object, but got `{0}`")] @@ -59,15 +59,21 @@ pub enum Error { PositionTooShort(usize), } -pub type Result = std::result::Result; +pub type Result = std::result::Result>; -impl From for Error { +impl From for Error +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn from(error: serde_json::Error) -> Self { Self::MalformedJson(error) } } -impl From for Error { +impl From for Error +where + T: geo_types::CoordFloat+ serde::Serialize, +{ fn from(error: std::io::Error) -> Self { Self::Io(error) } diff --git a/src/feature.rs b/src/feature.rs index dc7aaf1..4558bf8 100644 --- a/src/feature.rs +++ b/src/feature.rs @@ -20,8 +20,11 @@ use crate::{util, Feature, Geometry, Value}; use crate::{JsonObject, JsonValue}; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; -impl From for Feature { - fn from(geom: Geometry) -> Feature { +impl From> for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(geom: Geometry) -> Feature { Feature { bbox: geom.bbox.clone(), foreign_members: geom.foreign_members.clone(), @@ -32,8 +35,11 @@ impl From for Feature { } } -impl From for Feature { - fn from(val: Value) -> Feature { +impl From> for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(val: Value) -> Feature { Feature { bbox: None, foreign_members: None, @@ -44,16 +50,22 @@ impl From for Feature { } } -impl FromStr for Feature { - type Err = Error; +impl FromStr for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Self::try_from(crate::GeoJson::from_str(s)?) } } -impl<'a> From<&'a Feature> for JsonObject { - fn from(feature: &'a Feature) -> JsonObject { +impl<'a, T> From<&'a Feature> for JsonObject +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(feature: &'a Feature) -> JsonObject { // The unwrap() should never panic, because Feature contains only JSON-serializable types match serde_json::to_value(feature).unwrap() { serde_json::Value::Object(obj) => obj, @@ -69,12 +81,15 @@ impl<'a> From<&'a Feature> for JsonObject { } } -impl Feature { - pub fn from_json_object(object: JsonObject) -> Result { +impl Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + pub fn from_json_object(object: JsonObject) -> Result { Self::try_from(object) } - pub fn from_json_value(value: JsonValue) -> Result { + pub fn from_json_value(value: JsonValue) -> Result { Self::try_from(value) } @@ -129,13 +144,16 @@ impl Feature { } } -impl TryFrom for Feature { - type Error = Error; +impl TryFrom for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(mut object: JsonObject) -> Result { + fn try_from(mut object: JsonObject) -> Result { let res = &*util::expect_type(&mut object)?; match res { - "Feature" => Ok(Feature { + "Feature" => Ok(Self { geometry: util::get_geometry(&mut object)?, properties: util::get_properties(&mut object)?, id: util::get_id(&mut object)?, @@ -147,10 +165,13 @@ impl TryFrom for Feature { } } -impl TryFrom for Feature { - type Error = Error; +impl TryFrom for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(value: JsonValue) -> Result { + fn try_from(value: JsonValue) -> Result { if let JsonValue::Object(obj) = value { Self::try_from(obj) } else { @@ -159,7 +180,10 @@ impl TryFrom for Feature { } } -impl Serialize for Feature { +impl Serialize for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, @@ -183,8 +207,11 @@ impl Serialize for Feature { } } -impl<'de> Deserialize<'de> for Feature { - fn deserialize(deserializer: D) -> std::result::Result +impl<'de, T> Deserialize<'de> for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn deserialize(deserializer: D) -> std::result::Result, D::Error> where D: Deserializer<'de>, { @@ -259,7 +286,7 @@ mod tests { serde_json::to_string(&feature).unwrap() } - fn decode(json_string: String) -> GeoJson { + fn decode(json_string: String) -> GeoJson { json_string.parse().unwrap() } @@ -505,7 +532,7 @@ mod tests { }) .to_string(); - let feature = Feature::from_str(&feature_json).unwrap(); + let feature = Feature::::from_str(&feature_json).unwrap(); assert_eq!("Dinagat Islands", feature.property("name").unwrap()); } @@ -517,7 +544,7 @@ mod tests { }) .to_string(); - let actual_failure = Feature::from_str(&geometry_json).unwrap_err(); + let actual_failure = Feature::::from_str(&geometry_json).unwrap_err(); match actual_failure { Error::ExpectedType { actual, expected } => { assert_eq!(actual, "Geometry"); diff --git a/src/feature_collection.rs b/src/feature_collection.rs index ffaefa6..560d9d4 100644 --- a/src/feature_collection.rs +++ b/src/feature_collection.rs @@ -34,7 +34,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// use geojson::FeatureCollection; /// use geojson::GeoJson; /// -/// let feature_collection = FeatureCollection { +/// let feature_collection: FeatureCollection = FeatureCollection { /// bbox: None, /// features: vec![], /// foreign_members: None, @@ -53,7 +53,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// ```rust /// use geojson::{Feature, FeatureCollection, Value}; /// -/// let fc: FeatureCollection = (0..10) +/// let fc: FeatureCollection = (0..10) /// .map(|idx| -> Feature { /// let c = idx as f64; /// Value::Point(vec![1.0 * c, 2.0 * c, 3.0 * c]).into() @@ -62,38 +62,50 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// assert_eq!(fc.features.len(), 10); /// ``` #[derive(Clone, Debug, PartialEq)] -pub struct FeatureCollection { +pub struct FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ /// Bounding Box /// /// [GeoJSON Format Specification § 5](https://tools.ietf.org/html/rfc7946#section-5) - pub bbox: Option, - pub features: Vec, + pub bbox: Option>, + pub features: Vec>, /// Foreign Members /// /// [GeoJSON Format Specification § 6](https://tools.ietf.org/html/rfc7946#section-6) pub foreign_members: Option, } -impl IntoIterator for FeatureCollection { - type Item = Feature; - type IntoIter = std::vec::IntoIter; +impl IntoIterator for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Item = Feature; + type IntoIter = std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.features.into_iter() } } -impl<'a> IntoIterator for &'a FeatureCollection { - type Item = &'a Feature; - type IntoIter = std::slice::Iter<'a, Feature>; +impl<'a, T> IntoIterator for &'a FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Item = &'a Feature; + type IntoIter = std::slice::Iter<'a, Feature>; fn into_iter(self) -> Self::IntoIter { IntoIterator::into_iter(&self.features) } } -impl<'a> From<&'a FeatureCollection> for JsonObject { - fn from(fc: &'a FeatureCollection) -> JsonObject { +impl<'a, T> From<&'a FeatureCollection> for JsonObject +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(fc: &'a FeatureCollection) -> JsonObject { // The unwrap() should never panic, because FeatureCollection contains only JSON-serializable types match serde_json::to_value(fc).unwrap() { serde_json::Value::Object(obj) => obj, @@ -106,22 +118,28 @@ impl<'a> From<&'a FeatureCollection> for JsonObject { } } -impl FeatureCollection { - pub fn from_json_object(object: JsonObject) -> Result { +impl FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + pub fn from_json_object(object: JsonObject) -> Result { Self::try_from(object) } - pub fn from_json_value(value: JsonValue) -> Result { + pub fn from_json_value(value: JsonValue) -> Result { Self::try_from(value) } } -impl TryFrom for FeatureCollection { - type Error = Error; +impl TryFrom for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(mut object: JsonObject) -> Result { + fn try_from(mut object: JsonObject) -> Result { match util::expect_type(&mut object)? { - ref type_ if type_ == "FeatureCollection" => Ok(FeatureCollection { + ref type_ if type_ == "FeatureCollection" => Ok(Self { bbox: util::get_bbox(&mut object)?, features: util::get_features(&mut object)?, foreign_members: util::get_foreign_members(object)?, @@ -134,10 +152,13 @@ impl TryFrom for FeatureCollection { } } -impl TryFrom for FeatureCollection { - type Error = Error; +impl TryFrom for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(value: JsonValue) -> Result { + fn try_from(value: JsonValue) -> Result { if let JsonValue::Object(obj) = value { Self::try_from(obj) } else { @@ -146,15 +167,21 @@ impl TryFrom for FeatureCollection { } } -impl FromStr for FeatureCollection { - type Err = Error; +impl FromStr for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Self::try_from(crate::GeoJson::from_str(s)?) } } -impl Serialize for FeatureCollection { +impl Serialize for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, @@ -177,8 +204,11 @@ impl Serialize for FeatureCollection { } } -impl<'de> Deserialize<'de> for FeatureCollection { - fn deserialize(deserializer: D) -> std::result::Result +impl<'de, T> Deserialize<'de> for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn deserialize(deserializer: D) -> std::result::Result, D::Error> where D: Deserializer<'de>, { @@ -197,8 +227,11 @@ impl<'de> Deserialize<'de> for FeatureCollection { /// Otherwise, the output will not have a bounding-box. /// /// [`collect`]: std::iter::Iterator::collect -impl FromIterator for FeatureCollection { - fn from_iter>(iter: T) -> Self { +impl FromIterator> for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from_iter>>(iter: U) -> Self { let mut bbox = Some(vec![]); let features = iter @@ -248,7 +281,7 @@ impl FromIterator for FeatureCollection { }; }) .collect(); - FeatureCollection { + Self { bbox, features, foreign_members: None, @@ -312,13 +345,15 @@ mod tests { #[test] fn test_from_str_ok() { - let feature_collection = FeatureCollection::from_str(&feature_collection_json()).unwrap(); + let feature_collection = + FeatureCollection::::from_str(&feature_collection_json()).unwrap(); assert_eq!(2, feature_collection.features.len()); } #[test] fn iter_features() { - let feature_collection = FeatureCollection::from_str(&feature_collection_json()).unwrap(); + let feature_collection = + FeatureCollection::::from_str(&feature_collection_json()).unwrap(); let mut names: Vec = vec![]; for feature in &feature_collection { @@ -342,7 +377,7 @@ mod tests { }) .to_string(); - let actual_failure = FeatureCollection::from_str(&geometry_json).unwrap_err(); + let actual_failure = FeatureCollection::::from_str(&geometry_json).unwrap_err(); match actual_failure { Error::ExpectedType { actual, expected } => { assert_eq!(actual, "Geometry"); diff --git a/src/feature_iterator.rs b/src/feature_iterator.rs index 9155067..6346b36 100644 --- a/src/feature_iterator.rs +++ b/src/feature_iterator.rs @@ -33,11 +33,12 @@ use std::marker::PhantomData; /// Based on example code found at . /// /// [GeoJSON Format Specification § 3.3](https://datatracker.ietf.org/doc/html/rfc7946#section-3.3) -pub struct FeatureIterator<'de, R, D = Feature> { +pub struct FeatureIterator<'de, R, T: geo_types::CoordFloat + serde::Serialize = f64, D = Feature> { reader: R, state: State, output: PhantomData, lifetime: PhantomData<&'de ()>, + precision: PhantomData, } #[derive(Debug, Copy, Clone)] @@ -47,22 +48,27 @@ enum State { AfterFeatures, } -impl<'de, R, D> FeatureIterator<'de, R, D> { +impl<'de, R, T, D> FeatureIterator<'de, R, T, D> +where + T: geo_types::CoordFloat + serde::Serialize, +{ pub fn new(reader: R) -> Self { FeatureIterator { reader, state: State::BeforeFeatures, output: PhantomData, lifetime: PhantomData, + precision: PhantomData, } } } -impl<'de, R, D> FeatureIterator<'de, R, D> +impl<'de, R, T, D> FeatureIterator<'de, R, T, D> where R: io::Read, + T: geo_types::CoordFloat + serde::Serialize, { - fn seek_to_next_feature(&mut self) -> Result { + fn seek_to_next_feature(&mut self) -> Result { let mut next_bytes = [0]; loop { self.reader.read_exact(&mut next_bytes)?; @@ -101,12 +107,13 @@ where } } -impl<'de, R, D> Iterator for FeatureIterator<'de, R, D> +impl<'de, R, T, D> Iterator for FeatureIterator<'de, R, T, D> where R: io::Read, + T: geo_types::CoordFloat + serde::Serialize, D: Deserialize<'de>, { - type Item = Result; + type Item = Result; fn next(&mut self) -> Option { match self.seek_to_next_feature() { @@ -183,7 +190,7 @@ mod tests { #[test] fn stream_read_test() { - let mut fi = FeatureIterator::<_, Feature>::new(BufReader::new(fc().as_bytes())); + let mut fi = FeatureIterator::<_, f64, Feature>::new(BufReader::new(fc().as_bytes())); assert_eq!( Geometry { bbox: None, @@ -252,7 +259,7 @@ mod tests { } "#; let features: Vec = - FeatureIterator::new(BufReader::new(type_first.as_bytes())) + FeatureIterator::<_, f64>::new(BufReader::new(type_first.as_bytes())) .map(Result::unwrap) .collect(); assert_eq!(features.len(), 2); @@ -284,7 +291,7 @@ mod tests { } "#; let features: Vec = - FeatureIterator::new(BufReader::new(type_first.as_bytes())) + FeatureIterator::<_, f64>::new(BufReader::new(type_first.as_bytes())) .map(Result::unwrap) .collect(); assert_eq!(features.len(), 2); diff --git a/src/feature_reader.rs b/src/feature_reader.rs index 0d0a7e8..a5e7e40 100644 --- a/src/feature_reader.rs +++ b/src/feature_reader.rs @@ -4,16 +4,21 @@ use crate::{Feature, Result}; use serde::de::DeserializeOwned; use std::io::Read; +use std::marker::PhantomData; /// Enumerates individual Features from a GeoJSON FeatureCollection -pub struct FeatureReader { +pub struct FeatureReader +where + T: geo_types::CoordFloat + serde::Serialize, +{ reader: R, + precision: PhantomData, } -impl FeatureReader { +impl FeatureReader { /// Create a FeatureReader from the given `reader`. pub fn from_reader(reader: R) -> Self { - Self { reader } + Self { reader, precision: PhantomData } } /// Iterate over the individual [`Feature`s](Feature) of a FeatureCollection. @@ -47,10 +52,10 @@ impl FeatureReader { /// .as_bytes(); /// let io_reader = std::io::BufReader::new(feature_collection_string); /// - /// use geojson::FeatureReader; + /// use geojson::{FeatureReader, Feature}; /// let feature_reader = FeatureReader::from_reader(io_reader); /// for feature in feature_reader.features() { - /// let feature = feature.expect("valid geojson feature"); + /// let feature: Feature = feature.expect("valid geojson feature"); /// /// let name = feature.property("name").unwrap().as_str().unwrap(); /// let age = feature.property("age").unwrap().as_u64().unwrap(); @@ -64,7 +69,7 @@ impl FeatureReader { /// } /// } /// ``` - pub fn features(self) -> impl Iterator> { + pub fn features(self) -> impl Iterator, T>> { #[allow(deprecated)] crate::FeatureIterator::new(self.reader) } @@ -127,7 +132,7 @@ impl FeatureReader { /// # age: u64, /// # } /// - /// let feature_reader = FeatureReader::from_reader(io_reader); + /// let feature_reader = FeatureReader::<_, f64>::from_reader(io_reader); /// for feature in feature_reader.deserialize::().unwrap() { /// let my_struct = feature.expect("valid geojson feature"); /// @@ -206,7 +211,7 @@ mod tests { fn deserialize_into_type() { let feature_collection_string = feature_collection_string(); let mut bytes_reader = feature_collection_string.as_bytes(); - let feature_reader = FeatureReader::from_reader(&mut bytes_reader); + let feature_reader = FeatureReader::<_, f64>::from_reader(&mut bytes_reader); let records: Vec = feature_reader .deserialize() diff --git a/src/feature_writer.rs b/src/feature_writer.rs index 18949df..b824918 100644 --- a/src/feature_writer.rs +++ b/src/feature_writer.rs @@ -1,5 +1,5 @@ use crate::ser::to_feature_writer; -use crate::{Error, Feature, JsonObject, JsonValue, Result}; +use crate::{Error, Feature, Result}; use serde::Serialize; use std::io::Write; @@ -36,7 +36,10 @@ impl FeatureWriter { /// Write a [`crate::Feature`] struct to the output stream. If you'd like to /// serialize your own custom structs, see [`FeatureWriter::serialize`] instead. - pub fn write_feature(&mut self, feature: &Feature) -> Result<()> { + pub fn write_feature(&mut self, feature: &Feature) -> Result<(), T> + where + T: geo_types::CoordFloat + serde::Serialize, + { match self.state { State::Finished => { return Err(Error::InvalidWriterState( @@ -247,15 +250,24 @@ impl FeatureWriter { Ok(self.writer.flush()?) } - fn write_prefix(&mut self) -> Result<()> { + fn write_prefix(&mut self) -> Result<(), T> + where + T: geo_types::CoordFloat + serde::Serialize, + { self.write_str(r#"{ "type": "FeatureCollection", "features": ["#) } - fn write_suffix(&mut self) -> Result<()> { + fn write_suffix(&mut self) -> Result<(), T> + where + T: geo_types::CoordFloat + serde::Serialize, + { self.write_str("]}") } - fn write_str(&mut self, text: &str) -> Result<()> { + fn write_str(&mut self, text: &str) -> Result<(), T> + where + T: geo_types::CoordFloat + serde::Serialize, + { self.writer.write_all(text.as_bytes())?; Ok(()) } diff --git a/src/geojson.rs b/src/geojson.rs index 8a3656e..aa995b9 100644 --- a/src/geojson.rs +++ b/src/geojson.rs @@ -44,14 +44,20 @@ use std::str::FromStr; /// ``` /// [GeoJSON Format Specification § 3](https://tools.ietf.org/html/rfc7946#section-3) #[derive(Clone, Debug, PartialEq)] -pub enum GeoJson { - Geometry(Geometry), - Feature(Feature), - FeatureCollection(FeatureCollection), +pub enum GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + Geometry(Geometry), + Feature(Feature), + FeatureCollection(FeatureCollection), } -impl<'a> From<&'a GeoJson> for JsonObject { - fn from(geojson: &'a GeoJson) -> JsonObject { +impl<'a, T> From<&'a GeoJson> for JsonObject +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(geojson: &'a GeoJson) -> JsonObject { match *geojson { GeoJson::Geometry(ref geometry) => geometry.into(), GeoJson::Feature(ref feature) => feature.into(), @@ -60,8 +66,11 @@ impl<'a> From<&'a GeoJson> for JsonObject { } } -impl From for JsonValue { - fn from(geojson: GeoJson) -> JsonValue { +impl From> for JsonValue +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(geojson: GeoJson) -> JsonValue { match geojson { GeoJson::Geometry(geometry) => JsonValue::Object(JsonObject::from(&geometry)), GeoJson::Feature(feature) => JsonValue::Object(JsonObject::from(&feature)), @@ -70,13 +79,19 @@ impl From for JsonValue { } } -impl> From for GeoJson { +impl>> From for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn from(geometry: G) -> Self { GeoJson::Geometry(geometry.into()) } } -impl> FromIterator for GeoJson { +impl>> FromIterator for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn from_iter>(iter: I) -> Self { use crate::Value; let geometries = iter.into_iter().map(|g| g.into()).collect(); @@ -85,27 +100,40 @@ impl> FromIterator for GeoJson { } } -impl From for GeoJson { - fn from(feature: Feature) -> Self { +impl From> for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(feature: Feature) -> Self { GeoJson::Feature(feature) } } -impl From for GeoJson { - fn from(feature_collection: FeatureCollection) -> GeoJson { +impl From> for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(feature_collection: FeatureCollection) -> Self { GeoJson::FeatureCollection(feature_collection) } } -impl From> for GeoJson { - fn from(features: Vec) -> GeoJson { - GeoJson::from(features.into_iter().collect::()) +impl From>> for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(features: Vec>) -> Self { + GeoJson::from(features.into_iter().collect::>()) } } -impl TryFrom for Geometry { - type Error = Error; - fn try_from(value: GeoJson) -> Result { +impl TryFrom> for Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; + + fn try_from(value: GeoJson) -> Result { match value { GeoJson::Geometry(g) => Ok(g), GeoJson::Feature(_) => Err(Error::ExpectedType { @@ -120,9 +148,13 @@ impl TryFrom for Geometry { } } -impl TryFrom for Feature { - type Error = Error; - fn try_from(value: GeoJson) -> Result { +impl TryFrom> for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; + + fn try_from(value: GeoJson) -> Result { match value { GeoJson::Geometry(_) => Err(Error::ExpectedType { expected: "Feature".to_string(), @@ -137,9 +169,13 @@ impl TryFrom for Feature { } } -impl TryFrom for FeatureCollection { - type Error = Error; - fn try_from(value: GeoJson) -> Result { +impl TryFrom> for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; + + fn try_from(value: GeoJson) -> Result { match value { GeoJson::Geometry(_) => Err(Error::ExpectedType { expected: "FeatureCollection".to_string(), @@ -154,8 +190,11 @@ impl TryFrom for FeatureCollection { } } -impl GeoJson { - pub fn from_json_object(object: JsonObject) -> Result { +impl GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + pub fn from_json_object(object: JsonObject) -> Result { Self::try_from(object) } @@ -191,7 +230,7 @@ impl GeoJson { /// }) /// ); /// ``` - pub fn from_json_value(value: JsonValue) -> Result { + pub fn from_json_value(value: JsonValue) -> Result { Self::try_from(value) } @@ -201,7 +240,7 @@ impl GeoJson { /// use geojson::GeoJson; /// use serde_json::json; /// - /// let geojson = GeoJson::try_from( json!({ + /// let geojson: GeoJson = GeoJson::try_from( json!({ /// "type": "Feature", /// "geometry": { /// "type": "Point", @@ -235,10 +274,13 @@ impl GeoJson { } } -impl TryFrom for GeoJson { - type Error = Error; +impl TryFrom for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(object: JsonObject) -> Result { + fn try_from(object: JsonObject) -> Result { let type_ = match object.get("type") { Some(JsonValue::String(t)) => Type::from_str(t), _ => return Err(Error::GeometryUnknownType("type".to_owned())), @@ -254,10 +296,13 @@ impl TryFrom for GeoJson { } } -impl TryFrom for GeoJson { - type Error = Error; +impl TryFrom for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(value: JsonValue) -> Result { + fn try_from(value: JsonValue) -> Result { if let JsonValue::Object(obj) = value { Self::try_from(obj) } else { @@ -296,7 +341,10 @@ impl Type { } } -impl Serialize for GeoJson { +impl Serialize for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, @@ -309,8 +357,11 @@ impl Serialize for GeoJson { } } -impl<'de> Deserialize<'de> for GeoJson { - fn deserialize(deserializer: D) -> std::result::Result +impl<'de, T> Deserialize<'de> for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn deserialize(deserializer: D) -> std::result::Result, D::Error> where D: Deserializer<'de>, { @@ -344,31 +395,40 @@ impl<'de> Deserialize<'de> for GeoJson { /// ] /// } /// "#; -/// let geo_json = GeoJson::from_str(&geojson_str).unwrap(); +/// let geo_json: GeoJson = GeoJson::from_str(&geojson_str).unwrap(); /// if let GeoJson::FeatureCollection(collection) = geo_json { /// assert_eq!(1, collection.features.len()); /// } else { /// panic!("expected feature collection"); /// } /// ``` -impl FromStr for GeoJson { - type Err = Error; +impl FromStr for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { let object = get_object(s)?; GeoJson::from_json_object(object) } } -fn get_object(s: &str) -> Result { +fn get_object(s: &str) -> Result +where + T: geo_types::CoordFloat + serde::Serialize, +{ match ::serde_json::from_str(s)? { JsonValue::Object(object) => Ok(object), other => Err(Error::ExpectedObjectValue(other)), } } -impl fmt::Display for GeoJson { +impl fmt::Display for GeoJson +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::serde_json::to_string(self) .map_err(|_| fmt::Error) @@ -376,7 +436,10 @@ impl fmt::Display for GeoJson { } } -impl fmt::Display for Feature { +impl fmt::Display for Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::serde_json::to_string(self) .map_err(|_| fmt::Error) @@ -384,7 +447,10 @@ impl fmt::Display for Feature { } } -impl fmt::Display for Geometry { +impl fmt::Display for Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::serde_json::to_string(self) .map_err(|_| fmt::Error) @@ -392,7 +458,10 @@ impl fmt::Display for Geometry { } } -impl fmt::Display for FeatureCollection { +impl fmt::Display for FeatureCollection +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ::serde_json::to_string(self) .map_err(|_| fmt::Error) @@ -538,7 +607,7 @@ mod tests { ] }"#; assert!(matches!( - GeoJson::from_str(geojson_str), + GeoJson::::from_str(geojson_str), Err(Error::MalformedJson(_)) )) } diff --git a/src/geometry.rs b/src/geometry.rs index a4a129d..17995b4 100644 --- a/src/geometry.rs +++ b/src/geometry.rs @@ -46,44 +46,50 @@ use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer} /// # test() /// ``` #[derive(Clone, Debug, PartialEq)] -pub enum Value { +pub enum Value +where + T: geo_types::CoordFloat + serde::Serialize, +{ /// Point /// /// [GeoJSON Format Specification § 3.1.2](https://tools.ietf.org/html/rfc7946#section-3.1.2) - Point(PointType), + Point(PointType), /// MultiPoint /// /// [GeoJSON Format Specification § 3.1.3](https://tools.ietf.org/html/rfc7946#section-3.1.3) - MultiPoint(Vec), + MultiPoint(Vec>), /// LineString /// /// [GeoJSON Format Specification § 3.1.4](https://tools.ietf.org/html/rfc7946#section-3.1.4) - LineString(LineStringType), + LineString(LineStringType), /// MultiLineString /// /// [GeoJSON Format Specification § 3.1.5](https://tools.ietf.org/html/rfc7946#section-3.1.5) - MultiLineString(Vec), + MultiLineString(Vec>), /// Polygon /// /// [GeoJSON Format Specification § 3.1.6](https://tools.ietf.org/html/rfc7946#section-3.1.6) - Polygon(PolygonType), + Polygon(PolygonType), /// MultiPolygon /// /// [GeoJSON Format Specification § 3.1.7](https://tools.ietf.org/html/rfc7946#section-3.1.7) - MultiPolygon(Vec), + MultiPolygon(Vec>), /// GeometryCollection /// /// [GeoJSON Format Specification § 3.1.8](https://tools.ietf.org/html/rfc7946#section-3.1.8) - GeometryCollection(Vec), + GeometryCollection(Vec>), } -impl Value { +impl Value +where + T: geo_types::CoordFloat + serde::Serialize, +{ pub fn type_name(&self) -> &'static str { match self { Value::Point(..) => "Point", @@ -97,8 +103,11 @@ impl Value { } } -impl<'a> From<&'a Value> for JsonObject { - fn from(value: &'a Value) -> JsonObject { +impl<'a, T> From<&'a Value> for JsonObject +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(value: &'a Value) -> JsonObject { let mut map = JsonObject::new(); map.insert( String::from("type"), @@ -117,12 +126,15 @@ impl<'a> From<&'a Value> for JsonObject { } } -impl Value { - pub fn from_json_object(object: JsonObject) -> Result { +impl Value +where + T: geo_types::CoordFloat + serde::Serialize, +{ + pub fn from_json_object(object: JsonObject) -> Result { Self::try_from(object) } - pub fn from_json_value(value: JsonValue) -> Result { + pub fn from_json_value(value: JsonValue) -> Result { Self::try_from(value) } @@ -142,18 +154,24 @@ impl Value { } } -impl TryFrom for Value { - type Error = Error; +impl TryFrom for Value +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(mut object: JsonObject) -> Result { + fn try_from(mut object: JsonObject) -> Result { util::get_value(&mut object) } } -impl TryFrom for Value { - type Error = Error; +impl TryFrom for Value +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(value: JsonValue) -> Result { + fn try_from(value: JsonValue) -> Result { if let JsonValue::Object(obj) = value { Self::try_from(obj) } else { @@ -162,7 +180,10 @@ impl TryFrom for Value { } } -impl fmt::Display for Value { +impl fmt::Display for Value +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ::serde_json::to_string(&JsonObject::from(self)) .map_err(|_| fmt::Error) @@ -170,13 +191,19 @@ impl fmt::Display for Value { } } -impl<'a> From<&'a Value> for JsonValue { - fn from(value: &'a Value) -> JsonValue { +impl<'a, T> From<&'a Value> for JsonValue +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(value: &'a Value) -> JsonValue { ::serde_json::to_value(value).unwrap() } } -impl Serialize for Value { +impl Serialize for Value +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, @@ -259,22 +286,28 @@ impl Serialize for Value { /// let geom: geo_types::Geometry = geometry.try_into().unwrap(); /// ``` #[derive(Clone, Debug, PartialEq)] -pub struct Geometry { +pub struct Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ /// Bounding Box /// /// [GeoJSON Format Specification § 5](https://tools.ietf.org/html/rfc7946#section-5) - pub bbox: Option, - pub value: Value, + pub bbox: Option>, + pub value: Value, /// Foreign Members /// /// [GeoJSON Format Specification § 6](https://tools.ietf.org/html/rfc7946#section-6) pub foreign_members: Option, } -impl Geometry { +impl Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ /// Returns a new `Geometry` with the specified `value`. `bbox` and `foreign_members` will be /// set to `None`. - pub fn new(value: Value) -> Self { + pub fn new(value: Value) -> Self { Geometry { bbox: None, value, @@ -283,8 +316,11 @@ impl Geometry { } } -impl<'a> From<&'a Geometry> for JsonObject { - fn from(geometry: &'a Geometry) -> JsonObject { +impl<'a, T> From<&'a Geometry> for JsonObject +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn from(geometry: &'a Geometry) -> JsonObject { let mut map = JsonObject::from(&geometry.value); if let Some(ref bbox) = geometry.bbox { map.insert(String::from("bbox"), ::serde_json::to_value(bbox).unwrap()); @@ -299,12 +335,15 @@ impl<'a> From<&'a Geometry> for JsonObject { } } -impl Geometry { - pub fn from_json_object(object: JsonObject) -> Result { +impl Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ + pub fn from_json_object(object: JsonObject) -> Result { Self::try_from(object) } - pub fn from_json_value(value: JsonValue) -> Result { + pub fn from_json_value(value: JsonValue) -> Result { Self::try_from(value) } @@ -326,10 +365,13 @@ impl Geometry { } } -impl TryFrom for Geometry { - type Error = Error; +impl TryFrom for Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(mut object: JsonObject) -> Result { + fn try_from(mut object: JsonObject) -> Result, T> { let bbox = util::get_bbox(&mut object)?; let value = util::get_value(&mut object)?; let foreign_members = util::get_foreign_members(object)?; @@ -341,10 +383,13 @@ impl TryFrom for Geometry { } } -impl TryFrom for Geometry { - type Error = Error; +impl TryFrom for Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Error = Error; - fn try_from(value: JsonValue) -> Result { + fn try_from(value: JsonValue) -> Result { if let JsonValue::Object(obj) = value { Self::try_from(obj) } else { @@ -353,15 +398,21 @@ impl TryFrom for Geometry { } } -impl FromStr for Geometry { - type Err = Error; +impl FromStr for Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ + type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { Self::try_from(crate::GeoJson::from_str(s)?) } } -impl Serialize for Geometry { +impl Serialize for Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, @@ -372,8 +423,11 @@ impl Serialize for Geometry { } } -impl<'de> Deserialize<'de> for Geometry { - fn deserialize(deserializer: D) -> std::result::Result +impl<'de, T> Deserialize<'de> for Geometry +where + T: geo_types::CoordFloat + serde::Serialize, +{ + fn deserialize(deserializer: D) -> std::result::Result, D::Error> where D: Deserializer<'de>, { @@ -385,11 +439,12 @@ impl<'de> Deserialize<'de> for Geometry { } } -impl From for Geometry +impl From for Geometry where - V: Into, + T: geo_types::CoordFloat + serde::Serialize, + V: Into>, { - fn from(v: V) -> Geometry { + fn from(v: V) -> Geometry { Geometry::new(v.into()) } } @@ -538,7 +593,7 @@ mod tests { }) .to_string(); - let geometry = Geometry::from_str(&geometry_json).unwrap(); + let geometry = Geometry::::from_str(&geometry_json).unwrap(); assert!(matches!(geometry.value, Value::Point(_))); } @@ -556,7 +611,7 @@ mod tests { }) .to_string(); - let actual_failure = Geometry::from_str(&feature_json).unwrap_err(); + let actual_failure = Geometry::::from_str(&feature_json).unwrap_err(); match actual_failure { Error::ExpectedType { actual, expected } => { assert_eq!(actual, "Feature"); @@ -568,13 +623,14 @@ mod tests { #[test] fn test_reject_too_few_coordinates() { - let err = Geometry::from_str(r#"{"type": "Point", "coordinates": []}"#).unwrap_err(); + let err = Geometry::::from_str(r#"{"type": "Point", "coordinates": []}"#).unwrap_err(); assert_eq!( err.to_string(), "A position must contain two or more elements, but got `0`" ); - let err = Geometry::from_str(r#"{"type": "Point", "coordinates": [23.42]}"#).unwrap_err(); + let err = + Geometry::::from_str(r#"{"type": "Point", "coordinates": [23.42]}"#).unwrap_err(); assert_eq!( err.to_string(), "A position must contain two or more elements, but got `1`" diff --git a/src/lib.rs b/src/lib.rs index 045faa0..cfd5e4f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -419,16 +419,16 @@ /// Bounding Boxes /// /// [GeoJSON Format Specification § 5](https://tools.ietf.org/html/rfc7946#section-5) -pub type Bbox = Vec; +pub type Bbox = Vec; /// Positions /// /// [GeoJSON Format Specification § 3.1.1](https://tools.ietf.org/html/rfc7946#section-3.1.1) -pub type Position = Vec; +pub type Position = Vec; -pub type PointType = Position; -pub type LineStringType = Vec; -pub type PolygonType = Vec>; +pub type PointType = Position; +pub type LineStringType = Vec>; +pub type PolygonType = Vec>>; mod util; @@ -473,15 +473,18 @@ pub use conversion::quick_collection; /// /// [GeoJSON Format Specification § 3.2](https://tools.ietf.org/html/rfc7946#section-3.2) #[derive(Clone, Debug, Default, PartialEq)] -pub struct Feature { +pub struct Feature +where + T: geo_types::CoordFloat + serde::Serialize, +{ /// Bounding Box /// /// [GeoJSON Format Specification § 5](https://tools.ietf.org/html/rfc7946#section-5) - pub bbox: Option, + pub bbox: Option>, /// Geometry /// /// [GeoJSON Format Specification § 3.2](https://tools.ietf.org/html/rfc7946#section-3.2) - pub geometry: Option, + pub geometry: Option>, /// Identifier /// /// [GeoJSON Format Specification § 3.2](https://tools.ietf.org/html/rfc7946#section-3.2) diff --git a/src/ser.rs b/src/ser.rs index 1a17e5f..f84e8d8 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -206,10 +206,11 @@ where /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. -pub fn to_feature_collection_writer(writer: W, features: &[T]) -> Result<()> +pub fn to_feature_collection_writer(writer: W, features: &[T]) -> Result<(), U> where W: io::Write, T: Serialize, + U: geo_types::CoordFloat + Serialize, { use serde::ser::SerializeMap; @@ -253,10 +254,11 @@ where /// /// assert!(geojson_string.contains(r#""geometry":{"coordinates":[11.1,22.2],"type":"Point"}"#)); /// ``` -pub fn serialize_geometry(geometry: IG, ser: S) -> std::result::Result +pub fn serialize_geometry(geometry: IG, ser: S) -> std::result::Result where - IG: std::convert::TryInto, - S: serde::Serializer, + T: geo_types::CoordFloat + Serialize, + IG: std::convert::TryInto>, + S: Serializer, { geometry .try_into() @@ -280,7 +282,7 @@ where } } -impl<'a, T> serde::Serialize for Features<'a, T> +impl<'a, T> Serialize for Features<'a, T> where T: Serialize, { diff --git a/src/util.rs b/src/util.rs index a1e4897..5c402f6 100644 --- a/src/util.rs +++ b/src/util.rs @@ -16,40 +16,58 @@ use crate::errors::{Error, Result}; use crate::{feature, Bbox, Feature, Geometry, Position, Value}; use crate::{JsonObject, JsonValue}; -pub fn expect_type(value: &mut JsonObject) -> Result { +pub fn expect_type(value: &mut JsonObject) -> Result +where + T: geo_types::CoordFloat + serde::Serialize, +{ let prop = expect_property(value, "type")?; expect_string(prop) } -pub fn expect_string(value: JsonValue) -> Result { +pub fn expect_string(value: JsonValue) -> Result +where + T: geo_types::CoordFloat + serde::Serialize, +{ match value { JsonValue::String(s) => Ok(s), _ => Err(Error::ExpectedStringValue(value)), } } -pub fn expect_f64(value: &JsonValue) -> Result { +pub fn expect_float(value: &JsonValue) -> Result +where + T: geo_types::CoordFloat + serde::Serialize, +{ match value.as_f64() { - Some(v) => Ok(v), - None => Err(Error::ExpectedF64Value), + Some(v) => Ok(T::from(v).unwrap()), + None => Err(Error::ExpectedFloatValue), } } -pub fn expect_array(value: &JsonValue) -> Result<&Vec> { +pub fn expect_array(value: &JsonValue) -> Result<&Vec, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ match value.as_array() { Some(v) => Ok(v), None => Err(Error::ExpectedArrayValue("None".to_string())), } } -fn expect_property(obj: &mut JsonObject, name: &'static str) -> Result { +fn expect_property(obj: &mut JsonObject, name: &'static str) -> Result +where + T: geo_types::CoordFloat + serde::Serialize, +{ match obj.remove(name) { Some(v) => Ok(v), None => Err(Error::ExpectedProperty(name.to_string())), } } -fn expect_owned_array(value: JsonValue) -> Result> { +fn expect_owned_array(value: JsonValue) -> Result, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ match value { JsonValue::Array(v) => Ok(v), _ => match value { @@ -64,19 +82,28 @@ fn expect_owned_array(value: JsonValue) -> Result> { } } -pub(crate) fn expect_owned_object(value: JsonValue) -> Result { +pub(crate) fn expect_owned_object(value: JsonValue) -> Result +where + T: geo_types::CoordFloat + serde::Serialize, +{ match value { JsonValue::Object(o) => Ok(o), _ => Err(Error::ExpectedObjectValue(value)), } } -pub fn get_coords_value(object: &mut JsonObject) -> Result { +pub fn get_coords_value(object: &mut JsonObject) -> Result +where + T: geo_types::CoordFloat + serde::Serialize, +{ expect_property(object, "coordinates") } /// Used by FeatureCollection, Feature, Geometry -pub fn get_bbox(object: &mut JsonObject) -> Result> { +pub fn get_bbox(object: &mut JsonObject) -> Result>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let bbox_json = match object.remove("bbox") { Some(b) => b, None => return Ok(None), @@ -87,13 +114,19 @@ pub fn get_bbox(object: &mut JsonObject) -> Result> { }; let bbox = bbox_array .into_iter() - .map(|i| i.as_f64().ok_or(Error::BboxExpectedNumericValues(i))) - .collect::>>()?; + .map(|i| match i.as_f64() { + Some(v) => Ok(T::from(v).unwrap()), + None => Err(Error::BboxExpectedNumericValues(i)), + }) + .collect::, T>>()?; Ok(Some(bbox)) } /// Used by FeatureCollection, Feature, Geometry -pub fn get_foreign_members(object: JsonObject) -> Result> { +pub fn get_foreign_members(object: JsonObject) -> Result, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ if object.is_empty() { Ok(None) } else { @@ -102,7 +135,10 @@ pub fn get_foreign_members(object: JsonObject) -> Result> { } /// Used by Feature -pub fn get_properties(object: &mut JsonObject) -> Result> { +pub fn get_properties(object: &mut JsonObject) -> Result, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let properties = expect_property(object, "properties"); match properties { Ok(JsonValue::Object(x)) => Ok(Some(x)), @@ -115,7 +151,10 @@ pub fn get_properties(object: &mut JsonObject) -> Result> { /// Retrieve a single Position from the value of the "coordinates" key /// /// Used by Value::Point -pub fn get_coords_one_pos(object: &mut JsonObject) -> Result { +pub fn get_coords_one_pos(object: &mut JsonObject) -> Result, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_json = get_coords_value(object)?; json_to_position(&coords_json) } @@ -123,7 +162,10 @@ pub fn get_coords_one_pos(object: &mut JsonObject) -> Result { /// Retrieve a one dimensional Vec of Positions from the value of the "coordinates" key /// /// Used by Value::MultiPoint and Value::LineString -pub fn get_coords_1d_pos(object: &mut JsonObject) -> Result> { +pub fn get_coords_1d_pos(object: &mut JsonObject) -> Result>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_json = get_coords_value(object)?; json_to_1d_positions(&coords_json) } @@ -131,7 +173,10 @@ pub fn get_coords_1d_pos(object: &mut JsonObject) -> Result> { /// Retrieve a two dimensional Vec of Positions from the value of the "coordinates" key /// /// Used by Value::MultiLineString and Value::Polygon -pub fn get_coords_2d_pos(object: &mut JsonObject) -> Result>> { +pub fn get_coords_2d_pos(object: &mut JsonObject) -> Result>>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_json = get_coords_value(object)?; json_to_2d_positions(&coords_json) } @@ -139,13 +184,19 @@ pub fn get_coords_2d_pos(object: &mut JsonObject) -> Result>> /// Retrieve a three dimensional Vec of Positions from the value of the "coordinates" key /// /// Used by Value::MultiPolygon -pub fn get_coords_3d_pos(object: &mut JsonObject) -> Result>>> { +pub fn get_coords_3d_pos(object: &mut JsonObject) -> Result>>>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_json = get_coords_value(object)?; json_to_3d_positions(&coords_json) } /// Used by Value::GeometryCollection -pub fn get_geometries(object: &mut JsonObject) -> Result> { +pub fn get_geometries(object: &mut JsonObject) -> Result>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let geometries_json = expect_property(object, "geometries")?; let geometries_array = expect_owned_array(geometries_json)?; let mut geometries = Vec::with_capacity(geometries_array.len()); @@ -158,7 +209,10 @@ pub fn get_geometries(object: &mut JsonObject) -> Result> { } /// Used by Feature -pub fn get_id(object: &mut JsonObject) -> Result> { +pub fn get_id(object: &mut JsonObject) -> Result, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ match object.remove("id") { Some(JsonValue::Number(x)) => Ok(Some(feature::Id::Number(x))), Some(JsonValue::String(s)) => Ok(Some(feature::Id::String(s))), @@ -168,7 +222,10 @@ pub fn get_id(object: &mut JsonObject) -> Result> { } /// Used by Geometry, Value -pub fn get_value(object: &mut JsonObject) -> Result { +pub fn get_value(object: &mut JsonObject) -> Result, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let res = &*expect_type(object)?; match res { "Point" => Ok(Value::Point(get_coords_one_pos(object)?)), @@ -183,7 +240,10 @@ pub fn get_value(object: &mut JsonObject) -> Result { } /// Used by Feature -pub fn get_geometry(object: &mut JsonObject) -> Result> { +pub fn get_geometry(object: &mut JsonObject) -> Result>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let geometry = expect_property(object, "geometry")?; match geometry { JsonValue::Object(x) => { @@ -196,31 +256,40 @@ pub fn get_geometry(object: &mut JsonObject) -> Result> { } /// Used by FeatureCollection -pub fn get_features(object: &mut JsonObject) -> Result> { +pub fn get_features(object: &mut JsonObject) -> Result>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let prop = expect_property(object, "features")?; let features_json = expect_owned_array(prop)?; let mut features = Vec::with_capacity(features_json.len()); for feature in features_json { let feature = expect_owned_object(feature)?; - let feature: Feature = Feature::from_json_object(feature)?; + let feature = Feature::from_json_object(feature)?; features.push(feature); } Ok(features) } -fn json_to_position(json: &JsonValue) -> Result { +fn json_to_position(json: &JsonValue) -> Result, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_array = expect_array(json)?; if coords_array.len() < 2 { return Err(Error::PositionTooShort(coords_array.len())); } let mut coords = Vec::with_capacity(coords_array.len()); for position in coords_array { - coords.push(expect_f64(position)?); + coords.push(expect_float(position)?); } Ok(coords) } -fn json_to_1d_positions(json: &JsonValue) -> Result> { +fn json_to_1d_positions(json: &JsonValue) -> Result>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_array = expect_array(json)?; let mut coords = Vec::with_capacity(coords_array.len()); for item in coords_array { @@ -229,7 +298,10 @@ fn json_to_1d_positions(json: &JsonValue) -> Result> { Ok(coords) } -fn json_to_2d_positions(json: &JsonValue) -> Result>> { +fn json_to_2d_positions(json: &JsonValue) -> Result>>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_array = expect_array(json)?; let mut coords = Vec::with_capacity(coords_array.len()); for item in coords_array { @@ -238,7 +310,10 @@ fn json_to_2d_positions(json: &JsonValue) -> Result>> { Ok(coords) } -fn json_to_3d_positions(json: &JsonValue) -> Result>>> { +fn json_to_3d_positions(json: &JsonValue) -> Result>>>, T> +where + T: geo_types::CoordFloat + serde::Serialize, +{ let coords_array = expect_array(json)?; let mut coords = Vec::with_capacity(coords_array.len()); for item in coords_array {