From e07b7b04e42512433e6b163c9bf96cfbf1192371 Mon Sep 17 00:00:00 2001 From: kestred Date: Fri, 10 Mar 2017 17:20:51 -0700 Subject: [PATCH] Support PATH type with geo::LineString --- README.md | 15 +++++++++++ postgres-shared/src/types/geo.rs | 43 +++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fd938ba..804740ac 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,13 @@ types. The driver currently supports the following conversions: BOX + + + geo::LineString<f64> + (optional) + + PATH + @@ -340,3 +347,11 @@ support is provided optionally by the `with-geo` feature, which adds `ToSql` and [BOX](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6883) support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `Bbox` type. + +### PATH type + +[PATH](https://www.postgresql.org/docs/9.4/static/datatype-geometric.html#AEN6912) +support is provided optionally by the `with-geo` feature, which adds `ToSql` and `FromSql` implementations for `geo`'s `LineString` type. +Paths converted from LineString are always treated as "open" paths. Use the +[pclose](https://www.postgresql.org/docs/8.2/static/functions-geometry.html#FUNCTIONS-GEOMETRY-FUNC-TABLE) +geometric function to insert a closed path. diff --git a/postgres-shared/src/types/geo.rs b/postgres-shared/src/types/geo.rs index 62e38f2a..e6bb2e11 100644 --- a/postgres-shared/src/types/geo.rs +++ b/postgres-shared/src/types/geo.rs @@ -1,7 +1,7 @@ extern crate geo; use postgres_protocol::types; -use self::geo::{Bbox, Point}; +use self::geo::{Bbox, LineString, Point}; use std::error::Error; use types::{FromSql, ToSql, IsNull, Type}; @@ -59,3 +59,44 @@ impl ToSql for Bbox { accepts!(Type::Box); to_sql_checked!(); } + +impl FromSql for LineString { + fn from_sql(_: &Type, raw: &[u8]) -> Result> { + if raw.len() < 5 { + return Err("invalid message length".into()); + } + + // let _ = types::bool_from_sql(&raw[0..1])?; // is path open or closed + let n_points = types::int4_from_sql(&raw[1..5])? as usize; + let raw_points = &raw[5..raw.len()-1]; + if raw_points.len() != 16 * n_points { + return Err("invalid message length".into()); + } + + let mut points = Vec::with_capacity(n_points); + for n in 0..n_points { + let x = types::float8_from_sql(&raw[n..n+8])?; + let y = types::float8_from_sql(&raw[n+8..n+16])?; + points.push(Point::new(x, y)); + } + Ok(LineString(points)) + } + + accepts!(Type::Path); +} + +impl ToSql for LineString { + fn to_sql(&self, _: &Type, out: &mut Vec) -> Result> { + let closed = false; // always encode an open path from LineString + types::bool_to_sql(closed, out); + types::int4_to_sql(self.0.len() as i32, out); + for point in &self.0 { + types::float8_to_sql(point.x(), out); + types::float8_to_sql(point.y(), out); + } + Ok(IsNull::No) + } + + accepts!(Type::Path); + to_sql_checked!(); +}