Skip to main content

rbdc_pg/types/
point.rs

1use crate::types::decode::Decode;
2use crate::types::encode::{Encode, IsNull};
3use crate::value::{PgValueFormat, PgValueRef};
4use rbdc::Error;
5use rbs::Value;
6use std::fmt::{Display, Formatter};
7
8/// PostgreSQL POINT type for geometric points
9///
10/// Represents a point in 2D space (x, y).
11/// This implementation uses WKT (Well-Known Text) format for text representation.
12///
13/// # Examples
14///
15/// ```ignore
16/// // Create a point at (116.4, 39.9) - Beijing coordinates
17/// let point = Point { x: 116.4, y: 39.9 };
18///
19/// // WKT format: "POINT(116.4 39.9)"
20/// ```
21///
22/// For more advanced GIS operations, consider using PostGIS extension directly
23/// or the `geo-types` crate for parsing WKT/WKB formats.
24#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq)]
25pub struct Point {
26    pub x: f64,
27    pub y: f64,
28}
29
30impl Display for Point {
31    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32        write!(f, "POINT({} {})", self.x, self.y)
33    }
34}
35
36impl From<Point> for Value {
37    fn from(arg: Point) -> Self {
38        rbs::Value::Ext(
39            "point",
40            Box::new(rbs::Value::Ext(
41                "point",
42                Box::new(rbs::Value::Array(vec![
43                    rbs::Value::F64(arg.x),
44                    rbs::Value::F64(arg.y),
45                ])),
46            )),
47        )
48    }
49}
50
51impl Decode for Point {
52    fn decode(value: PgValueRef) -> Result<Self, Error> {
53        Ok(match value.format() {
54            PgValueFormat::Binary => {
55                // Binary format is WKB (Well-Known Binary)
56                // For simplicity, we don't support direct binary parsing
57                // Applications should use geo-types crate for proper WKB parsing
58                // Or use TEXT format in PostgreSQL: ST_AsText(point_column)
59                return Err(Error::from(
60                    "POINT binary format (WKB) not supported. \
61                     Use TEXT format: ST_AsText(point_column) or parse with geo-types crate.",
62                ));
63            }
64            PgValueFormat::Text => {
65                // Text format is WKT (Well-Known Text): "POINT(x y)"
66                let s = value.as_str()?;
67
68                // Parse WKT format: "POINT(x y)"
69                let s = s.trim();
70                if !s.starts_with("POINT(") || !s.ends_with(')') {
71                    return Err(Error::from(format!(
72                        "Invalid POINT format: {}. Expected 'POINT(x y)'",
73                        s
74                    )));
75                }
76
77                let coords = &s[6..s.len() - 1]; // Remove "POINT(" and ")"
78                let parts: Vec<&str> = coords.split_whitespace().collect();
79
80                if parts.len() != 2 {
81                    return Err(Error::from(format!(
82                        "Invalid POINT coords: {}. Expected 2 values.",
83                        coords
84                    )));
85                }
86
87                let x = parts[0]
88                    .parse::<f64>()
89                    .map_err(|e| Error::from(format!("Invalid x coordinate: {}", e)))?;
90                let y = parts[1]
91                    .parse::<f64>()
92                    .map_err(|e| Error::from(format!("Invalid y coordinate: {}", e)))?;
93
94                Self { x, y }
95            }
96        })
97    }
98}
99
100impl Encode for Point {
101    fn encode(self, _buf: &mut crate::arguments::PgArgumentBuffer) -> Result<IsNull, Error> {
102        // For PostGIS POINT, use TEXT format in your query:
103        // ST_GeomFromText('POINT(116.4 39.9)')
104        Err(Error::from(
105            "POINT encoding not supported. Use PostGIS ST_GeomFromText() or ST_MakePoint() in your query instead."
106        ))
107    }
108}