sqlx_postgres/types/geometry/
point.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 sqlx_core::Error;
8use std::str::FromStr;
9
10#[derive(Debug, Clone, PartialEq)]
27pub struct PgPoint {
28 pub x: f64,
29 pub y: f64,
30}
31
32impl Type<Postgres> for PgPoint {
33 fn type_info() -> PgTypeInfo {
34 PgTypeInfo::with_name("point")
35 }
36}
37
38impl PgHasArrayType for PgPoint {
39 fn array_type_info() -> PgTypeInfo {
40 PgTypeInfo::with_name("_point")
41 }
42}
43
44impl<'r> Decode<'r, Postgres> for PgPoint {
45 fn decode(value: PgValueRef<'r>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
46 match value.format() {
47 PgValueFormat::Text => Ok(PgPoint::from_str(value.as_str()?)?),
48 PgValueFormat::Binary => Ok(PgPoint::from_bytes(value.as_bytes()?)?),
49 }
50 }
51}
52
53impl<'q> Encode<'q, Postgres> for PgPoint {
54 fn produces(&self) -> Option<PgTypeInfo> {
55 Some(PgTypeInfo::with_name("point"))
56 }
57
58 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
59 self.serialize(buf)?;
60 Ok(IsNull::No)
61 }
62}
63
64fn parse_float_from_str(s: &str, error_msg: &str) -> Result<f64, Error> {
65 s.trim()
66 .parse()
67 .map_err(|_| Error::Decode(error_msg.into()))
68}
69
70impl FromStr for PgPoint {
71 type Err = BoxDynError;
72
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 let (x_str, y_str) = s
75 .trim_matches(|c| c == '(' || c == ')' || c == ' ')
76 .split_once(',')
77 .ok_or_else(|| format!("error decoding POINT: could not get x and y from {}", s))?;
78
79 let x = parse_float_from_str(x_str, "error decoding POINT: could not get x")?;
80 let y = parse_float_from_str(y_str, "error decoding POINT: could not get y")?;
81
82 Ok(PgPoint { x, y })
83 }
84}
85
86impl PgPoint {
87 fn from_bytes(mut bytes: &[u8]) -> Result<PgPoint, BoxDynError> {
88 let x = bytes.get_f64();
89 let y = bytes.get_f64();
90 Ok(PgPoint { x, y })
91 }
92
93 fn serialize(&self, buff: &mut PgArgumentBuffer) -> Result<(), BoxDynError> {
94 buff.extend_from_slice(&self.x.to_be_bytes());
95 buff.extend_from_slice(&self.y.to_be_bytes());
96 Ok(())
97 }
98
99 #[cfg(test)]
100 fn serialize_to_vec(&self) -> Vec<u8> {
101 let mut buff = PgArgumentBuffer::default();
102 self.serialize(&mut buff).unwrap();
103 buff.to_vec()
104 }
105}
106
107#[cfg(test)]
108mod point_tests {
109
110 use std::str::FromStr;
111
112 use super::PgPoint;
113
114 const POINT_BYTES: &[u8] = &[
115 64, 0, 204, 204, 204, 204, 204, 205, 64, 20, 204, 204, 204, 204, 204, 205,
116 ];
117
118 #[test]
119 fn can_deserialise_point_type_bytes() {
120 let point = PgPoint::from_bytes(POINT_BYTES).unwrap();
121 assert_eq!(point, PgPoint { x: 2.1, y: 5.2 })
122 }
123
124 #[test]
125 fn can_deserialise_point_type_str() {
126 let point = PgPoint::from_str("(2, 3)").unwrap();
127 assert_eq!(point, PgPoint { x: 2., y: 3. });
128 }
129
130 #[test]
131 fn can_deserialise_point_type_str_float() {
132 let point = PgPoint::from_str("(2.5, 3.4)").unwrap();
133 assert_eq!(point, PgPoint { x: 2.5, y: 3.4 });
134 }
135
136 #[test]
137 fn can_serialise_point_type() {
138 let point = PgPoint { x: 2.1, y: 5.2 };
139 assert_eq!(point.serialize_to_vec(), POINT_BYTES,)
140 }
141}