sqlx_xugu/types/geometry/
circle.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
14const ERROR: &str = "[E50044]error decoding CIRCLE";
15
16/// ## Xugu Geometric Circle type
17///
18/// Description: Circle
19/// Representation: `< (x, y), radius >` (center point and radius)
20///
21/// ```text
22/// < ( x , y ) , radius >
23/// ( ( x , y ) , radius )
24///   ( x , y ) , radius
25///     x , y   , radius
26/// ```
27/// where `(x,y)` is the center point.
28///
29#[derive(Debug, Clone, PartialEq)]
30pub struct XgCircle {
31    pub x: f64,
32    pub y: f64,
33    pub radius: f64,
34}
35
36impl Type<Xugu> for XgCircle {
37    fn type_info() -> XuguTypeInfo {
38        XuguTypeInfo::binary(ColumnType::CIRCLE)
39    }
40
41    fn compatible(ty: &XuguTypeInfo) -> bool {
42        matches!(ty.r#type, ColumnType::CHAR | ColumnType::CIRCLE)
43    }
44}
45
46impl<'r> Decode<'r, Xugu> for XgCircle {
47    fn decode(value: XuguValueRef<'r>) -> Result<Self, BoxDynError> {
48        let s = value.as_str()?;
49        Ok(Self::from_str(s)?)
50    }
51}
52
53impl Encode<'_, Xugu> for XgCircle {
54    fn encode_by_ref(&self, args: &mut Vec<XuguArgumentValue>) -> Result<IsNull, BoxDynError> {
55        let s = self.to_string();
56
57        args.push(XuguArgumentValue::Str(Cow::Owned(s)));
58
59        Ok(IsNull::No)
60    }
61
62    fn produces(&self) -> Option<XuguTypeInfo> {
63        Some(XuguTypeInfo::binary(ColumnType::CHAR))
64    }
65}
66
67impl Display for XgCircle {
68    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
69        write!(f, "<({},{}),{}>", self.x, self.y, self.radius)
70    }
71}
72
73impl FromStr for XgCircle {
74    type Err = BoxDynError;
75
76    fn from_str(s: &str) -> Result<Self, Self::Err> {
77        let sanitised = s.replace(['<', '>', '(', ')', ' '], "");
78        let mut parts = sanitised.split(',');
79
80        let x = parts
81            .next()
82            .and_then(|s| s.trim().parse::<f64>().ok())
83            .ok_or_else(|| format!("{}: could not get x from {}", ERROR, s))?;
84
85        let y = parts
86            .next()
87            .and_then(|s| s.trim().parse::<f64>().ok())
88            .ok_or_else(|| format!("{}: could not get y from {}", ERROR, s))?;
89
90        let radius = parts
91            .next()
92            .and_then(|s| s.trim().parse::<f64>().ok())
93            .ok_or_else(|| format!("{}: could not get radius from {}", ERROR, s))?;
94
95        if parts.next().is_some() {
96            return Err(format!("{}: too many numbers inputted in {}", ERROR, s).into());
97        }
98
99        if radius < 0. {
100            return Err(format!("{}: cannot have negative radius: {}", ERROR, s).into());
101        }
102
103        Ok(XgCircle { x, y, radius })
104    }
105}
106
107impl XgCircle {
108    pub fn from_bytes(mut bytes: &[u8]) -> Result<XgCircle, Error> {
109        let x = bytes.get_f64();
110        let y = bytes.get_f64();
111        let r = bytes.get_f64();
112        Ok(XgCircle { x, y, radius: r })
113    }
114}