poem_openapi/types/external/
geo.rs

1use geo_types::*;
2
3use crate::types::Type;
4
5trait GeoJson {
6    type Coordinates: Type;
7}
8
9macro_rules! impl_geojson_types {
10    ($geometry:tt, $name:literal, $coordinates:ty) => {
11        impl<T: CoordNum + crate::types::Type> GeoJson for $geometry<T> {
12            type Coordinates = $coordinates;
13        }
14
15        impl crate::types::Type for $geometry {
16            const IS_REQUIRED: bool = true;
17            type RawValueType = Self;
18            type RawElementValueType = Self;
19
20            fn name() -> ::std::borrow::Cow<'static, str> {
21                concat!("GeoJSON_", $name).into()
22            }
23
24            fn schema_ref() -> crate::registry::MetaSchemaRef {
25                crate::registry::MetaSchemaRef::Reference(Self::name().into_owned())
26            }
27
28            fn register(registry: &mut crate::registry::Registry) {
29                registry.create_schema::<Self, _>(Self::name().into_owned(), |registry| {
30                    String::register(registry);
31                    <<Self as GeoJson>::Coordinates>::register(registry);
32                    crate::registry::MetaSchema {
33                        required: vec!["type", "coordinates"],
34                        properties: vec![
35                            ("type", String::schema_ref()),
36                            (
37                                "coordinates",
38                                <<Self as GeoJson>::Coordinates>::schema_ref(),
39                            ),
40                        ],
41                        ..crate::registry::MetaSchema::new("object")
42                    }
43                })
44            }
45
46            fn as_raw_value(&self) -> Option<&Self::RawValueType> {
47                Some(self)
48            }
49
50            fn raw_element_iter<'a>(
51                &'a self,
52            ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
53                Box::new(IntoIterator::into_iter(self.as_raw_value()))
54            }
55        }
56
57        impl crate::types::ParseFromJSON for $geometry {
58            fn parse_from_json(
59                value: Option<::serde_json::Value>,
60            ) -> Result<Self, crate::types::ParseError<Self>> {
61                let value = value.ok_or(crate::types::ParseError::expected_input())?;
62                Self::try_from(geojson::Geometry::try_from(value)?).map_err(Into::into)
63            }
64        }
65
66        impl crate::types::ToJSON for $geometry {
67            fn to_json(&self) -> Option<::serde_json::Value> {
68                Some(
69                    ::serde_json::Map::<String, ::serde_json::Value>::from(
70                        &geojson::Geometry::from(self),
71                    )
72                    .into(),
73                )
74            }
75        }
76    };
77}
78
79impl_geojson_types!(Point, "Point", [T; 2]);
80impl_geojson_types!(MultiPoint, "MultiPoint", Vec<[T; 2]>);
81impl_geojson_types!(LineString, "LineString", Vec<[T; 2]>);
82impl_geojson_types!(MultiLineString, "MultiLineString", Vec<Vec<[T; 2]>>);
83impl_geojson_types!(Polygon, "Polygon", Vec<Vec<[T; 2]>>);
84impl_geojson_types!(MultiPolygon, "MultiPolygon", Vec<Vec<Vec<[T; 2]>>>);
85
86#[cfg(test)]
87mod tests {
88    use geo_types::Point;
89
90    use crate::types::{ParseFromJSON, ToJSON};
91
92    fn point_geo() -> Point {
93        Point::new(1.0, 2.0)
94    }
95
96    fn point_json() -> serde_json::Value {
97        serde_json::json!({
98            "type": "Point",
99            "coordinates": [1.0, 2.0]
100        })
101    }
102
103    #[test]
104    fn serializes_geo_to_json() {
105        assert_eq!(point_json(), point_geo().to_json().unwrap())
106    }
107
108    #[test]
109    fn deserializes_json_to_geo() {
110        assert_eq!(
111            Point::parse_from_json(Some(point_json())).unwrap(),
112            point_geo()
113        )
114    }
115}