Skip to main content

sentinel_driver/types/
geometric.rs

1use bytes::{BufMut, BytesMut};
2
3use crate::error::{Error, Result};
4use crate::types::{FromSql, Oid, ToSql};
5
6/// PostgreSQL POINT type -- 16 bytes (2 x f64).
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct PgPoint {
9    pub x: f64,
10    pub y: f64,
11}
12
13/// PostgreSQL LINE type -- 24 bytes (3 x f64), represents Ax + By + C = 0.
14#[derive(Debug, Clone, Copy, PartialEq)]
15pub struct PgLine {
16    pub a: f64,
17    pub b: f64,
18    pub c: f64,
19}
20
21/// PostgreSQL LSEG type -- 32 bytes (4 x f64), a line segment.
22#[derive(Debug, Clone, Copy, PartialEq)]
23pub struct PgLSeg {
24    pub start: PgPoint,
25    pub end: PgPoint,
26}
27
28/// PostgreSQL BOX type -- 32 bytes (4 x f64), axis-aligned bounding box.
29#[derive(Debug, Clone, Copy, PartialEq)]
30pub struct PgBox {
31    pub upper_right: PgPoint,
32    pub lower_left: PgPoint,
33}
34
35/// PostgreSQL CIRCLE type -- 24 bytes (2 x f64 center + f64 radius).
36#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct PgCircle {
38    pub center: PgPoint,
39    pub radius: f64,
40}
41
42// Helper to read f64 from a byte slice at an offset
43fn read_f64(buf: &[u8], off: usize) -> f64 {
44    f64::from_be_bytes([
45        buf[off],
46        buf[off + 1],
47        buf[off + 2],
48        buf[off + 3],
49        buf[off + 4],
50        buf[off + 5],
51        buf[off + 6],
52        buf[off + 7],
53    ])
54}
55
56// -- PgPoint (16 bytes) --
57
58impl ToSql for PgPoint {
59    fn oid(&self) -> Oid {
60        Oid::POINT
61    }
62
63    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
64        buf.put_f64(self.x);
65        buf.put_f64(self.y);
66        Ok(())
67    }
68}
69
70impl FromSql for PgPoint {
71    fn oid() -> Oid {
72        Oid::POINT
73    }
74
75    fn from_sql(buf: &[u8]) -> Result<Self> {
76        if buf.len() != 16 {
77            return Err(Error::Decode(format!(
78                "point: expected 16 bytes, got {}",
79                buf.len()
80            )));
81        }
82        Ok(PgPoint {
83            x: read_f64(buf, 0),
84            y: read_f64(buf, 8),
85        })
86    }
87}
88
89// -- PgLine (24 bytes) --
90
91impl ToSql for PgLine {
92    fn oid(&self) -> Oid {
93        Oid::LINE
94    }
95
96    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
97        buf.put_f64(self.a);
98        buf.put_f64(self.b);
99        buf.put_f64(self.c);
100        Ok(())
101    }
102}
103
104impl FromSql for PgLine {
105    fn oid() -> Oid {
106        Oid::LINE
107    }
108
109    fn from_sql(buf: &[u8]) -> Result<Self> {
110        if buf.len() != 24 {
111            return Err(Error::Decode(format!(
112                "line: expected 24 bytes, got {}",
113                buf.len()
114            )));
115        }
116        Ok(PgLine {
117            a: read_f64(buf, 0),
118            b: read_f64(buf, 8),
119            c: read_f64(buf, 16),
120        })
121    }
122}
123
124// -- PgLSeg (32 bytes) --
125
126impl ToSql for PgLSeg {
127    fn oid(&self) -> Oid {
128        Oid::LSEG
129    }
130
131    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
132        buf.put_f64(self.start.x);
133        buf.put_f64(self.start.y);
134        buf.put_f64(self.end.x);
135        buf.put_f64(self.end.y);
136        Ok(())
137    }
138}
139
140impl FromSql for PgLSeg {
141    fn oid() -> Oid {
142        Oid::LSEG
143    }
144
145    fn from_sql(buf: &[u8]) -> Result<Self> {
146        if buf.len() != 32 {
147            return Err(Error::Decode(format!(
148                "lseg: expected 32 bytes, got {}",
149                buf.len()
150            )));
151        }
152        Ok(PgLSeg {
153            start: PgPoint {
154                x: read_f64(buf, 0),
155                y: read_f64(buf, 8),
156            },
157            end: PgPoint {
158                x: read_f64(buf, 16),
159                y: read_f64(buf, 24),
160            },
161        })
162    }
163}
164
165// -- PgBox (32 bytes) --
166
167impl ToSql for PgBox {
168    fn oid(&self) -> Oid {
169        Oid::PG_BOX
170    }
171
172    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
173        buf.put_f64(self.upper_right.x);
174        buf.put_f64(self.upper_right.y);
175        buf.put_f64(self.lower_left.x);
176        buf.put_f64(self.lower_left.y);
177        Ok(())
178    }
179}
180
181impl FromSql for PgBox {
182    fn oid() -> Oid {
183        Oid::PG_BOX
184    }
185
186    fn from_sql(buf: &[u8]) -> Result<Self> {
187        if buf.len() != 32 {
188            return Err(Error::Decode(format!(
189                "box: expected 32 bytes, got {}",
190                buf.len()
191            )));
192        }
193        Ok(PgBox {
194            upper_right: PgPoint {
195                x: read_f64(buf, 0),
196                y: read_f64(buf, 8),
197            },
198            lower_left: PgPoint {
199                x: read_f64(buf, 16),
200                y: read_f64(buf, 24),
201            },
202        })
203    }
204}
205
206// -- PgCircle (24 bytes) --
207
208impl ToSql for PgCircle {
209    fn oid(&self) -> Oid {
210        Oid::CIRCLE
211    }
212
213    fn to_sql(&self, buf: &mut BytesMut) -> Result<()> {
214        buf.put_f64(self.center.x);
215        buf.put_f64(self.center.y);
216        buf.put_f64(self.radius);
217        Ok(())
218    }
219}
220
221impl FromSql for PgCircle {
222    fn oid() -> Oid {
223        Oid::CIRCLE
224    }
225
226    fn from_sql(buf: &[u8]) -> Result<Self> {
227        if buf.len() != 24 {
228            return Err(Error::Decode(format!(
229                "circle: expected 24 bytes, got {}",
230                buf.len()
231            )));
232        }
233        Ok(PgCircle {
234            center: PgPoint {
235                x: read_f64(buf, 0),
236                y: read_f64(buf, 8),
237            },
238            radius: read_f64(buf, 16),
239        })
240    }
241}