postgres_types_extra/
pg_path.rs

1use bytes::{Buf, BufMut};
2use postgres_types::{FromSql, IsNull, ToSql, Type, accepts, to_sql_checked};
3use std::{error::Error, fmt};
4
5use super::pg_point::PgPoint;
6
7#[derive(Debug, Clone, PartialEq)]
8pub struct PgPath {
9    pub points: Vec<PgPoint>,
10    pub is_closed: bool,
11}
12
13impl fmt::Display for PgPath {
14    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15        let path_type = if self.is_closed { "(" } else { "[" };
16        write!(
17            f,
18            "{}{}{})",
19            path_type,
20            self.points
21                .iter()
22                .map(|p| p.to_string())
23                .collect::<Vec<_>>()
24                .join(","),
25            if self.is_closed { ")" } else { "]" }
26        )
27    }
28}
29
30impl FromSql<'_> for PgPath {
31    fn from_sql(ty: &Type, mut raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
32        if ty.name() != "path" {
33            return Err("Unexpected type".into());
34        }
35        let is_closed = raw.get_u8() != 0;
36        let npoints = raw.get_i32();
37        let mut points = Vec::with_capacity(npoints as usize);
38        for _ in 0..npoints {
39            let x = raw.get_f64();
40            let y = raw.get_f64();
41            points.push(PgPoint { x, y });
42        }
43        Ok(PgPath { points, is_closed })
44    }
45
46    accepts!(PATH);
47}
48
49impl ToSql for PgPath {
50    fn to_sql(
51        &self,
52        ty: &Type,
53        out: &mut bytes::BytesMut,
54    ) -> Result<postgres_types::IsNull, Box<dyn Error + Sync + Send>>
55    where
56        Self: Sized,
57    {
58        if ty.name() != "path" {
59            return Err("Unexpected type".into());
60        }
61
62        // Write closed flag (u8)
63        out.put_u8(if self.is_closed { 1 } else { 0 });
64
65        // Write number of points (i32)
66        let npoints: i32 = self.points.len().try_into()?;
67        out.put_i32(npoints);
68
69        // Write each point as (f64, f64)
70        for pt in &self.points {
71            out.put_f64(pt.x);
72            out.put_f64(pt.y);
73        }
74
75        Ok(IsNull::No)
76    }
77
78    accepts!(PATH);
79
80    to_sql_checked!();
81}