postgis_diesel/
points.rs

1use std::io::Cursor;
2
3use crate::{
4    ewkb::{EwkbSerializable, GeometryType, BIG_ENDIAN},
5    types::*,
6};
7use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
8
9#[cfg(feature = "diesel")]
10use crate::ewkb::{read_ewkb_header, write_ewkb_header};
11use crate::sql_types::*;
12
13pub enum Dimension {
14    None = 0,
15    Z = 0x80000000,
16    M = 0x40000000,
17    ZM = 0x40000000 | 0x80000000,
18}
19
20impl EwkbSerializable for Point {
21    fn geometry_type(&self) -> u32 {
22        GeometryType::Point as u32
23    }
24}
25
26impl EwkbSerializable for PointZ {
27    fn geometry_type(&self) -> u32 {
28        GeometryType::Point as u32 | Dimension::Z as u32
29    }
30}
31
32impl EwkbSerializable for PointM {
33    fn geometry_type(&self) -> u32 {
34        GeometryType::Point as u32 | Dimension::M as u32
35    }
36}
37
38impl EwkbSerializable for PointZM {
39    fn geometry_type(&self) -> u32 {
40        GeometryType::Point as u32 | Dimension::ZM as u32
41    }
42}
43
44impl Point {
45    pub fn new(x: f64, y: f64, srid: Option<u32>) -> Self {
46        Point::new_point(x, y, srid, None, None).unwrap()
47    }
48}
49
50impl PointZ {
51    pub fn new(x: f64, y: f64, z: f64, srid: Option<u32>) -> Self {
52        PointZ::new_point(x, y, srid, Some(z), None).unwrap()
53    }
54}
55
56impl PointM {
57    pub fn new(x: f64, y: f64, m: f64, srid: Option<u32>) -> Self {
58        PointM::new_point(x, y, srid, None, Some(m)).unwrap()
59    }
60}
61
62impl PointZM {
63    pub fn new(x: f64, y: f64, z: f64, m: f64, srid: Option<u32>) -> Self {
64        PointZM::new_point(x, y, srid, Some(z), Some(m)).unwrap()
65    }
66}
67
68impl PointT for Point {
69    fn get_x(&self) -> f64 {
70        self.x
71    }
72
73    fn get_y(&self) -> f64 {
74        self.y
75    }
76
77    fn get_srid(&self) -> Option<u32> {
78        self.srid
79    }
80
81    fn get_z(&self) -> Option<f64> {
82        None
83    }
84
85    fn get_m(&self) -> Option<f64> {
86        None
87    }
88
89    fn dimension(&self) -> u32 {
90        0
91    }
92
93    fn new_point(
94        x: f64,
95        y: f64,
96        srid: Option<u32>,
97        z: Option<f64>,
98        m: Option<f64>,
99    ) -> Result<Self, PointConstructorError> {
100        if z.is_some() || m.is_some() {
101            return Err(PointConstructorError {
102                reason: format!("unexpectedly defined Z {:?} or M {:?} for Point", z, m)
103                    .to_string(),
104            });
105        }
106        Ok(Point { x, y, srid })
107    }
108}
109
110impl PointT for PointZ {
111    fn get_x(&self) -> f64 {
112        self.x
113    }
114
115    fn get_y(&self) -> f64 {
116        self.y
117    }
118
119    fn get_srid(&self) -> Option<u32> {
120        self.srid
121    }
122
123    fn get_z(&self) -> Option<f64> {
124        Some(self.z)
125    }
126
127    fn get_m(&self) -> Option<f64> {
128        None
129    }
130
131    fn dimension(&self) -> u32 {
132        Dimension::Z as u32
133    }
134
135    fn new_point(
136        x: f64,
137        y: f64,
138        srid: Option<u32>,
139        z: Option<f64>,
140        m: Option<f64>,
141    ) -> Result<Self, PointConstructorError> {
142        if z.is_none() {
143            return Err(PointConstructorError {
144                reason: "Z is not defined, but mandatory for PointZ".to_string(),
145            });
146        }
147        if m.is_some() {
148            return Err(PointConstructorError {
149                reason: format!("unexpectedly defined M {:?} for PointZ", m).to_string(),
150            });
151        }
152        Ok(PointZ {
153            x,
154            y,
155            z: z.unwrap(),
156            srid,
157        })
158    }
159}
160
161impl PointT for PointM {
162    fn get_x(&self) -> f64 {
163        self.x
164    }
165
166    fn get_y(&self) -> f64 {
167        self.y
168    }
169
170    fn get_srid(&self) -> Option<u32> {
171        self.srid
172    }
173
174    fn get_z(&self) -> Option<f64> {
175        None
176    }
177
178    fn get_m(&self) -> Option<f64> {
179        Some(self.m)
180    }
181
182    fn dimension(&self) -> u32 {
183        Dimension::M as u32
184    }
185
186    fn new_point(
187        x: f64,
188        y: f64,
189        srid: Option<u32>,
190        z: Option<f64>,
191        m: Option<f64>,
192    ) -> Result<Self, PointConstructorError> {
193        if m.is_none() {
194            return Err(PointConstructorError {
195                reason: "M is not defined, but mandatory for PointM".to_string(),
196            });
197        }
198        if z.is_some() {
199            return Err(PointConstructorError {
200                reason: format!("unexpectedly defined Z {:?} for PointM", z).to_string(),
201            });
202        }
203        Ok(PointM {
204            x,
205            y,
206            m: m.unwrap(),
207            srid,
208        })
209    }
210}
211
212impl PointT for PointZM {
213    fn get_x(&self) -> f64 {
214        self.x
215    }
216
217    fn get_y(&self) -> f64 {
218        self.y
219    }
220
221    fn get_srid(&self) -> Option<u32> {
222        self.srid
223    }
224
225    fn get_z(&self) -> Option<f64> {
226        Some(self.z)
227    }
228
229    fn get_m(&self) -> Option<f64> {
230        Some(self.m)
231    }
232
233    fn dimension(&self) -> u32 {
234        Dimension::ZM as u32
235    }
236
237    fn new_point(
238        x: f64,
239        y: f64,
240        srid: Option<u32>,
241        z: Option<f64>,
242        m: Option<f64>,
243    ) -> Result<Self, PointConstructorError> {
244        if z.is_none() {
245            return Err(PointConstructorError {
246                reason: "Z is not defined, but mandatory for PointZM".to_string(),
247            });
248        }
249        if m.is_none() {
250            return Err(PointConstructorError {
251                reason: "M is not defined, but mandatory for PointZM".to_string(),
252            });
253        }
254        Ok(PointZM {
255            x,
256            y,
257            z: z.unwrap(),
258            m: m.unwrap(),
259            srid,
260        })
261    }
262}
263
264macro_rules! impl_point_from_to_sql {
265    ($g:ident, $p:ident) => {
266        #[cfg(feature = "diesel")]
267        impl diesel::deserialize::FromSql<$g, diesel::pg::Pg> for $p {
268            fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
269                let mut r = Cursor::new(bytes.as_bytes());
270                let end = r.read_u8()?;
271                if end == BIG_ENDIAN {
272                    read_point::<BigEndian, $p>(&mut r)
273                } else {
274                    read_point::<LittleEndian, $p>(&mut r)
275                }
276            }
277        }
278
279        #[cfg(feature = "diesel")]
280        impl diesel::serialize::ToSql<$g, diesel::pg::Pg> for $p {
281            fn to_sql(
282                &self,
283                out: &mut diesel::serialize::Output<diesel::pg::Pg>,
284            ) -> diesel::serialize::Result {
285                write_point(self, self.get_srid(), out)?;
286                Ok(diesel::serialize::IsNull::No)
287            }
288        }
289    };
290}
291
292impl_point_from_to_sql!(Geometry, Point);
293impl_point_from_to_sql!(Geometry, PointZ);
294impl_point_from_to_sql!(Geometry, PointM);
295impl_point_from_to_sql!(Geometry, PointZM);
296
297impl_point_from_to_sql!(Geography, Point);
298impl_point_from_to_sql!(Geography, PointZ);
299impl_point_from_to_sql!(Geography, PointM);
300impl_point_from_to_sql!(Geography, PointZM);
301
302#[cfg(feature = "diesel")]
303pub fn write_point<T>(
304    point: &T,
305    srid: Option<u32>,
306    out: &mut diesel::serialize::Output<diesel::pg::Pg>,
307) -> diesel::serialize::Result
308where
309    T: PointT + EwkbSerializable,
310{
311    write_ewkb_header(point, srid, out)?;
312    write_point_coordinates(point, out)?;
313    Ok(diesel::serialize::IsNull::No)
314}
315
316#[cfg(feature = "diesel")]
317pub fn write_point_coordinates<T>(
318    point: &T,
319    out: &mut diesel::serialize::Output<diesel::pg::Pg>,
320) -> diesel::serialize::Result
321where
322    T: PointT,
323{
324    out.write_f64::<LittleEndian>(point.get_x())?;
325    out.write_f64::<LittleEndian>(point.get_y())?;
326    if point.get_z().is_some() {
327        out.write_f64::<LittleEndian>(point.get_z().unwrap())?;
328    }
329    if point.get_m().is_some() {
330        out.write_f64::<LittleEndian>(point.get_m().unwrap())?;
331    }
332    Ok(diesel::serialize::IsNull::No)
333}
334
335#[cfg(feature = "diesel")]
336fn read_point<T, P>(cursor: &mut Cursor<&[u8]>) -> diesel::deserialize::Result<P>
337where
338    T: byteorder::ByteOrder,
339    P: PointT,
340{
341    let g_header = read_ewkb_header::<T>(cursor)?.expect(GeometryType::Point)?;
342    read_point_coordinates::<T, P>(cursor, g_header.g_type, g_header.srid)
343}
344
345#[cfg(feature = "diesel")]
346pub fn read_point_coordinates<T, P>(
347    cursor: &mut Cursor<&[u8]>,
348    g_type: u32,
349    srid: Option<u32>,
350) -> diesel::deserialize::Result<P>
351where
352    T: byteorder::ByteOrder,
353    P: PointT,
354{
355    let x = cursor.read_f64::<T>()?;
356    let y = cursor.read_f64::<T>()?;
357    let mut z = None;
358    if g_type & Dimension::Z as u32 == Dimension::Z as u32 {
359        z = Some(cursor.read_f64::<T>()?);
360    }
361    let mut m = None;
362    if g_type & Dimension::M as u32 == Dimension::M as u32 {
363        m = Some(cursor.read_f64::<T>()?);
364    }
365    Ok(P::new_point(x, y, srid, z, m)?)
366}
367
368#[cfg(test)]
369mod tests {
370    // Note this useful idiom: importing names from outer (for mod tests) scope.
371    use super::*;
372
373    #[test]
374    fn test_point_dimensions() {
375        assert_eq!(
376            Dimension::None as u32,
377            Point::new(0.0, 0.0, None).dimension()
378        );
379        assert_eq!(
380            Dimension::Z as u32,
381            PointZ::new(0.0, 0.0, 0.0, None).dimension()
382        );
383        assert_eq!(
384            Dimension::M as u32,
385            PointM::new(0.0, 0.0, 0.0, None).dimension()
386        );
387        assert_eq!(
388            Dimension::ZM as u32,
389            PointZM::new(0.0, 0.0, 0.0, 0.0, None).dimension()
390        );
391    }
392
393    #[test]
394    #[should_panic(expected = "unexpectedly defined Z Some(1.0) or M Some(1.0) for Point")]
395    fn test_new_point_err() {
396        Point::new_point(72.0, 64.0, None, Some(1.0), Some(1.0)).unwrap();
397    }
398
399    #[test]
400    #[should_panic(expected = "Z is not defined, but mandatory for PointZ")]
401    fn test_new_point_z_not_def_err() {
402        PointZ::new_point(72.0, 64.0, None, None, None).unwrap();
403    }
404
405    #[test]
406    #[should_panic(expected = "unexpectedly defined M Some(1.0) for PointZ")]
407    fn test_new_point_z_m_def_err() {
408        PointZ::new_point(72.0, 64.0, None, Some(1.0), Some(1.0)).unwrap();
409    }
410
411    #[test]
412    #[should_panic(expected = "M is not defined, but mandatory for PointM")]
413    fn test_new_point_m_not_def_err() {
414        PointM::new_point(72.0, 64.0, None, None, None).unwrap();
415    }
416
417    #[test]
418    #[should_panic(expected = "unexpectedly defined Z Some(1.0) for PointM")]
419    fn test_new_point_m_z_def_err() {
420        PointM::new_point(72.0, 64.0, None, Some(1.0), Some(1.0)).unwrap();
421    }
422
423    #[test]
424    #[should_panic(expected = "Z is not defined, but mandatory for PointZM")]
425    fn test_new_point_zm_z_not_def_err() {
426        PointZM::new_point(72.0, 64.0, None, None, Some(1.0)).unwrap();
427    }
428
429    #[test]
430    #[should_panic(expected = "M is not defined, but mandatory for PointZM")]
431    fn test_new_point_zm_m_not_def_err() {
432        PointZM::new_point(72.0, 64.0, None, Some(1.0), None).unwrap();
433    }
434}