sqlx_xugu/types/geometry/
box.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 std::borrow::Cow;
10use std::fmt::{Display, Formatter};
11use std::str::FromStr;
12
13const ERROR: &str = "[E50044]Error decoding BOX";
14
15/// ## Xugu Geometric Box type
16///
17/// Description: Rectangular box
18/// Representation: `((upper_right_x,upper_right_y),(lower_left_x,lower_left_y))`
19///
20/// Boxes are represented by pairs of points that are opposite corners of the box. Values of type box are specified using any of the following syntaxes:
21///
22/// ```text
23/// ( ( upper_right_x , upper_right_y ) , ( lower_left_x , lower_left_y ) )
24/// ( upper_right_x , upper_right_y ) , ( lower_left_x , lower_left_y )
25///   upper_right_x , upper_right_y   ,   lower_left_x , lower_left_y
26/// ```
27/// where `(upper_right_x,upper_right_y) and (lower_left_x,lower_left_y)` are any two opposite corners of the box.
28/// Any two opposite corners can be supplied on input, but the values will be reordered as needed to store the upper right and lower left corners, in that order.
29///
30#[derive(Debug, Clone, PartialEq)]
31pub struct XgBox {
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<Xugu> for XgBox {
39    fn type_info() -> XuguTypeInfo {
40        XuguTypeInfo::binary(ColumnType::BOX)
41    }
42
43    fn compatible(ty: &XuguTypeInfo) -> bool {
44        matches!(
45            ty.r#type,
46            ColumnType::CHAR | ColumnType::BOX | ColumnType::BOX_OLD | ColumnType::BOX2D
47        )
48    }
49}
50
51impl<'r> Decode<'r, Xugu> for XgBox {
52    fn decode(value: XuguValueRef<'r>) -> Result<Self, BoxDynError> {
53        let ty = value.type_info.r#type;
54        if ty == ColumnType::BOX_OLD {
55            let buf = value.as_bytes()?;
56            return Ok(Self::from_bytes(buf)?);
57        }
58        // 部分旧版的是按字节解码,新版的是字符串
59        if ty == ColumnType::BOX {
60            let buf = value.as_bytes()?;
61            if buf.len() == 32 && buf[0] != b'(' && !buf.contains(&b',') {
62                return Ok(Self::from_bytes(buf)?);
63            }
64        }
65
66        let s = value.as_str()?;
67        Ok(Self::from_str(s)?)
68    }
69}
70
71impl Encode<'_, Xugu> for XgBox {
72    fn encode_by_ref(&self, args: &mut Vec<XuguArgumentValue>) -> Result<IsNull, BoxDynError> {
73        let s = self.to_string();
74
75        args.push(XuguArgumentValue::Str(Cow::Owned(s)));
76
77        Ok(IsNull::No)
78    }
79
80    fn produces(&self) -> Option<XuguTypeInfo> {
81        Some(XuguTypeInfo::binary(ColumnType::CHAR))
82    }
83}
84
85impl Display for XgBox {
86    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
87        write!(
88            f,
89            "({},{}),({},{})",
90            self.upper_right_x, self.upper_right_y, self.lower_left_x, self.lower_left_y
91        )
92    }
93}
94
95impl FromStr for XgBox {
96    type Err = BoxDynError;
97
98    fn from_str(s: &str) -> Result<Self, Self::Err> {
99        let sanitised = s.replace(['(', ')', '[', ']', ' '], "");
100        let mut parts = sanitised.split(',');
101
102        let upper_right_x = parts
103            .next()
104            .and_then(|s| s.parse::<f64>().ok())
105            .ok_or_else(|| format!("{}: could not get upper_right_x from {}", ERROR, s))?;
106
107        let upper_right_y = parts
108            .next()
109            .and_then(|s| s.parse::<f64>().ok())
110            .ok_or_else(|| format!("{}: could not get upper_right_y from {}", ERROR, s))?;
111
112        let lower_left_x = parts
113            .next()
114            .and_then(|s| s.parse::<f64>().ok())
115            .ok_or_else(|| format!("{}: could not get lower_left_x from {}", ERROR, s))?;
116
117        let lower_left_y = parts
118            .next()
119            .and_then(|s| s.parse::<f64>().ok())
120            .ok_or_else(|| format!("{}: could not get lower_left_y from {}", ERROR, s))?;
121
122        if parts.next().is_some() {
123            return Err(format!("{}: too many numbers inputted in {}", ERROR, s).into());
124        }
125
126        Ok(Self {
127            upper_right_x,
128            upper_right_y,
129            lower_left_x,
130            lower_left_y,
131        })
132    }
133}
134
135impl XgBox {
136    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, BoxDynError> {
137        let upper_right_x = bytes.get_f64();
138        let upper_right_y = bytes.get_f64();
139        let lower_left_x = bytes.get_f64();
140        let lower_left_y = bytes.get_f64();
141
142        Ok(Self {
143            upper_right_x,
144            upper_right_y,
145            lower_left_x,
146            lower_left_y,
147        })
148    }
149}