use crate::GeozeroGeometry;
use crate::wkb::{self, FromWkb};
use sqlx::ValueRef;
use sqlx::decode::Decode;
use sqlx::encode::{Encode, IsNull};
use sqlx::postgres::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef, Postgres};
type BoxDynError = Box<dyn std::error::Error + Send + Sync>;
impl<T: FromWkb + Sized> sqlx::Type<Postgres> for wkb::Decode<T> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::with_name("geometry")
}
}
impl<T: FromWkb + Sized> PgHasArrayType for wkb::Decode<T> {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_name("_geometry")
}
}
impl<'de, T: FromWkb + Sized> Decode<'de, Postgres> for wkb::Decode<T> {
fn decode(value: PgValueRef<'de>) -> Result<Self, BoxDynError> {
if value.is_null() {
return Ok(wkb::Decode { geometry: None });
}
let mut blob = <&[u8] as Decode<Postgres>>::decode(value)?;
let geom = T::from_wkb(&mut blob, wkb::WkbDialect::Ewkb)
.map_err(|e| sqlx::Error::Decode(e.to_string().into()))?;
Ok(wkb::Decode {
geometry: Some(geom),
})
}
}
impl<B: AsRef<[u8]>> sqlx::Type<Postgres> for wkb::Ewkb<B> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::with_name("geometry")
}
}
impl<B: AsRef<[u8]>> PgHasArrayType for wkb::Ewkb<B> {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_name("_geometry")
}
}
impl<'de> Decode<'de, Postgres> for wkb::Ewkb<Vec<u8>> {
fn decode(value: PgValueRef<'de>) -> Result<Self, BoxDynError> {
if value.is_null() {
return Ok(wkb::Ewkb(Vec::new()));
}
let blob = <&[u8] as Decode<Postgres>>::decode(value)?;
Ok(wkb::Ewkb(blob.to_vec()))
}
}
impl<T: GeozeroGeometry + Sized> sqlx::Type<Postgres> for wkb::Encode<T> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::with_name("geometry")
}
}
impl<T: GeozeroGeometry + Sized> PgHasArrayType for wkb::Encode<T> {
fn array_type_info() -> PgTypeInfo {
PgTypeInfo::with_name("_geometry")
}
}
impl<T: GeozeroGeometry + Sized> Encode<'_, Postgres> for wkb::Encode<T> {
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
let mut wkb_out: Vec<u8> = Vec::new();
let mut writer = wkb::WkbWriter::with_opts(
&mut wkb_out,
wkb::WkbDialect::Ewkb,
self.0.dims(),
self.0.srid(),
Vec::new(),
);
self.0
.process_geom(&mut writer)
.expect("Failed to encode Geometry");
buf.extend(&wkb_out);
Ok(IsNull::No)
}
}
#[macro_export]
macro_rules! impl_sqlx_postgis_type_info {
( $t:ty ) => {
impl sqlx::Type<sqlx::postgres::Postgres> for $t {
fn type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_name("geometry")
}
}
impl sqlx::postgres::PgHasArrayType for $t {
fn array_type_info() -> sqlx::postgres::PgTypeInfo {
sqlx::postgres::PgTypeInfo::with_name("_geometry")
}
}
};
}
#[macro_export]
macro_rules! impl_sqlx_postgis_decode {
( $t:ty ) => {
impl<'de> sqlx::decode::Decode<'de, sqlx::postgres::Postgres> for $t {
fn decode(
value: sqlx::postgres::PgValueRef<'de>,
) -> std::result::Result<Self, Box<dyn std::error::Error + Send + Sync>> {
use sqlx::ValueRef;
use $crate::wkb::FromWkb;
if value.is_null() {
return Err(Box::new(sqlx::Error::Decode(
"Cannot decode NULL value".into(),
)));
}
let mut blob =
<&[u8] as sqlx::decode::Decode<sqlx::postgres::Postgres>>::decode(value)?;
let geom = <$t>::from_wkb(&mut blob, $crate::wkb::WkbDialect::Ewkb)
.map_err(|e| sqlx::Error::Decode(e.to_string().into()))?;
Ok(geom)
}
}
};
}
#[macro_export]
macro_rules! impl_sqlx_postgis_encode {
( $t:ty ) => {
impl sqlx::encode::Encode<'_, sqlx::postgres::Postgres> for $t {
fn encode_by_ref(
&self,
buf: &mut sqlx::postgres::PgArgumentBuffer,
) -> std::result::Result<
sqlx::encode::IsNull,
Box<(dyn std::error::Error + Send + Sync + 'static)>,
> {
use $crate::GeozeroGeometry;
let mut wkb_out: Vec<u8> = Vec::new();
let mut writer = $crate::wkb::WkbWriter::with_opts(
&mut wkb_out,
$crate::wkb::WkbDialect::Ewkb,
self.dims(),
self.srid(),
Vec::new(),
);
self.process_geom(&mut writer)
.expect("Failed to encode Geometry");
buf.extend(&wkb_out);
std::result::Result::Ok(sqlx::encode::IsNull::No)
}
}
};
}