sqlx_xugu/types/geometry/
polygon.rs1use super::XgPoint;
2use crate::arguments::XuguArgumentValue;
3use crate::protocol::text::ColumnType;
4use crate::{Xugu, XuguTypeInfo, XuguValueRef};
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, Write};
12use std::str::FromStr;
13
14#[derive(Debug, Clone, PartialEq)]
33pub struct XgPolygon {
34 pub points: Vec<XgPoint>,
35}
36
37impl Type<Xugu> for XgPolygon {
38 fn type_info() -> XuguTypeInfo {
39 XuguTypeInfo::binary(ColumnType::POLYGON)
40 }
41
42 fn compatible(ty: &XuguTypeInfo) -> bool {
43 matches!(
44 ty.r#type,
45 ColumnType::CHAR | ColumnType::POLYGON | ColumnType::POLYGON_OLD
46 )
47 }
48}
49
50impl<'r> Decode<'r, Xugu> for XgPolygon {
51 fn decode(value: XuguValueRef<'r>) -> Result<Self, BoxDynError> {
52 let s = value.as_str()?;
53 Ok(Self::from_str(s)?)
54 }
55}
56
57impl Encode<'_, Xugu> for XgPolygon {
58 fn encode_by_ref(&self, args: &mut Vec<XuguArgumentValue>) -> Result<IsNull, BoxDynError> {
59 let s = self.to_string();
60
61 args.push(XuguArgumentValue::Str(Cow::Owned(s)));
62
63 Ok(IsNull::No)
64 }
65
66 fn produces(&self) -> Option<XuguTypeInfo> {
67 Some(XuguTypeInfo::binary(ColumnType::CHAR))
68 }
69}
70
71impl Display for XgPolygon {
72 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
73 f.write_char('(')?;
74 for (i, p) in self.points.iter().enumerate() {
75 if i > 0 {
76 f.write_char(',')?;
77 }
78 Display::fmt(&p, f)?;
79 }
80 f.write_char(')')
81 }
82}
83
84fn parse_float_from_str(s: &str, error_msg: &str) -> Result<f64, Error> {
85 s.parse().map_err(|_| Error::Decode(error_msg.into()))
86}
87
88impl FromStr for XgPolygon {
89 type Err = Error;
90
91 fn from_str(s: &str) -> Result<Self, Self::Err> {
92 let sanitised = s.replace(['(', ')', '[', ']', ' '], "");
93 let parts = sanitised.split(',').collect::<Vec<_>>();
94
95 let mut points = vec![];
96
97 if parts.len() % 2 != 0 {
98 return Err(Error::Decode(
99 format!("Unmatched pair in POLYGON: {}", s).into(),
100 ));
101 }
102
103 for chunk in parts.chunks_exact(2) {
104 if let [x_str, y_str] = chunk {
105 let x = parse_float_from_str(x_str, "could not get x")?;
106 let y = parse_float_from_str(y_str, "could not get y")?;
107
108 let point = XgPoint { x, y };
109 points.push(point);
110 }
111 }
112
113 if !points.is_empty() {
114 return Ok(Self { points });
115 }
116
117 Err(Error::Decode(
118 format!("could not get polygon from {}", s).into(),
119 ))
120 }
121}