1use crate::decode::Decode;
2use crate::encode::{Encode, IsNull};
3use crate::error::BoxDynError;
4use crate::types::{PgPoint, Type};
5use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
6use sqlx_core::bytes::Buf;
7use sqlx_core::Error;
8use std::mem;
9use std::str::FromStr;
10
11const BYTE_WIDTH: usize = mem::size_of::<f64>();
12
13#[derive(Debug, Clone, PartialEq)]
36pub struct PgPolygon {
37 pub points: Vec<PgPoint>,
38}
39
40#[derive(Copy, Clone, Debug, PartialEq, Eq)]
41struct Header {
42 length: usize,
43}
44
45impl Type<Postgres> for PgPolygon {
46 fn type_info() -> PgTypeInfo {
47 PgTypeInfo::with_name("polygon")
48 }
49}
50
51impl PgHasArrayType for PgPolygon {
52 fn array_type_info() -> PgTypeInfo {
53 PgTypeInfo::with_name("_polygon")
54 }
55}
56
57impl<'r> Decode<'r, Postgres> for PgPolygon {
58 fn decode(value: PgValueRef<'r>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
59 match value.format() {
60 PgValueFormat::Text => Ok(PgPolygon::from_str(value.as_str()?)?),
61 PgValueFormat::Binary => Ok(PgPolygon::from_bytes(value.as_bytes()?)?),
62 }
63 }
64}
65
66impl<'q> Encode<'q, Postgres> for PgPolygon {
67 fn produces(&self) -> Option<PgTypeInfo> {
68 Some(PgTypeInfo::with_name("polygon"))
69 }
70
71 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
72 self.serialize(buf)?;
73 Ok(IsNull::No)
74 }
75}
76
77impl FromStr for PgPolygon {
78 type Err = Error;
79
80 fn from_str(s: &str) -> Result<Self, Self::Err> {
81 let sanitised = s.replace(['(', ')', '[', ']', ' '], "");
82 let parts = sanitised.split(',').collect::<Vec<_>>();
83
84 let mut points = vec![];
85
86 if parts.len() % 2 != 0 {
87 return Err(Error::Decode(
88 format!("Unmatched pair in POLYGON: {}", s).into(),
89 ));
90 }
91
92 for chunk in parts.chunks_exact(2) {
93 if let [x_str, y_str] = chunk {
94 let x = parse_float_from_str(x_str, "could not get x")?;
95 let y = parse_float_from_str(y_str, "could not get y")?;
96
97 let point = PgPoint { x, y };
98 points.push(point);
99 }
100 }
101
102 if !points.is_empty() {
103 return Ok(PgPolygon { points });
104 }
105
106 Err(Error::Decode(
107 format!("could not get polygon from {}", s).into(),
108 ))
109 }
110}
111
112impl PgPolygon {
113 fn header(&self) -> Header {
114 Header {
115 length: self.points.len(),
116 }
117 }
118
119 fn from_bytes(mut bytes: &[u8]) -> Result<Self, BoxDynError> {
120 let header = Header::try_read(&mut bytes)?;
121
122 if bytes.len() != header.data_size() {
123 return Err(format!(
124 "expected {} bytes after header, got {}",
125 header.data_size(),
126 bytes.len()
127 )
128 .into());
129 }
130
131 if bytes.len() % BYTE_WIDTH * 2 != 0 {
132 return Err(format!(
133 "data length not divisible by pairs of {BYTE_WIDTH}: {}",
134 bytes.len()
135 )
136 .into());
137 }
138
139 let mut out_points = Vec::with_capacity(bytes.len() / (BYTE_WIDTH * 2));
140 while bytes.has_remaining() {
141 let point = PgPoint {
142 x: bytes.get_f64(),
143 y: bytes.get_f64(),
144 };
145 out_points.push(point)
146 }
147 Ok(PgPolygon { points: out_points })
148 }
149
150 fn serialize(&self, buff: &mut PgArgumentBuffer) -> Result<(), BoxDynError> {
151 let header = self.header();
152 buff.reserve(header.data_size());
153 header.try_write(buff)?;
154
155 for point in &self.points {
156 buff.extend_from_slice(&point.x.to_be_bytes());
157 buff.extend_from_slice(&point.y.to_be_bytes());
158 }
159 Ok(())
160 }
161
162 #[cfg(test)]
163 fn serialize_to_vec(&self) -> Vec<u8> {
164 let mut buff = PgArgumentBuffer::default();
165 self.serialize(&mut buff).unwrap();
166 buff.to_vec()
167 }
168}
169
170impl Header {
171 const HEADER_WIDTH: usize = mem::size_of::<i8>() + mem::size_of::<i32>();
172
173 fn data_size(&self) -> usize {
174 self.length * BYTE_WIDTH * 2
175 }
176
177 fn try_read(buf: &mut &[u8]) -> Result<Self, String> {
178 if buf.len() < Self::HEADER_WIDTH {
179 return Err(format!(
180 "expected polygon data to contain at least {} bytes, got {}",
181 Self::HEADER_WIDTH,
182 buf.len()
183 ));
184 }
185
186 let length = buf.get_i32();
187
188 let length = usize::try_from(length).ok().ok_or_else(|| {
189 format!(
190 "received polygon with length: {length}. Expected length between 0 and {}",
191 usize::MAX
192 )
193 })?;
194
195 Ok(Self { length })
196 }
197
198 fn try_write(&self, buff: &mut PgArgumentBuffer) -> Result<(), String> {
199 let length = i32::try_from(self.length).map_err(|_| {
200 format!(
201 "polygon length exceeds allowed maximum ({} > {})",
202 self.length,
203 i32::MAX
204 )
205 })?;
206
207 buff.extend(length.to_be_bytes());
208
209 Ok(())
210 }
211}
212
213fn parse_float_from_str(s: &str, error_msg: &str) -> Result<f64, Error> {
214 s.parse().map_err(|_| Error::Decode(error_msg.into()))
215}
216
217#[cfg(test)]
218mod polygon_tests {
219
220 use std::str::FromStr;
221
222 use crate::types::PgPoint;
223
224 use super::PgPolygon;
225
226 const POLYGON_BYTES: &[u8] = &[
227 0, 0, 0, 12, 192, 0, 0, 0, 0, 0, 0, 0, 192, 8, 0, 0, 0, 0, 0, 0, 191, 240, 0, 0, 0, 0, 0,
228 0, 192, 8, 0, 0, 0, 0, 0, 0, 191, 240, 0, 0, 0, 0, 0, 0, 191, 240, 0, 0, 0, 0, 0, 0, 63,
229 240, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0, 64, 8, 0, 0,
230 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 64, 8, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 192,
231 8, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0, 0, 0, 0, 192, 8, 0, 0, 0, 0, 0, 0, 63, 240, 0, 0, 0,
232 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 191,
233 240, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0,
234 0, 0, 0,
235 ];
236
237 #[test]
238 fn can_deserialise_polygon_type_bytes() {
239 let polygon = PgPolygon::from_bytes(POLYGON_BYTES).unwrap();
240 assert_eq!(
241 polygon,
242 PgPolygon {
243 points: vec![
244 PgPoint { x: -2., y: -3. },
245 PgPoint { x: -1., y: -3. },
246 PgPoint { x: -1., y: -1. },
247 PgPoint { x: 1., y: 1. },
248 PgPoint { x: 1., y: 3. },
249 PgPoint { x: 2., y: 3. },
250 PgPoint { x: 2., y: -3. },
251 PgPoint { x: 1., y: -3. },
252 PgPoint { x: 1., y: 0. },
253 PgPoint { x: -1., y: 0. },
254 PgPoint { x: -1., y: -2. },
255 PgPoint { x: -2., y: -2. }
256 ]
257 }
258 )
259 }
260
261 #[test]
262 fn can_deserialise_polygon_type_str_first_syntax() {
263 let polygon = PgPolygon::from_str("[( 1, 2), (3, 4 )]").unwrap();
264 assert_eq!(
265 polygon,
266 PgPolygon {
267 points: vec![PgPoint { x: 1., y: 2. }, PgPoint { x: 3., y: 4. }]
268 }
269 );
270 }
271
272 #[test]
273 fn can_deserialise_polygon_type_str_second_syntax() {
274 let polygon = PgPolygon::from_str("(( 1, 2), (3, 4 ))").unwrap();
275 assert_eq!(
276 polygon,
277 PgPolygon {
278 points: vec![PgPoint { x: 1., y: 2. }, PgPoint { x: 3., y: 4. }]
279 }
280 );
281 }
282
283 #[test]
284 fn cannot_deserialise_polygon_type_str_uneven_points_first_syntax() {
285 let input_str = "[( 1, 2), (3)]";
286 let polygon = PgPolygon::from_str(input_str);
287
288 assert!(polygon.is_err());
289
290 if let Err(err) = polygon {
291 assert_eq!(
292 err.to_string(),
293 format!("error occurred while decoding: Unmatched pair in POLYGON: {input_str}")
294 )
295 }
296 }
297
298 #[test]
299 fn cannot_deserialise_polygon_type_str_invalid_numbers() {
300 let input_str = "[( 1, 2), (2, three)]";
301 let polygon = PgPolygon::from_str(input_str);
302
303 assert!(polygon.is_err());
304
305 if let Err(err) = polygon {
306 assert_eq!(
307 err.to_string(),
308 format!("error occurred while decoding: could not get y")
309 )
310 }
311 }
312
313 #[test]
314 fn can_deserialise_polygon_type_str_third_syntax() {
315 let polygon = PgPolygon::from_str("(1, 2), (3, 4 )").unwrap();
316 assert_eq!(
317 polygon,
318 PgPolygon {
319 points: vec![PgPoint { x: 1., y: 2. }, PgPoint { x: 3., y: 4. }]
320 }
321 );
322 }
323
324 #[test]
325 fn can_deserialise_polygon_type_str_fourth_syntax() {
326 let polygon = PgPolygon::from_str("1, 2, 3, 4").unwrap();
327 assert_eq!(
328 polygon,
329 PgPolygon {
330 points: vec![PgPoint { x: 1., y: 2. }, PgPoint { x: 3., y: 4. }]
331 }
332 );
333 }
334
335 #[test]
336 fn can_deserialise_polygon_type_str_float() {
337 let polygon = PgPolygon::from_str("(1.1, 2.2), (3.3, 4.4)").unwrap();
338 assert_eq!(
339 polygon,
340 PgPolygon {
341 points: vec![PgPoint { x: 1.1, y: 2.2 }, PgPoint { x: 3.3, y: 4.4 }]
342 }
343 );
344 }
345
346 #[test]
347 fn can_serialise_polygon_type() {
348 let polygon = PgPolygon {
349 points: vec![
350 PgPoint { x: -2., y: -3. },
351 PgPoint { x: -1., y: -3. },
352 PgPoint { x: -1., y: -1. },
353 PgPoint { x: 1., y: 1. },
354 PgPoint { x: 1., y: 3. },
355 PgPoint { x: 2., y: 3. },
356 PgPoint { x: 2., y: -3. },
357 PgPoint { x: 1., y: -3. },
358 PgPoint { x: 1., y: 0. },
359 PgPoint { x: -1., y: 0. },
360 PgPoint { x: -1., y: -2. },
361 PgPoint { x: -2., y: -2. },
362 ],
363 };
364 assert_eq!(polygon.serialize_to_vec(), POLYGON_BYTES,)
365 }
366}