sqlx_postgres/types/geometry/
box.rs1use crate::decode::Decode;
2use crate::encode::{Encode, IsNull};
3use crate::error::BoxDynError;
4use crate::types::Type;
5use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
6use sqlx_core::bytes::Buf;
7use std::str::FromStr;
8
9const ERROR: &str = "error decoding BOX";
10
11#[derive(Debug, Clone, PartialEq)]
31pub struct PgBox {
32 pub upper_right_x: f64,
33 pub upper_right_y: f64,
34 pub lower_left_x: f64,
35 pub lower_left_y: f64,
36}
37
38impl Type<Postgres> for PgBox {
39 fn type_info() -> PgTypeInfo {
40 PgTypeInfo::with_name("box")
41 }
42}
43
44impl PgHasArrayType for PgBox {
45 fn array_type_info() -> PgTypeInfo {
46 PgTypeInfo::with_name("_box")
47 }
48}
49
50impl<'r> Decode<'r, Postgres> for PgBox {
51 fn decode(value: PgValueRef<'r>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
52 match value.format() {
53 PgValueFormat::Text => Ok(PgBox::from_str(value.as_str()?)?),
54 PgValueFormat::Binary => Ok(PgBox::from_bytes(value.as_bytes()?)?),
55 }
56 }
57}
58
59impl<'q> Encode<'q, Postgres> for PgBox {
60 fn produces(&self) -> Option<PgTypeInfo> {
61 Some(PgTypeInfo::with_name("box"))
62 }
63
64 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
65 self.serialize(buf)?;
66 Ok(IsNull::No)
67 }
68}
69
70impl FromStr for PgBox {
71 type Err = BoxDynError;
72
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 let sanitised = s.replace(['(', ')', '[', ']', ' '], "");
75 let mut parts = sanitised.split(',');
76
77 let upper_right_x = parts
78 .next()
79 .and_then(|s| s.parse::<f64>().ok())
80 .ok_or_else(|| format!("{}: could not get upper_right_x from {}", ERROR, s))?;
81
82 let upper_right_y = parts
83 .next()
84 .and_then(|s| s.parse::<f64>().ok())
85 .ok_or_else(|| format!("{}: could not get upper_right_y from {}", ERROR, s))?;
86
87 let lower_left_x = parts
88 .next()
89 .and_then(|s| s.parse::<f64>().ok())
90 .ok_or_else(|| format!("{}: could not get lower_left_x from {}", ERROR, s))?;
91
92 let lower_left_y = parts
93 .next()
94 .and_then(|s| s.parse::<f64>().ok())
95 .ok_or_else(|| format!("{}: could not get lower_left_y from {}", ERROR, s))?;
96
97 if parts.next().is_some() {
98 return Err(format!("{}: too many numbers inputted in {}", ERROR, s).into());
99 }
100
101 Ok(PgBox {
102 upper_right_x,
103 upper_right_y,
104 lower_left_x,
105 lower_left_y,
106 })
107 }
108}
109
110impl PgBox {
111 fn from_bytes(mut bytes: &[u8]) -> Result<PgBox, BoxDynError> {
112 let upper_right_x = bytes.get_f64();
113 let upper_right_y = bytes.get_f64();
114 let lower_left_x = bytes.get_f64();
115 let lower_left_y = bytes.get_f64();
116
117 Ok(PgBox {
118 upper_right_x,
119 upper_right_y,
120 lower_left_x,
121 lower_left_y,
122 })
123 }
124
125 fn serialize(&self, buff: &mut PgArgumentBuffer) -> Result<(), String> {
126 let min_x = &self.upper_right_x.min(self.lower_left_x);
127 let min_y = &self.upper_right_y.min(self.lower_left_y);
128 let max_x = &self.upper_right_x.max(self.lower_left_x);
129 let max_y = &self.upper_right_y.max(self.lower_left_y);
130
131 buff.extend_from_slice(&max_x.to_be_bytes());
132 buff.extend_from_slice(&max_y.to_be_bytes());
133 buff.extend_from_slice(&min_x.to_be_bytes());
134 buff.extend_from_slice(&min_y.to_be_bytes());
135
136 Ok(())
137 }
138
139 #[cfg(test)]
140 fn serialize_to_vec(&self) -> Vec<u8> {
141 let mut buff = PgArgumentBuffer::default();
142 self.serialize(&mut buff).unwrap();
143 buff.to_vec()
144 }
145}
146
147#[cfg(test)]
148mod box_tests {
149
150 use std::str::FromStr;
151
152 use super::PgBox;
153
154 const BOX_BYTES: &[u8] = &[
155 64, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0,
156 0, 0, 0, 0,
157 ];
158
159 #[test]
160 fn can_deserialise_box_type_bytes_in_order() {
161 let pg_box = PgBox::from_bytes(BOX_BYTES).unwrap();
162 assert_eq!(
163 pg_box,
164 PgBox {
165 upper_right_x: 2.,
166 upper_right_y: 2.,
167 lower_left_x: -2.,
168 lower_left_y: -2.
169 }
170 )
171 }
172
173 #[test]
174 fn can_deserialise_box_type_str_first_syntax() {
175 let pg_box = PgBox::from_str("[( 1, 2), (3, 4 )]").unwrap();
176 assert_eq!(
177 pg_box,
178 PgBox {
179 upper_right_x: 1.,
180 upper_right_y: 2.,
181 lower_left_x: 3.,
182 lower_left_y: 4.
183 }
184 );
185 }
186 #[test]
187 fn can_deserialise_box_type_str_second_syntax() {
188 let pg_box = PgBox::from_str("(( 1, 2), (3, 4 ))").unwrap();
189 assert_eq!(
190 pg_box,
191 PgBox {
192 upper_right_x: 1.,
193 upper_right_y: 2.,
194 lower_left_x: 3.,
195 lower_left_y: 4.
196 }
197 );
198 }
199
200 #[test]
201 fn can_deserialise_box_type_str_third_syntax() {
202 let pg_box = PgBox::from_str("(1, 2), (3, 4 )").unwrap();
203 assert_eq!(
204 pg_box,
205 PgBox {
206 upper_right_x: 1.,
207 upper_right_y: 2.,
208 lower_left_x: 3.,
209 lower_left_y: 4.
210 }
211 );
212 }
213
214 #[test]
215 fn can_deserialise_box_type_str_fourth_syntax() {
216 let pg_box = PgBox::from_str("1, 2, 3, 4").unwrap();
217 assert_eq!(
218 pg_box,
219 PgBox {
220 upper_right_x: 1.,
221 upper_right_y: 2.,
222 lower_left_x: 3.,
223 lower_left_y: 4.
224 }
225 );
226 }
227
228 #[test]
229 fn cannot_deserialise_too_many_numbers() {
230 let input_str = "1, 2, 3, 4, 5";
231 let pg_box = PgBox::from_str(input_str);
232 assert!(pg_box.is_err());
233 if let Err(err) = pg_box {
234 assert_eq!(
235 err.to_string(),
236 format!("error decoding BOX: too many numbers inputted in {input_str}")
237 )
238 }
239 }
240
241 #[test]
242 fn cannot_deserialise_too_few_numbers() {
243 let input_str = "1, 2, 3 ";
244 let pg_box = PgBox::from_str(input_str);
245 assert!(pg_box.is_err());
246 if let Err(err) = pg_box {
247 assert_eq!(
248 err.to_string(),
249 format!("error decoding BOX: could not get lower_left_y from {input_str}")
250 )
251 }
252 }
253
254 #[test]
255 fn cannot_deserialise_invalid_numbers() {
256 let input_str = "1, 2, 3, FOUR";
257 let pg_box = PgBox::from_str(input_str);
258 assert!(pg_box.is_err());
259 if let Err(err) = pg_box {
260 assert_eq!(
261 err.to_string(),
262 format!("error decoding BOX: could not get lower_left_y from {input_str}")
263 )
264 }
265 }
266
267 #[test]
268 fn can_deserialise_box_type_str_float() {
269 let pg_box = PgBox::from_str("(1.1, 2.2), (3.3, 4.4)").unwrap();
270 assert_eq!(
271 pg_box,
272 PgBox {
273 upper_right_x: 1.1,
274 upper_right_y: 2.2,
275 lower_left_x: 3.3,
276 lower_left_y: 4.4
277 }
278 );
279 }
280
281 #[test]
282 fn can_serialise_box_type_in_order() {
283 let pg_box = PgBox {
284 upper_right_x: 2.,
285 lower_left_x: -2.,
286 upper_right_y: -2.,
287 lower_left_y: 2.,
288 };
289 assert_eq!(pg_box.serialize_to_vec(), BOX_BYTES,)
290 }
291
292 #[test]
293 fn can_serialise_box_type_out_of_order() {
294 let pg_box = PgBox {
295 upper_right_x: -2.,
296 lower_left_x: 2.,
297 upper_right_y: 2.,
298 lower_left_y: -2.,
299 };
300 assert_eq!(pg_box.serialize_to_vec(), BOX_BYTES,)
301 }
302
303 #[test]
304 fn can_order_box() {
305 let pg_box = PgBox {
306 upper_right_x: -2.,
307 lower_left_x: 2.,
308 upper_right_y: 2.,
309 lower_left_y: -2.,
310 };
311 let bytes = pg_box.serialize_to_vec();
312
313 let pg_box = PgBox::from_bytes(&bytes).unwrap();
314 assert_eq!(
315 pg_box,
316 PgBox {
317 upper_right_x: 2.,
318 upper_right_y: 2.,
319 lower_left_x: -2.,
320 lower_left_y: -2.
321 }
322 )
323 }
324}