sqlx_xugu/types/geometry/
point.rs

1use crate::arguments::XuguArgumentValue;
2use crate::protocol::text::ColumnType;
3use crate::{Xugu, XuguTypeInfo, XuguValueRef};
4use bytes::Buf;
5use sqlx_core::decode::Decode;
6use sqlx_core::encode::{Encode, IsNull};
7use sqlx_core::error::BoxDynError;
8use sqlx_core::types::Type;
9use sqlx_core::Error;
10use std::borrow::Cow;
11use std::fmt::{Display, Formatter};
12use std::str::FromStr;
13
14/// ## Xugu Geometric Point type
15///
16/// Description: Point on a plane
17/// Representation: `(x, y)`
18///
19/// Points are the fundamental two-dimensional building block for geometric types. Values of type point are specified using either of the following syntaxes:
20/// ```text
21/// ( x , y )
22///  x , y
23/// ````
24/// where x and y are the respective coordinates, as floating-point numbers.
25///
26#[derive(Debug, Clone, PartialEq)]
27pub struct XgPoint {
28    pub x: f64,
29    pub y: f64,
30}
31
32impl Type<Xugu> for XgPoint {
33    fn type_info() -> XuguTypeInfo {
34        XuguTypeInfo::binary(ColumnType::POINT)
35    }
36
37    fn compatible(ty: &XuguTypeInfo) -> bool {
38        matches!(
39            ty.r#type,
40            ColumnType::CHAR | ColumnType::POINT | ColumnType::POINT_OLD
41        )
42    }
43}
44
45impl Encode<'_, Xugu> for XgPoint {
46    fn encode_by_ref(&self, args: &mut Vec<XuguArgumentValue>) -> Result<IsNull, BoxDynError> {
47        let s = self.to_string();
48
49        args.push(XuguArgumentValue::Str(Cow::Owned(s)));
50
51        Ok(IsNull::No)
52    }
53
54    fn produces(&self) -> Option<XuguTypeInfo> {
55        Some(XuguTypeInfo::binary(ColumnType::CHAR))
56    }
57}
58
59impl<'r> Decode<'r, Xugu> for XgPoint {
60    fn decode(value: XuguValueRef<'r>) -> Result<Self, BoxDynError> {
61        let ty = value.type_info.r#type;
62        if ty == ColumnType::POINT_OLD {
63            let buf = value.as_bytes()?;
64            return Ok(Self::from_bytes(buf)?);
65        }
66        // 部分旧版的是按字节解码,新版的是字符串
67        if ty == ColumnType::POINT {
68            let buf = value.as_bytes()?;
69            if buf.len() == 16 && buf[0] != b'(' && !buf.contains(&b',') {
70                return Ok(Self::from_bytes(buf)?);
71            }
72        }
73
74        let s = value.as_str()?;
75        Ok(Self::from_str(s)?)
76    }
77}
78
79fn parse_float_from_str(s: &str, error_msg: &str) -> Result<f64, Error> {
80    s.trim()
81        .parse()
82        .map_err(|_| Error::Decode(error_msg.into()))
83}
84
85impl Display for XgPoint {
86    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
87        write!(f, "({},{})", self.x, self.y)
88    }
89}
90
91impl FromStr for XgPoint {
92    type Err = BoxDynError;
93
94    fn from_str(s: &str) -> Result<Self, Self::Err> {
95        let (x_str, y_str) = s
96            .trim_matches(|c| c == '(' || c == ')' || c == ' ')
97            .split_once(',')
98            .ok_or_else(|| {
99                format!(
100                    "[E50044]error decoding POINT: could not get x and y from {}",
101                    s
102                )
103            })?;
104
105        let x = parse_float_from_str(x_str, "[E50044]error decoding POINT: could not get x")?;
106        let y = parse_float_from_str(y_str, "[E50044]error decoding POINT: could not get y")?;
107
108        Ok(Self { x, y })
109    }
110}
111
112impl XgPoint {
113    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, BoxDynError> {
114        let x = bytes.get_f64();
115        let y = bytes.get_f64();
116        Ok(Self { x, y })
117    }
118}