Skip to content

Commit

Permalink
Add support for geo::Line<f64> for Postgres
Browse files Browse the repository at this point in the history
  • Loading branch information
qtbeee committed Apr 18, 2020
1 parent 76b07a8 commit d5d60fb
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 18 deletions.
4 changes: 3 additions & 1 deletion sqlx-core/src/postgres/protocol/type_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
90 changes: 75 additions & 15 deletions sqlx-core/src/postgres/types/geo.rs
Original file line number Diff line number Diff line change
@@ -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;

// <https://www.postgresql.org/docs/12/datatype-geometric.html>

Expand All @@ -19,15 +19,7 @@ impl Type<Postgres> for Coordinate<f64> {
impl<'de> Decode<'de, Postgres> for Coordinate<f64> {
fn decode(value: PgValue<'de>) -> crate::Result<Self> {
match value.try_get()? {
PgData::Binary(mut buf) => {
let x = buf.get_f64::<BigEndian>()?;
println!("we have what is hopefully x: {}", x);

let y = buf.get_f64::<BigEndian>()?;
println!("is this a y? {}", y);

Ok((x, y).into())
}
PgData::Binary(buf) => decode_coordinate_binary(buf),

PgData::Text(s) => {
let parens: &[_] = &['(', ')'];
Expand All @@ -41,13 +33,23 @@ impl<'de> Decode<'de, Postgres> for Coordinate<f64> {
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<Coordinate<f64>> {
let x = buf.get_f64::<BigEndian>()?;

let y = buf.get_f64::<BigEndian>()?;

Ok((x, y).into())
}

impl Encode<Postgres> for Coordinate<f64> {
fn encode(&self, buf: &mut PgRawBuffer) {
Encode::<Postgres>::encode(&self.x, buf);
Expand All @@ -58,3 +60,61 @@ impl Encode<Postgres> for Coordinate<f64> {
2 * mem::size_of::<f64>()
}
}

impl Type<Postgres> for Line<f64> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::new(TypeId::LSEG, "LSEG")
}
}

impl<'de> Decode<'de, Postgres> for Line<f64> {
fn decode(value: PgValue<'de>) -> crate::Result<Self> {
match value.try_get()? {
PgData::Binary(mut buf) => {
let start = decode_coordinate_binary(buf)?;
buf.advance(Encode::<Postgres>::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<Postgres> for Line<f64> {
fn encode(&self, buf: &mut PgRawBuffer) {
Encode::<Postgres>::encode(&self.start, buf);
Encode::<Postgres>::encode(&self.end, buf);
}

fn size_hint(&self) -> usize {
2 * Encode::<Postgres>::size_hint(&self.start)
}
}
4 changes: 2 additions & 2 deletions sqlx-core/src/postgres/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@
//! [`Json<T>`] can be used for structured JSON data with Postgres.
//!
//! [`Json<T>`]: crate::types::Json
//!
//!
//! ### [`geo`](https://crates.io/crates/geo)
//!
//! Requires the `geo` Cargo feature flag.
//!
//! | Rust type | Postgres type(s) |
//! |---------------------------------------|------------------------------------------------------|
//! | `geo::Coordinate<f64>` | POINT |
//! | `geo::Line<f64>` | LINE, LSEG |
//! | `geo::Line<f64>` | LSEG |
//! | `geo::Rect<f64>` | BOX |
//! | `geo::LineString<f64>` | PATH |
//! | `geo::Polygon<f64>` | POLYGON |
Expand Down

0 comments on commit d5d60fb

Please sign in to comment.