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