1use super::sql_types;
2use crate::GeometryError;
3use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
4use diesel::deserialize;
5use diesel::deserialize::FromSql;
6use diesel::mysql::Mysql;
7use diesel::serialize;
8use diesel::serialize::{IsNull, Output, ToSql};
9use geo_types;
10use std::io::{Cursor, Write};
11use std::ops::{Deref, DerefMut};
12use wkb::{WKBReadExt, WKBWriteExt};
13
14pub type SRID = u32;
19
20pub const SRID_CARTESIAN: SRID = 0;
22
23macro_rules! mysql_type {
27 ($type:ident, $geom:ident, $sql_name:ty, $doc:literal) => {
28 #[derive(Debug, Clone, PartialEq, FromSqlRow, AsExpression)]
29 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30 #[sql_type = stringify!($sql_name)]
31 #[doc=$doc]
32 pub struct $type {
36 pub srid: SRID,
37 #[cfg_attr(feature = "serde", serde(flatten))]
38 pub geom: geo_types::$geom<f64>,
39 }
40
41 impl From<geo_types::$geom<f64>> for $type {
42 #[inline]
43 fn from(geom: geo_types::$geom<f64>) -> Self {
44 Self {
45 srid: 0,
46 geom: geom.into(),
47 }
48 }
49 }
50
51 impl AsRef<geo_types::$geom<f64>> for $type {
52 #[inline]
53 fn as_ref(&self) -> &geo_types::$geom<f64> {
54 &self.geom
55 }
56 }
57
58 impl AsMut<geo_types::$geom<f64>> for $type {
59 #[inline]
60 fn as_mut(&mut self) -> &mut geo_types::$geom<f64> {
61 &mut self.geom
62 }
63 }
64
65 impl Deref for $type {
66 type Target = geo_types::$geom<f64>;
67
68 #[inline]
69 fn deref(&self) -> &Self::Target {
70 &self.geom
71 }
72 }
73
74 impl DerefMut for $type {
75 #[inline]
76 fn deref_mut(&mut self) -> &mut Self::Target {
77 &mut self.geom
78 }
79 }
80 };
81}
82
83macro_rules! impl_traits {
85 ($type:ident, $geom:ident, $sql_name:ty) => {
86 impl FromSql<$sql_name, Mysql> for $type {
87 fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
88 let mut bytes = not_none!(bytes);
89 let srid = bytes.read_u32::<LittleEndian>()?;
90 let mut bytes_cursor = Cursor::new(bytes);
91 let geom = bytes_cursor
92 .read_wkb()
93 .map_err(|e| GeometryError::from(e))?;
94 if let geo_types::Geometry::$geom(p) = geom {
95 Ok(Self { srid, geom: p })
96 } else {
97 Err(Box::new(GeometryError::WrongType))
98 }
99 }
100 }
101
102 impl FromSql<$sql_name, Mysql> for Geometry {
103 fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
104 let mut bytes = not_none!(bytes);
105 let srid = bytes.read_u32::<LittleEndian>()?;
106 let mut bytes_cursor = Cursor::new(bytes);
107 let geom = bytes_cursor.read_wkb().map_err(GeometryError::from)?;
108 Ok(Self { srid, geom })
109 }
110 }
111
112 impl FromSql<$sql_name, Mysql> for geo_types::$geom<f64> {
113 fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
114 let wrapped = $type::from_sql(bytes)?;
115 Ok(wrapped.geom)
116 }
117 }
118
119 impl FromSql<$sql_name, Mysql> for geo_types::Geometry<f64> {
120 fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
121 let wrapped = <Geometry as FromSql<$sql_name, Mysql>>::from_sql(bytes)?;
122 Ok(wrapped.geom)
123 }
124 }
125
126 impl ToSql<$sql_name, Mysql> for $type {
127 fn to_sql<W: Write>(&self, out: &mut Output<W, Mysql>) -> serialize::Result {
128 out.write_u32::<LittleEndian>(self.srid)?;
129 out.write_wkb(&geo_types::Geometry::$geom(self.geom.clone()))
130 .map_err(GeometryError::from)?;
131 Ok(IsNull::No)
132 }
133 }
134
135 impl ToSql<sql_types::Geometry, Mysql> for $type {
136 fn to_sql<W: Write>(&self, out: &mut Output<W, Mysql>) -> serialize::Result {
137 out.write_u32::<LittleEndian>(self.srid)?;
138 out.write_wkb(&geo_types::Geometry::$geom(self.geom.clone()))
139 .map_err(GeometryError::from)?;
140 Ok(IsNull::No)
141 }
142 }
143
144 impl ToSql<$sql_name, Mysql> for geo_types::$geom<f64> {
145 fn to_sql<W: Write>(&self, out: &mut Output<W, Mysql>) -> serialize::Result {
146 out.write_u32::<LittleEndian>(0)?;
147 out.write_wkb(&geo_types::Geometry::$geom(self.clone()))
148 .map_err(GeometryError::from)?;
149 Ok(IsNull::No)
150 }
151 }
152
153 impl ToSql<$sql_name, Mysql> for geo_types::Geometry<f64> {
154 fn to_sql<W: Write>(&self, out: &mut Output<W, Mysql>) -> serialize::Result {
155 out.write_u32::<LittleEndian>(0)?;
156 out.write_wkb(self).map_err(GeometryError::from)?;
157 Ok(IsNull::No)
158 }
159 }
160
161 impl From<$type> for Geometry {
162 fn from(other: $type) -> Self {
163 Self {
164 srid: other.srid,
165 geom: geo_types::Geometry::$geom(other.geom),
166 }
167 }
168 }
169
170 impl TryFrom<Geometry> for $type {
171 type Error = <geo_types::$geom<f64> as TryFrom<geo_types::Geometry<f64>>>::Error;
172
173 fn try_from(other: Geometry) -> Result<Self, Self::Error> {
174 Ok(Self {
175 srid: other.srid,
176 geom: other.geom.try_into()?,
177 })
178 }
179 }
180 };
181}
182
183mysql_type!(
184 Point,
185 Point,
186 sql_types::Point,
187 "A single point in 2D space."
188);
189mysql_type!(
190 Polygon,
191 Polygon,
192 sql_types::Polygon,
193 "A bounded two-dimensional area."
194);
195mysql_type!(
196 LineString,
197 LineString,
198 sql_types::LineString,
199 "A linearly interpolated path between locations."
200);
201mysql_type!(
202 MultiPoint,
203 MultiPoint,
204 sql_types::MultiPoint,
205 "A collection of [`Point`][geo_types::Point]s."
206);
207mysql_type!(
208 MultiLineString,
209 MultiLineString,
210 sql_types::MultiLineString,
211 "A collection of [`LineString`][geo_types::LineString]s."
212);
213mysql_type!(
214 MultiPolygon,
215 MultiPolygon,
216 sql_types::MultiPolygon,
217 "A collection of [`Polygon`][geo_types::Polygon]s."
218);
219mysql_type!(
220 GeometryCollection,
221 GeometryCollection,
222 sql_types::GeometryCollection,
223 "A collection of [`Geometry`][geo_types::Geometry] types."
224);
225mysql_type!(
226 Geometry,
227 Geometry,
228 sql_types::Geometry,
229 "An arbitrary geometry."
230);
231mysql_type!(
232 BoundingBox,
233 Rect,
234 sql_types::Polygon,
235 "An axis-aligned bounding rectangle.
236
237MySQL doesn't support native `Rect`s. Corresponding functions like `ST_Envelope()` return
238a `Polygon` of the shape `POLYGON((MINX MINY, MAXX MINY, MAXX MAXY, MINX MAXY, MINX MINY))`
239instead. This struct exists to provide more convenient access to the coordinates if your
240column always contains such polygons.
241
242**Warning**: This can only be unserialized from a [`Polygon`][sql_types::Polygon]
243column. If polygons in other shapes are unserialized, the process may either fail
244or silently result in wrong bounds.
245"
246);
247
248impl_traits!(Point, Point, sql_types::Point);
249impl_traits!(Polygon, Polygon, sql_types::Polygon);
250impl_traits!(LineString, LineString, sql_types::LineString);
251impl_traits!(MultiPoint, MultiPoint, sql_types::MultiPoint);
252impl_traits!(MultiLineString, MultiLineString, sql_types::MultiLineString);
253impl_traits!(MultiPolygon, MultiPolygon, sql_types::MultiPolygon);
254impl_traits!(
255 GeometryCollection,
256 GeometryCollection,
257 sql_types::GeometryCollection
258);
259
260impl FromSql<sql_types::Geometry, Mysql> for Geometry {
261 fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
262 let mut bytes = not_none!(bytes);
263 let srid = bytes.read_u32::<LittleEndian>()?;
264 let mut bytes_cursor = Cursor::new(bytes);
265 let geom = bytes_cursor.read_wkb().map_err(GeometryError::from)?;
266 Ok(Self { srid, geom })
267 }
268}
269
270impl ToSql<sql_types::Geometry, Mysql> for Geometry {
271 fn to_sql<W: Write>(&self, out: &mut Output<W, Mysql>) -> serialize::Result {
272 out.write_u32::<LittleEndian>(self.srid)?;
273 out.write_wkb(&self.geom).map_err(GeometryError::from)?;
274 Ok(IsNull::No)
275 }
276}
277
278impl FromSql<sql_types::Polygon, Mysql> for BoundingBox {
279 fn from_sql(bytes: Option<&[u8]>) -> deserialize::Result<Self> {
280 let polygon = <Polygon as FromSql<sql_types::Polygon, Mysql>>::from_sql(bytes)?;
281 let mut coords = polygon.exterior().coords();
282 let min = match coords.next() {
283 None => return Err(Box::new(GeometryError::InvalidValue)),
284 Some(c) => *c,
285 };
286 if coords.next().is_none() {
287 return Err(Box::new(GeometryError::InvalidValue));
288 }
289 let max = match coords.next() {
290 None => return Err(Box::new(GeometryError::InvalidValue)),
291 Some(c) => *c,
292 };
293 Ok(Self {
294 srid: polygon.srid,
295 geom: geo_types::Rect::new(min, max),
296 })
297 }
298}
299
300impl ToSql<sql_types::Polygon, Mysql> for BoundingBox {
301 fn to_sql<W: Write>(&self, out: &mut Output<W, Mysql>) -> serialize::Result {
302 out.write_u32::<LittleEndian>(self.srid)?;
303 out.write_wkb(&geo_types::Geometry::Polygon(self.to_polygon()))
304 .map_err(GeometryError::from)?;
305 Ok(IsNull::No)
306 }
307}
308
309impl From<BoundingBox> for Geometry {
310 fn from(other: BoundingBox) -> Self {
311 Self {
312 srid: other.srid,
313 geom: geo_types::Geometry::Polygon(other.geom.to_polygon()),
314 }
315 }
316}
317
318impl From<BoundingBox> for Polygon {
319 fn from(other: BoundingBox) -> Self {
320 Self {
321 srid: other.srid,
322 geom: other.geom.to_polygon(),
323 }
324 }
325}
326
327impl Geometry {
328 #[inline]
333 #[must_use]
334 pub const fn empty(srid: SRID) -> Self {
335 Self {
336 srid,
337 geom: geo_types::Geometry::GeometryCollection(geo_types::GeometryCollection(vec![])),
338 }
339 }
340}
341
342impl GeometryCollection {
343 #[inline]
345 #[must_use]
346 pub const fn empty(srid: SRID) -> Self {
347 Self {
348 srid,
349 geom: geo_types::GeometryCollection(vec![]),
350 }
351 }
352}
353
354impl Default for GeometryCollection {
355 fn default() -> Self {
356 Self::empty(0)
357 }
358}
359
360impl Default for Geometry {
361 fn default() -> Self {
362 Self::empty(0)
363 }
364}