pgwire 0.38.3

Postgresql wire protocol implemented as a library
Documentation
use std::error::Error;
use std::io::Cursor;

use bytes::{BufMut, BytesMut};
use postgis::ewkb::{
    self, AsEwkbGeometry, AsEwkbGeometryCollection, AsEwkbLineString, AsEwkbMultiLineString,
    AsEwkbMultiPoint, AsEwkbMultiPolygon, AsEwkbPoint, AsEwkbPolygon, EwkbRead, EwkbWrite,
};
use postgres_types::Type;

use crate::types::format::FormatOptions;
use crate::types::{FromSqlText, ToSqlText};

macro_rules! impl_postgis_type {
    ($type:ty) => {
        impl<'a> FromSqlText<'a> for $type {
            fn from_sql_text(
                _ty: &Type,
                input: &'a [u8],
                _format_options: &FormatOptions,
            ) -> Result<Self, Box<dyn Error + Sync + Send>>
            where
                Self: Sized,
            {
                read_ewkb::<Self>(input)
            }
        }

        impl ToSqlText for $type {
            fn to_sql_text(
                &self,
                _ty: &Type,
                out: &mut BytesMut,
                _format_options: &FormatOptions,
            ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
            where
                Self: Sized,
            {
                write_ewkb(&self.as_ewkb(), out)
            }
        }
    };
}

fn read_ewkb<T: EwkbRead>(input: &[u8]) -> Result<T, Box<dyn Error + Sync + Send>> {
    let bytes = hex::decode(input).map_err(Box::new)?;
    let mut cursor = Cursor::new(bytes.as_slice());
    T::read_ewkb(&mut cursor).map_err(|e| Box::new(e) as Box<dyn Error + Sync + Send>)
}

fn write_ewkb<T: EwkbWrite>(
    data: &T,
    out: &mut BytesMut,
) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
    let hex = data.to_hex_ewkb();
    out.put_slice(hex.as_bytes());
    Ok(postgres_types::IsNull::No)
}

impl_postgis_type!(ewkb::Point);
impl_postgis_type!(ewkb::PointZ);
impl_postgis_type!(ewkb::PointM);
impl_postgis_type!(ewkb::PointZM);
impl_postgis_type!(ewkb::LineString);
impl_postgis_type!(ewkb::LineStringZ);
impl_postgis_type!(ewkb::LineStringM);
impl_postgis_type!(ewkb::LineStringZM);
impl_postgis_type!(ewkb::Polygon);
impl_postgis_type!(ewkb::PolygonZ);
impl_postgis_type!(ewkb::PolygonM);
impl_postgis_type!(ewkb::PolygonZM);
impl_postgis_type!(ewkb::MultiPoint);
impl_postgis_type!(ewkb::MultiPointZ);
impl_postgis_type!(ewkb::MultiPointM);
impl_postgis_type!(ewkb::MultiPointZM);
impl_postgis_type!(ewkb::MultiLineString);
impl_postgis_type!(ewkb::MultiLineStringZ);
impl_postgis_type!(ewkb::MultiLineStringM);
impl_postgis_type!(ewkb::MultiLineStringZM);
impl_postgis_type!(ewkb::MultiPolygon);
impl_postgis_type!(ewkb::MultiPolygonZ);
impl_postgis_type!(ewkb::MultiPolygonM);
impl_postgis_type!(ewkb::MultiPolygonZM);
impl_postgis_type!(ewkb::Geometry);
impl_postgis_type!(ewkb::GeometryZ);
impl_postgis_type!(ewkb::GeometryM);
impl_postgis_type!(ewkb::GeometryZM);
impl_postgis_type!(ewkb::GeometryCollection);
impl_postgis_type!(ewkb::GeometryCollectionZ);
impl_postgis_type!(ewkb::GeometryCollectionM);
impl_postgis_type!(ewkb::GeometryCollectionZM);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_point_to_hex() {
        let point = ewkb::Point {
            x: 1.0,
            y: 2.0,
            srid: None,
        };
        let mut buf = BytesMut::new();
        point
            .to_sql_text(&Type::TEXT, &mut buf, &FormatOptions::default())
            .unwrap();
        let output = String::from_utf8_lossy(&buf[..]);
        assert_eq!(output, "0101000000000000000000F03F0000000000000040");
    }
}