use std::fmt;
use serde::de::{
Deserialize,
Deserializer,
Error,
IgnoredAny,
MapAccess,
SeqAccess,
Unexpected,
Visitor,
};
use util::CowStr;
#[derive(Clone, Debug, PartialEq)]
pub enum Geometry {
Point(Position),
MultiPoint(Vec<Position>),
LineString(LineString),
MultiLineString(Vec<LineString>),
Polygon(Polygon),
MultiPolygon(Vec<Polygon>),
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Position(
pub f64,
pub f64,
);
pub type LineString = Vec<Position>;
pub type Polygon = Vec<LinearRing>;
pub type LinearRing = LineString;
impl<'x> Deserialize<'x> for Geometry {
fn deserialize<D: Deserializer<'x>>(d: D) -> Result<Self, D::Error> {
d.deserialize_map(GeometryVisitor)
}
}
struct GeometryVisitor;
enum Coordinates {
F64(f64),
Dim0(Position), Dim1(Vec<Position>), Dim2(Vec<Vec<Position>>), Dim3(Vec<Vec<Vec<Position>>>), }
struct CoordinatesVisitor;
impl<'x> Visitor<'x> for GeometryVisitor {
type Value = Geometry;
fn visit_map<V: MapAccess<'x>>(self, mut v: V)
-> Result<Geometry, V::Error>
{
use self::Geometry::*;
let mut c = None;
macro_rules! end {
() => {
while v.next_entry::<IgnoredAny, IgnoredAny>()?.is_some() {}
};
}
while let Some(k) = v.next_key::<String>()? {
match &*k {
"type" => {
let t = v.next_value::<CowStr>()?;
macro_rules! match_type {
($C:ident, $($($V:ident($typ:expr))|* => $D:ident,)*) =>
{{
const EXPECTED: &[&str] = &[$($($typ),*),*];
match &*t {
$($($typ => match c {
Some($C::$D(val)) => {
end!();
return Ok($V(val));
},
None => {
while let Some(k)
= v.next_key::<CowStr>()?
{
if "coordinates" == &*k {
let c = v.next_value()?;
end!();
return Ok($V(c));
} else {
v.next_value::<IgnoredAny>()?;
}
}
return Err(V::Error::missing_field(
"coordinates"
));
},
_ => return Err(V::Error::custom(
"invalid coordinates type"
)),
},)*)*
s => return Err(
V::Error::unknown_variant(s, EXPECTED)
),
}
}};
}
match_type! {
Coordinates,
Point("Point") => Dim0,
MultiPoint("MultiPoint") | LineString("LineString")
=> Dim1,
MultiLineString("MultiLineString") | Polygon("Polygon")
=> Dim2,
MultiPolygon("MultiPolygon") => Dim3,
}
},
"coordinates" => c = Some(v.next_value()?),
_ => { v.next_value::<IgnoredAny>()?; },
}
}
Err(V::Error::missing_field("type"))
}
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a map with `type` and `coordinate` fields")
}
}
impl<'x> Deserialize<'x> for Coordinates {
fn deserialize<D: Deserializer<'x>>(d: D)
-> Result<Self, D::Error>
{
d.deserialize_any(CoordinatesVisitor)
}
}
impl<'x> Visitor<'x> for CoordinatesVisitor {
type Value = Coordinates;
fn visit_f64<E>(self, v: f64) -> Result<Coordinates, E> {
Ok(Coordinates::F64(v))
}
fn visit_i64<E>(self, v: i64) -> Result<Coordinates, E> {
Ok(Coordinates::F64(v as _))
}
fn visit_u64<E>(self, v: u64) -> Result<Coordinates, E> {
Ok(Coordinates::F64(v as _))
}
fn visit_seq<V: SeqAccess<'x>>(self, mut v: V)
-> Result<Coordinates, V::Error>
{
macro_rules! match_val {
(
$C:ident,
$($V:ident => $R:ident,)*
) => {
match v.next_element()? {
Some($C::F64(v1)) => {
let v2 = match v.next_element()? {
Some(val) => val,
None => return Err(
V::Error::invalid_length(1, &self)
),
};
while v.next_element::<IgnoredAny>()?.is_some() {}
Ok($C::Dim0(Position(v1, v2)))
},
$(Some($C::$V(val)) => {
let mut ret = v.size_hint()
.map(Vec::with_capacity)
.unwrap_or_else(Vec::new);
ret.push(val);
while let Some(val) = v.next_element()? {
ret.push(val);
}
Ok($C::$R(ret))
},)*
Some($C::Dim3(_)) => Err(
V::Error::invalid_type(Unexpected::Seq, &self)
),
None => Ok($C::Dim1(Vec::new())),
}
};
}
match_val! {
Coordinates,
Dim0 => Dim1,
Dim1 => Dim2,
Dim2 => Dim3,
}
}
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"a floating point number \
or an array of floating point number with a depth of 4 or lower"
)
}
}
#[cfg(test)]
mod tests {
use json;
use super::*;
#[test]
fn deserialize_pass() {
macro_rules! assert_deserialize_to {
($typ:ident($($inner:tt)*), $c:expr) => {{
let geo = Geometry::$typ($($inner)*);
let c = format!("\"coordinates\":{}", $c);
let t = format!("\"type\":\"{}\"", stringify!($typ));
assert_eq!(geo, json::from_str(&format!(
"{{{},{}}}", c, t
)).unwrap());
assert_eq!(geo, json::from_str(&format!
("{{{},{}}}", t, c
)).unwrap());
assert_eq!(geo, json::from_str(&format!(
"{{\"1\":0,{},\"2\":[],{},\"3\":null}}", c, t
)).unwrap());
assert_eq!(geo, json::from_str(&format!(
"{{\"1\":0,{},\"2\":[],{},\"3\":null}}", t, c
)).unwrap());
}};
}
assert_deserialize_to!(
Point(Position(-75.14310264, 40.05701649)),
"[-75.14310264,40.05701649]"
);
assert_deserialize_to!(
Polygon(vec![vec![
Position(2.2241006,48.8155414),
Position(2.4699099,48.8155414),
Position(2.4699099,48.9021461),
Position(2.2241006,48.9021461),
]]),
"[[
[2.2241006,48.8155414],
[2.4699099,48.8155414],
[2.4699099,48.9021461],
[2.2241006,48.9021461]
]]"
);
}
#[test]
fn deserialize_fail() {
macro_rules! assert_fail {
($json:expr) => {{
json::from_str::<::serde::de::IgnoredAny>($json).unwrap();
json::from_str::<Geometry>($json).unwrap_err();
}};
}
assert_fail!("{}");
assert_fail!("[0,1]");
assert_fail!("{\"coordinates\":[1],\"type\":\"Point\"}");
assert_fail!("{\"coordinates\":[1,2],\"type\":\"MultiPoint\"}");
assert_fail!("{\"coordinates\":[[[[[0,0]]]]],\"type\":\"MultiPolygon\"}");
assert_fail!("{\"coordinates\":[],\"type\":\"Foo\"}");
}
}