twitter_stream_message/
geometry.rs

1//! Geometry object
2
3use std::fmt;
4
5use serde::de::{
6    Deserialize,
7    Deserializer,
8    Error,
9    IgnoredAny,
10    MapAccess,
11    SeqAccess,
12    Unexpected,
13    Visitor,
14};
15
16use util::CowStr;
17
18/// The Geometry object specified in [The GeoJSON Format (RFC7946) ยง3.1][1].
19///
20/// [1]: https://tools.ietf.org/html/rfc7946#section-3.1
21#[derive(Clone, Debug, PartialEq)]
22pub enum Geometry {
23    Point(Position),
24    MultiPoint(Vec<Position>),
25    LineString(LineString),
26    MultiLineString(Vec<LineString>),
27    Polygon(Polygon),
28    MultiPolygon(Vec<Polygon>),
29    // GeometryCollection(Vec<Geometry>),
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
33pub struct Position(
34    /// Longitude
35    pub f64,
36    /// Latitude
37    pub f64,
38    // Twitter API does not provide altitudes
39);
40
41pub type LineString = Vec<Position>;
42pub type Polygon = Vec<LinearRing>;
43pub type LinearRing = LineString;
44
45impl<'x> Deserialize<'x> for Geometry {
46    fn deserialize<D: Deserializer<'x>>(d: D) -> Result<Self, D::Error> {
47        d.deserialize_map(GeometryVisitor)
48    }
49}
50
51struct GeometryVisitor;
52
53enum Coordinates {
54    F64(f64),
55    Dim0(Position), // Point
56    Dim1(Vec<Position>), // MultiPoint, LineString
57    Dim2(Vec<Vec<Position>>), // MultiLineString, Polygon
58    Dim3(Vec<Vec<Vec<Position>>>), // MultiPolygon
59}
60
61struct CoordinatesVisitor;
62
63impl<'x> Visitor<'x> for GeometryVisitor {
64    type Value = Geometry;
65
66    fn visit_map<V: MapAccess<'x>>(self, mut v: V)
67        -> Result<Geometry, V::Error>
68    {
69        use self::Geometry::*;
70
71        let mut c = None;
72
73        macro_rules! end {
74            () => {
75                while v.next_entry::<IgnoredAny, IgnoredAny>()?.is_some() {}
76            };
77        }
78
79        while let Some(k) = v.next_key::<String>()? {
80            match &*k {
81                "type" => {
82                    let t = v.next_value::<CowStr>()?;
83                    macro_rules! match_type {
84                        ($C:ident, $($($V:ident($typ:expr))|* => $D:ident,)*) =>
85                        {{
86                            const EXPECTED: &[&str] = &[$($($typ),*),*];
87                            match &*t {
88                                $($($typ => match c {
89                                    Some($C::$D(val)) => {
90                                        end!();
91                                        return Ok($V(val));
92                                    },
93                                    None => {
94                                        while let Some(k)
95                                            = v.next_key::<CowStr>()?
96                                        {
97                                            if "coordinates" == &*k {
98                                                let c = v.next_value()?;
99                                                end!();
100                                                return Ok($V(c));
101                                            } else {
102                                                v.next_value::<IgnoredAny>()?;
103                                            }
104                                        }
105                                        return Err(V::Error::missing_field(
106                                            "coordinates"
107                                        ));
108                                    },
109                                    _ => return Err(V::Error::custom(
110                                        "invalid coordinates type"
111                                    )),
112                                },)*)*
113                                s => return Err(
114                                    V::Error::unknown_variant(s, EXPECTED)
115                                ),
116                            }
117                        }};
118                    }
119                    match_type! {
120                        Coordinates,
121                        Point("Point") => Dim0,
122                        MultiPoint("MultiPoint") | LineString("LineString")
123                            => Dim1,
124                        MultiLineString("MultiLineString") | Polygon("Polygon")
125                            => Dim2,
126                        MultiPolygon("MultiPolygon") => Dim3,
127                    }
128                },
129                "coordinates" => c = Some(v.next_value()?),
130                _ => { v.next_value::<IgnoredAny>()?; },
131            }
132        }
133
134        Err(V::Error::missing_field("type"))
135    }
136
137    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
138        write!(f, "a map with `type` and `coordinate` fields")
139    }
140}
141
142impl<'x> Deserialize<'x> for Coordinates {
143    fn deserialize<D: Deserializer<'x>>(d: D)
144        -> Result<Self, D::Error>
145    {
146        d.deserialize_any(CoordinatesVisitor)
147    }
148}
149
150impl<'x> Visitor<'x> for CoordinatesVisitor {
151    type Value = Coordinates;
152
153    fn visit_f64<E>(self, v: f64) -> Result<Coordinates, E> {
154        Ok(Coordinates::F64(v))
155    }
156
157    fn visit_i64<E>(self, v: i64) -> Result<Coordinates, E> {
158        Ok(Coordinates::F64(v as _))
159    }
160
161    fn visit_u64<E>(self, v: u64) -> Result<Coordinates, E> {
162        Ok(Coordinates::F64(v as _))
163    }
164
165    fn visit_seq<V: SeqAccess<'x>>(self, mut v: V)
166        -> Result<Coordinates, V::Error>
167    {
168        macro_rules! match_val {
169            (
170                $C:ident,
171                $($V:ident => $R:ident,)*
172            ) => {
173                match v.next_element()? {
174                    Some($C::F64(v1)) => {
175                        let v2 = match v.next_element()? {
176                            Some(val) => val,
177                            None => return Err(
178                                V::Error::invalid_length(1, &self)
179                            ),
180                        };
181                        while v.next_element::<IgnoredAny>()?.is_some() {}
182                        Ok($C::Dim0(Position(v1, v2)))
183                    },
184                    $(Some($C::$V(val)) => {
185                        let mut ret = v.size_hint()
186                            .map(Vec::with_capacity)
187                            .unwrap_or_else(Vec::new);
188                        ret.push(val);
189                        while let Some(val) = v.next_element()? {
190                            ret.push(val);
191                        }
192                        Ok($C::$R(ret))
193                    },)*
194                    Some($C::Dim3(_)) => Err(
195                        V::Error::invalid_type(Unexpected::Seq, &self)
196                    ),
197                    None => Ok($C::Dim1(Vec::new())),
198                }
199            };
200        }
201
202        match_val! {
203            Coordinates,
204            Dim0 => Dim1,
205            Dim1 => Dim2,
206            Dim2 => Dim3,
207        }
208    }
209
210    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
211        write!(f,
212            "a floating point number \
213            or an array of floating point number with a depth of 4 or lower"
214        )
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use json;
221    use super::*;
222
223    #[test]
224    fn deserialize_pass() {
225        macro_rules! assert_deserialize_to {
226            ($typ:ident($($inner:tt)*), $c:expr) => {{
227                let geo = Geometry::$typ($($inner)*);
228                let c = format!("\"coordinates\":{}", $c);
229                let t = format!("\"type\":\"{}\"", stringify!($typ));
230                assert_eq!(geo, json::from_str(&format!(
231                    "{{{},{}}}", c, t
232                )).unwrap());
233                assert_eq!(geo, json::from_str(&format!
234                    ("{{{},{}}}", t, c
235                )).unwrap());
236                assert_eq!(geo, json::from_str(&format!(
237                    "{{\"1\":0,{},\"2\":[],{},\"3\":null}}", c, t
238                )).unwrap());
239                assert_eq!(geo, json::from_str(&format!(
240                    "{{\"1\":0,{},\"2\":[],{},\"3\":null}}", t, c
241                )).unwrap());
242            }};
243        }
244
245        assert_deserialize_to!(
246            Point(Position(-75.14310264, 40.05701649)),
247            "[-75.14310264,40.05701649]"
248        );
249        assert_deserialize_to!(
250            Polygon(vec![vec![
251                Position(2.2241006,48.8155414),
252                Position(2.4699099,48.8155414),
253                Position(2.4699099,48.9021461),
254                Position(2.2241006,48.9021461),
255            ]]),
256            "[[
257                [2.2241006,48.8155414],
258                [2.4699099,48.8155414],
259                [2.4699099,48.9021461],
260                [2.2241006,48.9021461]
261            ]]"
262        );
263    }
264
265    #[test]
266    fn deserialize_fail() {
267        macro_rules! assert_fail {
268            ($json:expr) => {{
269                json::from_str::<::serde::de::IgnoredAny>($json).unwrap();
270                json::from_str::<Geometry>($json).unwrap_err();
271            }};
272        }
273
274        assert_fail!("{}");
275        assert_fail!("[0,1]");
276        assert_fail!("{\"coordinates\":[1],\"type\":\"Point\"}");
277        assert_fail!("{\"coordinates\":[1,2],\"type\":\"MultiPoint\"}");
278        assert_fail!("{\"coordinates\":[[[[[0,0]]]]],\"type\":\"MultiPolygon\"}");
279        assert_fail!("{\"coordinates\":[],\"type\":\"Foo\"}");
280    }
281}