From d5d60fb78bd65bbd377cb506757a0280f666eb23 Mon Sep 17 00:00:00 2001 From: Kristy Brambila Date: Fri, 17 Apr 2020 18:33:17 -0700 Subject: [PATCH] Add support for geo::Line for Postgres --- sqlx-core/src/postgres/protocol/type_id.rs | 4 +- sqlx-core/src/postgres/types/geo.rs | 90 ++++++++++++++++++---- sqlx-core/src/postgres/types/mod.rs | 4 +- 3 files changed, 80 insertions(+), 18 deletions(-) diff --git a/sqlx-core/src/postgres/protocol/type_id.rs b/sqlx-core/src/postgres/protocol/type_id.rs index d3c4d16d06..3232ac779c 100644 --- a/sqlx-core/src/postgres/protocol/type_id.rs +++ b/sqlx-core/src/postgres/protocol/type_id.rs @@ -48,6 +48,7 @@ impl TypeId { pub(crate) const INET: TypeId = TypeId(869); pub(crate) const POINT: TypeId = TypeId(600); + pub(crate) const LSEG: TypeId = TypeId(601); // Arrays @@ -82,8 +83,9 @@ impl TypeId { pub(crate) const ARRAY_CIDR: TypeId = TypeId(651); pub(crate) const ARRAY_INET: TypeId = TypeId(1041); - + pub(crate) const ARRAY_POINT: TypeId = TypeId(1017); + pub(crate) const ARRAY_LSEG: TypeId = TypeId(1018); // JSON diff --git a/sqlx-core/src/postgres/types/geo.rs b/sqlx-core/src/postgres/types/geo.rs index c3b519f212..0892454a18 100644 --- a/sqlx-core/src/postgres/types/geo.rs +++ b/sqlx-core/src/postgres/types/geo.rs @@ -1,12 +1,12 @@ use crate::decode::Decode; use crate::encode::Encode; -use crate::types::Type; -use crate::postgres::protocol::TypeId; -use crate::postgres::{ PgData, PgValue, PgRawBuffer, PgTypeInfo, Postgres }; use crate::io::Buf; -use std::mem; -use geo::Coordinate; +use crate::postgres::protocol::TypeId; +use crate::postgres::{PgData, PgRawBuffer, PgTypeInfo, PgValue, Postgres}; +use crate::types::Type; use byteorder::BigEndian; +use geo::{Coordinate, Line}; +use std::mem; // @@ -19,15 +19,7 @@ impl Type for Coordinate { impl<'de> Decode<'de, Postgres> for Coordinate { fn decode(value: PgValue<'de>) -> crate::Result { match value.try_get()? { - PgData::Binary(mut buf) => { - let x = buf.get_f64::()?; - println!("we have what is hopefully x: {}", x); - - let y = buf.get_f64::()?; - println!("is this a y? {}", y); - - Ok((x, y).into()) - } + PgData::Binary(buf) => decode_coordinate_binary(buf), PgData::Text(s) => { let parens: &[_] = &['(', ')']; @@ -41,13 +33,23 @@ impl<'de> Decode<'de, Postgres> for Coordinate { Ok((x, y).into()) } - _ => Err(crate::Error::Decode(format!("expecting a value with the format \"(x,y)\"").into())) + _ => Err(crate::Error::Decode( + format!("expecting a value with the format \"(x,y)\"").into(), + )), } } } } } +fn decode_coordinate_binary(mut buf: &[u8]) -> crate::Result> { + let x = buf.get_f64::()?; + + let y = buf.get_f64::()?; + + Ok((x, y).into()) +} + impl Encode for Coordinate { fn encode(&self, buf: &mut PgRawBuffer) { Encode::::encode(&self.x, buf); @@ -58,3 +60,61 @@ impl Encode for Coordinate { 2 * mem::size_of::() } } + +impl Type for Line { + fn type_info() -> PgTypeInfo { + PgTypeInfo::new(TypeId::LSEG, "LSEG") + } +} + +impl<'de> Decode<'de, Postgres> for Line { + fn decode(value: PgValue<'de>) -> crate::Result { + match value.try_get()? { + PgData::Binary(mut buf) => { + let start = decode_coordinate_binary(buf)?; + buf.advance(Encode::::size_hint(&start)); + let end = decode_coordinate_binary(buf)?; + + Ok(Line::new(start, end)) + } + + // TODO: is there no way to make this make use of the Decode for Coordinate? + PgData::Text(s) => { + let brackets: &[_] = &['[', ']']; + let mut s = s + .trim_matches(brackets) + .split(|c| c == '(' || c == ')' || c == ',') + .filter_map(|part| if part == "" { None } else { Some(part) }); + + match (s.next(), s.next(), s.next(), s.next()) { + (Some(x1), Some(y1), Some(x2), Some(y2)) => { + let x1 = x1.parse().map_err(crate::Error::decode)?; + let y1 = y1.parse().map_err(crate::Error::decode)?; + let x2 = x2.parse().map_err(crate::Error::decode)?; + let y2 = y2.parse().map_err(crate::Error::decode)?; + + let start = Coordinate::from((x1, y1)); + let end = Coordinate::from((x2, y2)); + + Ok(Line::new(start, end)) + } + + _ => Err(crate::Error::Decode( + format!("expecting a value with the format \"[(x,y),(x,y)]\"").into(), + )), + } + } + } + } +} + +impl Encode for Line { + fn encode(&self, buf: &mut PgRawBuffer) { + Encode::::encode(&self.start, buf); + Encode::::encode(&self.end, buf); + } + + fn size_hint(&self) -> usize { + 2 * Encode::::size_hint(&self.start) + } +} diff --git a/sqlx-core/src/postgres/types/mod.rs b/sqlx-core/src/postgres/types/mod.rs index a45fc996f2..33290c5520 100644 --- a/sqlx-core/src/postgres/types/mod.rs +++ b/sqlx-core/src/postgres/types/mod.rs @@ -68,7 +68,7 @@ //! [`Json`] can be used for structured JSON data with Postgres. //! //! [`Json`]: crate::types::Json -//! +//! //! ### [`geo`](https://crates.io/crates/geo) //! //! Requires the `geo` Cargo feature flag. @@ -76,7 +76,7 @@ //! | Rust type | Postgres type(s) | //! |---------------------------------------|------------------------------------------------------| //! | `geo::Coordinate` | POINT | -//! | `geo::Line` | LINE, LSEG | +//! | `geo::Line` | LSEG | //! | `geo::Rect` | BOX | //! | `geo::LineString` | PATH | //! | `geo::Polygon` | POLYGON |