postgis_diesel/
linestring.rs

1use std::fmt::Debug;
2use std::io::Cursor;
3
4use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
5
6#[cfg(feature = "diesel")]
7use crate::ewkb::{read_ewkb_header, write_ewkb_header};
8use crate::points::Dimension;
9#[cfg(feature = "diesel")]
10use crate::points::{read_point_coordinates, write_point_coordinates};
11use crate::sql_types::*;
12use crate::{
13    ewkb::{EwkbSerializable, GeometryType, BIG_ENDIAN},
14    types::{LineString, PointT},
15};
16
17impl<T> EwkbSerializable for LineString<T>
18where
19    T: PointT,
20{
21    fn geometry_type(&self) -> u32 {
22        GeometryType::LineString as u32 | self.dimension()
23    }
24}
25
26impl<T> LineString<T>
27where
28    T: PointT,
29{
30    pub fn new(srid: Option<u32>) -> Self {
31        Self::with_capacity(srid, 0)
32    }
33
34    pub fn with_capacity(srid: Option<u32>, cap: usize) -> Self {
35        LineString {
36            points: Vec::with_capacity(cap),
37            srid,
38        }
39    }
40
41    pub fn add_point(&mut self, point: T) -> &mut Self {
42        self.points.push(point);
43        self
44    }
45
46    pub fn add_points(&mut self, points: impl IntoIterator<Item = T>) -> &mut Self {
47        for point in points {
48            self.points.push(point);
49        }
50        self
51    }
52
53    pub fn dimension(&self) -> u32 {
54        let mut dimension = Dimension::None as u32;
55        if let Some(point) = self.points.first() {
56            dimension |= point.dimension();
57        }
58        dimension
59    }
60}
61
62#[cfg(feature = "diesel")]
63impl<T> diesel::deserialize::FromSql<Geometry, diesel::pg::Pg> for LineString<T>
64where
65    T: PointT + Debug + Clone,
66{
67    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
68        let mut r = Cursor::new(bytes.as_bytes());
69        let end = r.read_u8()?;
70        if end == BIG_ENDIAN {
71            read_linestring::<BigEndian, T>(&mut r)
72        } else {
73            read_linestring::<LittleEndian, T>(&mut r)
74        }
75    }
76}
77
78#[cfg(feature = "diesel")]
79impl<T> diesel::deserialize::FromSql<Geography, diesel::pg::Pg> for LineString<T>
80where
81    T: PointT + Debug + Clone,
82{
83    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
84        diesel::deserialize::FromSql::<Geometry, diesel::pg::Pg>::from_sql(bytes)
85    }
86}
87
88#[cfg(feature = "diesel")]
89impl<T> diesel::serialize::ToSql<Geometry, diesel::pg::Pg> for LineString<T>
90where
91    T: PointT + Debug + EwkbSerializable,
92{
93    fn to_sql(
94        &self,
95        out: &mut diesel::serialize::Output<diesel::pg::Pg>,
96    ) -> diesel::serialize::Result {
97        write_linestring(self, self.srid, out)
98    }
99}
100
101#[cfg(feature = "diesel")]
102impl<T> diesel::serialize::ToSql<Geography, diesel::pg::Pg> for LineString<T>
103where
104    T: PointT + Debug + EwkbSerializable,
105{
106    fn to_sql(
107        &self,
108        out: &mut diesel::serialize::Output<diesel::pg::Pg>,
109    ) -> diesel::serialize::Result {
110        write_linestring(self, self.srid, out)
111    }
112}
113
114#[cfg(feature = "diesel")]
115pub fn write_linestring<T>(
116    linestring: &LineString<T>,
117    srid: Option<u32>,
118    out: &mut diesel::serialize::Output<diesel::pg::Pg>,
119) -> diesel::serialize::Result
120where
121    T: PointT + EwkbSerializable,
122{
123    write_ewkb_header(linestring, srid, out)?;
124    // size and points
125    out.write_u32::<LittleEndian>(linestring.points.len() as u32)?;
126    for point in linestring.points.iter() {
127        write_point_coordinates(point, out)?;
128    }
129    Ok(diesel::serialize::IsNull::No)
130}
131
132#[cfg(feature = "diesel")]
133fn read_linestring<T, P>(cursor: &mut Cursor<&[u8]>) -> diesel::deserialize::Result<LineString<P>>
134where
135    T: byteorder::ByteOrder,
136    P: PointT + Clone,
137{
138    let g_header = read_ewkb_header::<T>(cursor)?.expect(GeometryType::LineString)?;
139    read_linestring_body::<T, P>(g_header.g_type, g_header.srid, cursor)
140}
141
142#[cfg(feature = "diesel")]
143pub fn read_linestring_body<T, P>(
144    g_type: u32,
145    srid: Option<u32>,
146    cursor: &mut Cursor<&[u8]>,
147) -> diesel::deserialize::Result<LineString<P>>
148where
149    T: byteorder::ByteOrder,
150    P: PointT + Clone,
151{
152    let len = cursor.read_u32::<T>()?;
153    let mut ls = LineString::with_capacity(srid, len as usize);
154    for _i in 0..len {
155        ls.add_point(read_point_coordinates::<T, P>(cursor, g_type, srid)?);
156    }
157    Ok(ls)
158}