postgis_diesel/
types.rs

1use std::fmt;
2
3use crate::sql_types::Geography;
4use crate::sql_types::Geometry;
5
6/// Error which may be returned if point constructed without required fields or has some unexpected fields for type.
7/// ```
8/// use postgis_diesel::types::{PointT, PointZ, Point, PointConstructorError};
9/// let point = PointZ::new_point(72.0, 63.0, None, None, None);
10/// assert!(point.is_err());
11/// assert_eq!(Result::Err(PointConstructorError{reason:"Z is not defined, but mandatory for PointZ".to_string()}), point);
12/// let point = Point::new_point(72.0, 63.0, None, Some(10.0), None);
13/// assert!(point.is_err());
14/// assert_eq!(Result::Err(PointConstructorError{reason:"unexpectedly defined Z Some(10.0) or M None for Point".to_string()}), point);
15/// ```
16#[derive(Debug, Clone, PartialEq)]
17pub struct PointConstructorError {
18    pub reason: String,
19}
20
21impl fmt::Display for PointConstructorError {
22    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23        write!(f, "can't construct point: {}", self.reason)
24    }
25}
26
27impl std::error::Error for PointConstructorError {}
28
29/// Use that structure in `Insertable` or `Queryable` struct if you work with Point geometry.
30/// ```
31/// #[macro_use] extern crate diesel;
32/// use postgis_diesel::types::Point;
33/// #[derive(Queryable)]
34/// struct QueryablePointExample {
35///     id: i32,
36///     point: Point,
37/// }
38/// ```
39#[derive(Copy, Clone, Debug, PartialEq)]
40#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
41#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
42#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
43#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44#[cfg_attr(feature = "schemars", derive(JsonSchema))]
45pub struct Point {
46    pub x: f64,
47    pub y: f64,
48    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
49    pub srid: Option<u32>,
50}
51
52/// Use that structure in `Insertable` or `Queryable` struct if you work with PointZ geometry.
53/// ```
54/// #[macro_use] extern crate diesel;
55/// use postgis_diesel::types::PointZ;
56/// #[derive(Queryable)]
57/// struct QueryablePointZExample {
58///     id: i32,
59///     point: PointZ,
60/// }
61/// ```
62#[derive(Copy, Clone, Debug, PartialEq)]
63#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
64#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
65#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
66#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
67#[cfg_attr(feature = "schemars", derive(JsonSchema))]
68pub struct PointZ {
69    pub x: f64,
70    pub y: f64,
71    pub z: f64,
72    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
73    pub srid: Option<u32>,
74}
75
76/// Use that structure in `Insertable` or `Queryable` struct if you work with PointM geometry.
77/// ```
78/// #[macro_use] extern crate diesel;
79/// use postgis_diesel::types::PointM;
80/// #[derive(Queryable)]
81/// struct QueryablePointMExample {
82///     id: i32,
83///     point: PointM,
84/// }
85/// ```
86#[derive(Copy, Clone, Debug, PartialEq)]
87#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
88#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
89#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
90#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
91#[cfg_attr(feature = "schemars", derive(JsonSchema))]
92pub struct PointM {
93    pub x: f64,
94    pub y: f64,
95    pub m: f64,
96    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
97    pub srid: Option<u32>,
98}
99
100/// Use that structure in `Insertable` or `Queryable` struct if you work with PointZM geometry.
101/// ```
102/// #[macro_use] extern crate diesel;
103/// use postgis_diesel::types::PointZM;
104/// #[derive(Queryable)]
105/// struct QueryablePointZMExample {
106///     id: i32,
107///     point: PointZM,
108/// }
109/// ```
110#[derive(Copy, Clone, Debug, PartialEq)]
111#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
112#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
113#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
114#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
115#[cfg_attr(feature = "schemars", derive(JsonSchema))]
116pub struct PointZM {
117    pub x: f64,
118    pub y: f64,
119    pub z: f64,
120    pub m: f64,
121    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
122    pub srid: Option<u32>,
123}
124
125/// Allows uniform access across the four point types
126pub trait PointT {
127    fn new_point(
128        x: f64,
129        y: f64,
130        srid: Option<u32>,
131        z: Option<f64>,
132        m: Option<f64>,
133    ) -> Result<Self, PointConstructorError>
134    where
135        Self: Sized;
136    fn get_x(&self) -> f64;
137    fn get_y(&self) -> f64;
138    fn get_srid(&self) -> Option<u32>;
139    fn get_z(&self) -> Option<f64>;
140    fn get_m(&self) -> Option<f64>;
141    fn dimension(&self) -> u32;
142}
143
144/// Use that structure in `Insertable` or `Queryable` struct if you work with MultiPoint geometry.
145/// ```
146/// #[macro_use] extern crate diesel;
147/// use postgis_diesel::types::{MultiPoint,Point};
148/// #[derive(Queryable)]
149/// struct QueryableMultiPointExample {
150///     id: i32,
151///     multipoint: MultiPoint<Point>,
152/// }
153/// ```
154#[derive(Clone, Debug, PartialEq)]
155#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
156#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
157#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
158#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
159#[cfg_attr(feature = "schemars", derive(JsonSchema))]
160pub struct MultiPoint<T> {
161    pub points: Vec<T>,
162    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
163    pub srid: Option<u32>,
164}
165
166/// Use that structure in `Insertable` or `Queryable` struct if you work with LineString geometry.
167/// ```
168/// #[macro_use] extern crate diesel;
169/// use postgis_diesel::types::{LineString,Point};
170/// #[derive(Queryable)]
171/// struct QueryableLineStringExample {
172///     id: i32,
173///     linestring: LineString<Point>,
174/// }
175/// ```
176#[derive(Clone, Debug, PartialEq)]
177#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
178#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
179#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
180#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
181#[cfg_attr(feature = "schemars", derive(JsonSchema))]
182pub struct LineString<T> {
183    pub points: Vec<T>,
184    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
185    pub srid: Option<u32>,
186}
187
188/// Use that structure in `Insertable` or `Queryable` struct if you work with MultiLineString geometry.
189/// ```
190/// #[macro_use] extern crate diesel;
191/// use postgis_diesel::types::{MultiLineString, LineString,Point};
192/// #[derive(Queryable)]
193/// struct QueryableMultiLineStringExample {
194///     id: i32,
195///     multilinestring: MultiLineString<LineString<Point>>,
196/// }
197/// ```
198#[derive(Clone, Debug, PartialEq)]
199#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
200#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
201#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
202#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
203#[cfg_attr(feature = "schemars", derive(JsonSchema))]
204pub struct MultiLineString<T> {
205    pub lines: Vec<LineString<T>>,
206    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
207    pub srid: Option<u32>,
208}
209
210/// Use that structure in `Insertable` or `Queryable` struct if you work with Polygon geometry.
211/// ```
212/// #[macro_use] extern crate diesel;
213/// use postgis_diesel::types::{Polygon,Point};
214/// #[derive(Queryable)]
215/// struct QueryablePolygonExample {
216///     id: i32,
217///     polygon: Polygon<Point>,
218/// }
219/// ```
220#[derive(Clone, Debug, PartialEq)]
221#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
222#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
223#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
224#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
225#[cfg_attr(feature = "schemars", derive(JsonSchema))]
226pub struct Polygon<T> {
227    pub rings: Vec<Vec<T>>,
228    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
229    pub srid: Option<u32>,
230}
231
232/// Use that structure in `Insertable` or `Queryable` struct if you work with MultiPolygon geometry.
233/// ```
234/// #[macro_use] extern crate diesel;
235/// use postgis_diesel::types::{MultiPolygon, Polygon,Point};
236/// #[derive(Queryable)]
237/// struct QueryableMultiPolygonExample {
238///     id: i32,
239///     multipolygon: MultiPolygon<Polygon<Point>>,
240/// }
241/// ```
242#[derive(Clone, Debug, PartialEq)]
243#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
244#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
245#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
246#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
247#[cfg_attr(feature = "schemars", derive(JsonSchema))]
248pub struct MultiPolygon<T> {
249    pub polygons: Vec<Polygon<T>>,
250    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
251    pub srid: Option<u32>,
252}
253
254/// Represents any type that can appear in a geometry or geography column.
255///
256/// T is the Point type (Point or PointZ or PointM)
257#[derive(Clone, Debug, PartialEq)]
258#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
259#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
260#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
261#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
262#[cfg_attr(feature = "serde_geojson", derive(Deserialize))]
263#[cfg_attr(
264    feature = "serde_geojson",
265    serde(
266        tag = "type",
267        bound(
268            deserialize = "T: crate::geojson::GeoJsonGeometry<f64> + PointT + Clone + serde::Deserialize<'de>"
269        )
270    )
271)]
272#[cfg_attr(feature = "schemars", derive(JsonSchema))]
273pub enum GeometryContainer<T> {
274    Point(T),
275    LineString(LineString<T>),
276    Polygon(Polygon<T>),
277    MultiPoint(MultiPoint<T>),
278    MultiLineString(MultiLineString<T>),
279    MultiPolygon(MultiPolygon<T>),
280    GeometryCollection(GeometryCollection<T>),
281}
282
283/// Use that structure in `Insertable` or `Queryable` struct if you work with GeometryCollection geometry.
284/// ```
285/// #[macro_use] extern crate diesel;
286/// use postgis_diesel::types::{GeometryCollection, GeometryContainer, Point};
287/// #[derive(Queryable)]
288/// struct QueryableGeometryCollectionExample {
289///     id: i32,
290///     geometrycollection: GeometryCollection<GeometryContainer<Point>>,
291/// }
292/// ```
293#[derive(Clone, Debug, PartialEq)]
294#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
295#[cfg_attr(feature = "diesel", diesel(sql_type = Geometry))]
296#[cfg_attr(feature = "diesel", diesel(sql_type = Geography))]
297#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
298#[cfg_attr(feature = "schemars", derive(JsonSchema))]
299pub struct GeometryCollection<T> {
300    pub geometries: Vec<GeometryContainer<T>>,
301    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
302    pub srid: Option<u32>,
303}
304
305#[cfg(feature = "serde_geojson")]
306#[derive(Clone, Debug, PartialEq, Deserialize)]
307#[serde(
308    tag = "type",
309    bound(
310        deserialize = "T: crate::geojson::GeoJsonGeometry<f64> + PointT + Clone + serde::Deserialize<'de>, P: serde::Deserialize<'de>"
311    )
312)]
313pub struct Feature<T, P: serde::Serialize> {
314    pub id: Option<String>,
315    pub geometry: Option<GeometryContainer<T>>,
316    pub properties: Option<P>,
317}
318
319#[cfg(feature = "serde_geojson")]
320#[derive(Clone, Debug, PartialEq, Deserialize)]
321#[serde(
322    tag = "type",
323    bound(
324        deserialize = "T: crate::geojson::GeoJsonGeometry<f64> + PointT + Clone + serde::Deserialize<'de>, P: serde::Deserialize<'de>"
325    )
326)]
327pub struct FeatureCollection<T, P: serde::Serialize> {
328    pub features: Vec<Feature<T, P>>,
329}
330
331#[cfg(test)]
332#[cfg(feature = "serde")]
333mod tests {
334    // Note this useful idiom: importing names from outer (for mod tests) scope.
335    use super::*;
336
337    #[test]
338    fn test_point_serde() {
339        let point = Point::new(72.0, 64.0, None);
340        let expected_point = "{\"x\":72.0,\"y\":64.0}";
341        let point_from_json = serde_json::from_str(expected_point).unwrap();
342        assert_eq!(point, point_from_json);
343        let point_json = serde_json::to_string(&point).unwrap();
344        assert_eq!(expected_point, point_json);
345
346        #[cfg(feature = "schemars")]
347        {
348            let schema = schema_for!(Point);
349            let schema_json = serde_json::to_string(&schema).unwrap();
350            let expected_schema = r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"Point","description":"Use that structure in `Insertable` or `Queryable` struct if you work with Point geometry. ``` #[macro_use] extern crate diesel; use postgis_diesel::types::Point; #[derive(Queryable)] struct QueryablePointExample { id: i32, point: Point, } ```","type":"object","required":["x","y"],"properties":{"srid":{"type":["integer","null"],"format":"uint32","minimum":0.0},"x":{"type":"number","format":"double"},"y":{"type":"number","format":"double"}}}"#;
351            assert_eq!(expected_schema, schema_json);
352
353            let schema_for_value = schema_for_value!(point);
354            let schema_for_value_json = serde_json::to_string(&schema_for_value).unwrap();
355            println!("{}", schema_for_value_json);
356            let expected_schema_for_value = r#"{"$schema":"http://json-schema.org/draft-07/schema#","title":"Point","examples":[{"x":72.0,"y":64.0}],"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}}}"#;
357            assert_eq!(expected_schema_for_value, schema_for_value_json);
358        }
359    }
360}