sqlx_xugu/types/geometry/
box.rs1use 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#[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 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}