From 29c4e45587e2f950cfd9c61ece8d7c1812797e33 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 26 Nov 2024 16:03:06 -0500 Subject: [PATCH 1/5] Update geo_types writing --- src/geo_types_to_wkt.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/geo_types_to_wkt.rs b/src/geo_types_to_wkt.rs index e664ae3..10dd9ac 100644 --- a/src/geo_types_to_wkt.rs +++ b/src/geo_types_to_wkt.rs @@ -1,5 +1,6 @@ use geo_types::CoordNum; +use crate::to_wkt::write_point; use crate::types::{ Coord, GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, @@ -35,6 +36,17 @@ where } } +/// A wrapper around something that implements std::io::Write to be used with our writer traits, +/// which require std::fmt::Write +struct WriterWrapper(W); + +impl std::fmt::Write for WriterWrapper { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + self.0.write(s.as_bytes()).map_err(|_| std::fmt::Error)?; + Ok(()) + } +} + /// # Examples /// ``` /// use geo_types::{point, Point}; @@ -51,6 +63,17 @@ where fn to_wkt(&self) -> Wkt { Wkt::Point(g_point_to_w_point(self)) } + + fn wkt_string(&self) -> String { + let mut s = String::new(); + write_point(&mut s, self).unwrap(); + s + } + + fn write_wkt(&self, writer: impl std::io::Write) -> std::io::Result<()> { + write_point(&mut WriterWrapper(writer), self).unwrap(); + Ok(()) + } } /// # Examples From 9b8ca1a6d30ab10f2f6ad0a0b01c67def0618c32 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 26 Nov 2024 16:04:13 -0500 Subject: [PATCH 2/5] wrap multi polygon too --- src/geo_types_to_wkt.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/geo_types_to_wkt.rs b/src/geo_types_to_wkt.rs index 10dd9ac..ea75ad6 100644 --- a/src/geo_types_to_wkt.rs +++ b/src/geo_types_to_wkt.rs @@ -1,6 +1,6 @@ use geo_types::CoordNum; -use crate::to_wkt::write_point; +use crate::to_wkt::{write_multi_polygon, write_point}; use crate::types::{ Coord, GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, @@ -188,6 +188,17 @@ where fn to_wkt(&self) -> Wkt { g_mpolygon_to_w_mpolygon(self).into() } + + fn wkt_string(&self) -> String { + let mut s = String::new(); + write_multi_polygon(&mut s, self).unwrap(); + s + } + + fn write_wkt(&self, writer: impl std::io::Write) -> std::io::Result<()> { + write_multi_polygon(&mut WriterWrapper(writer), self).unwrap(); + Ok(()) + } } /// # Examples From a719912eb3bd65e874c1cfad3d1f3efb7e85234e Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 26 Nov 2024 18:03:08 -0500 Subject: [PATCH 3/5] Update blanket write_wkt impl --- src/geo_types_to_wkt.rs | 34 ---------------------------------- src/to_wkt/mod.rs | 21 +++++++++++++++++++-- 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/src/geo_types_to_wkt.rs b/src/geo_types_to_wkt.rs index ea75ad6..e664ae3 100644 --- a/src/geo_types_to_wkt.rs +++ b/src/geo_types_to_wkt.rs @@ -1,6 +1,5 @@ use geo_types::CoordNum; -use crate::to_wkt::{write_multi_polygon, write_point}; use crate::types::{ Coord, GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, @@ -36,17 +35,6 @@ where } } -/// A wrapper around something that implements std::io::Write to be used with our writer traits, -/// which require std::fmt::Write -struct WriterWrapper(W); - -impl std::fmt::Write for WriterWrapper { - fn write_str(&mut self, s: &str) -> std::fmt::Result { - self.0.write(s.as_bytes()).map_err(|_| std::fmt::Error)?; - Ok(()) - } -} - /// # Examples /// ``` /// use geo_types::{point, Point}; @@ -63,17 +51,6 @@ where fn to_wkt(&self) -> Wkt { Wkt::Point(g_point_to_w_point(self)) } - - fn wkt_string(&self) -> String { - let mut s = String::new(); - write_point(&mut s, self).unwrap(); - s - } - - fn write_wkt(&self, writer: impl std::io::Write) -> std::io::Result<()> { - write_point(&mut WriterWrapper(writer), self).unwrap(); - Ok(()) - } } /// # Examples @@ -188,17 +165,6 @@ where fn to_wkt(&self) -> Wkt { g_mpolygon_to_w_mpolygon(self).into() } - - fn wkt_string(&self) -> String { - let mut s = String::new(); - write_multi_polygon(&mut s, self).unwrap(); - s - } - - fn write_wkt(&self, writer: impl std::io::Write) -> std::io::Result<()> { - write_multi_polygon(&mut WriterWrapper(writer), self).unwrap(); - Ok(()) - } } /// # Examples diff --git a/src/to_wkt/mod.rs b/src/to_wkt/mod.rs index 3581c8b..66c222f 100644 --- a/src/to_wkt/mod.rs +++ b/src/to_wkt/mod.rs @@ -8,6 +8,18 @@ pub use geo_trait_impl::{ write_rect, write_triangle, }; +/// A wrapper around something that implements std::io::Write to be used with our writer traits, +/// which require std::fmt::Write +struct WriterWrapper(W); + +impl std::fmt::Write for WriterWrapper { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + // Sadly, this will lose the content of the error when mapping to std::fmt::Error + self.0.write(s.as_bytes()).map_err(|_| std::fmt::Error)?; + Ok(()) + } +} + /// A trait for converting values to WKT pub trait ToWkt where @@ -49,7 +61,12 @@ where /// /// assert_eq!(wkt_string, "POINT(1.2 3.4)"); /// ``` - fn write_wkt(&self, mut writer: impl std::io::Write) -> std::io::Result<()> { - writer.write_all(self.wkt_string().as_bytes()) + /// + /// ## Panics + /// + /// - If + fn write_wkt(&self, writer: impl std::io::Write) -> std::io::Result<()> { + write_geometry(&mut WriterWrapper(writer), &self.to_wkt()) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err.to_string())) } } From 8270b0a79cdf8f702cc41565a9e10fd979ba130d Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Tue, 26 Nov 2024 18:03:23 -0500 Subject: [PATCH 4/5] update comment --- src/to_wkt/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/to_wkt/mod.rs b/src/to_wkt/mod.rs index 66c222f..cecb137 100644 --- a/src/to_wkt/mod.rs +++ b/src/to_wkt/mod.rs @@ -61,10 +61,6 @@ where /// /// assert_eq!(wkt_string, "POINT(1.2 3.4)"); /// ``` - /// - /// ## Panics - /// - /// - If fn write_wkt(&self, writer: impl std::io::Write) -> std::io::Result<()> { write_geometry(&mut WriterWrapper(writer), &self.to_wkt()) .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err.to_string())) From f468b26b7d55af0aa57e4d31ce9de1ef4cb4fcd8 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 26 Nov 2024 15:55:22 -0800 Subject: [PATCH 5/5] Retain io::error details --- src/to_wkt/mod.rs | 67 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/src/to_wkt/mod.rs b/src/to_wkt/mod.rs index cecb137..7e3e5b6 100644 --- a/src/to_wkt/mod.rs +++ b/src/to_wkt/mod.rs @@ -8,14 +8,31 @@ pub use geo_trait_impl::{ write_rect, write_triangle, }; +use crate::error::Error; +use std::io; + /// A wrapper around something that implements std::io::Write to be used with our writer traits, /// which require std::fmt::Write -struct WriterWrapper(W); +struct WriterWrapper { + writer: W, + most_recent_err: Option, +} + +impl WriterWrapper { + fn new(writer: W) -> Self { + Self { + writer, + most_recent_err: None, + } + } +} -impl std::fmt::Write for WriterWrapper { +impl std::fmt::Write for WriterWrapper { fn write_str(&mut self, s: &str) -> std::fmt::Result { - // Sadly, this will lose the content of the error when mapping to std::fmt::Error - self.0.write(s.as_bytes()).map_err(|_| std::fmt::Error)?; + self.writer.write(s.as_bytes()).map_err(|err| { + self.most_recent_err = Some(err); + std::fmt::Error + })?; Ok(()) } } @@ -61,8 +78,44 @@ where /// /// assert_eq!(wkt_string, "POINT(1.2 3.4)"); /// ``` - fn write_wkt(&self, writer: impl std::io::Write) -> std::io::Result<()> { - write_geometry(&mut WriterWrapper(writer), &self.to_wkt()) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err.to_string())) + fn write_wkt(&self, writer: impl io::Write) -> io::Result<()> { + let mut writer_wrapper = WriterWrapper::new(writer); + write_geometry(&mut writer_wrapper, &self.to_wkt()).map_err(|err| { + match (err, writer_wrapper.most_recent_err) { + (Error::FmtError(_), Some(io_err)) => io_err, + (Error::FmtError(fmt_err), None) => { + debug_assert!(false, "FmtError without setting an error on WriterWrapper"); + io::Error::new(io::ErrorKind::Other, fmt_err.to_string()) + } + (other, _) => io::Error::new(io::ErrorKind::Other, other.to_string()), + } + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "geo-types")] + #[test] + fn write_wkt_error_handling() { + struct FailingWriter; + impl io::Write for FailingWriter { + fn write(&mut self, _buf: &[u8]) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Other, + "FailingWriter always fails", + )) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + let point = geo_types::Point::new(1.2, 3.4); + let err = point.write_wkt(FailingWriter).unwrap_err(); + assert_eq!(err.to_string(), "FailingWriter always fails"); } }