use geojson::GeoJson;
use crate::{
DateTime, Keyword, LocationUrl, MeetingUrl, OrganizationUrl, PaperUrl, PersonUrl, Url,
namespace::LocationNamespaceUrl,
};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Location {
pub id: LocationUrl,
#[serde(rename = "type")]
pub namespace: LocationNamespaceUrl,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub geojson: Option<GeoJson>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub street_address: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub room: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub postal_code: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sub_locality: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub locality: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub bodies: Vec<Url>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub organizations: Vec<OrganizationUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub persons: Vec<PersonUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub meetings: Vec<MeetingUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub papers: Vec<PaperUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub license: Option<Url>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub keyword: Vec<Keyword>,
pub created: DateTime,
pub modified: DateTime,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub web: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deleted: Option<bool>,
#[serde(default, flatten)]
pub extensions: serde_json::Map<String, serde_json::Value>,
}
#[cfg(test)]
mod serde_tests {
use pretty_assertions::assert_eq;
use serde_json::json;
use time::macros::datetime;
use super::Location;
use crate::namespace::LocationNamespaceUrl;
fn example_location() -> Location {
let geojson_feature = {
let mut f =
geojson::Feature::from(geojson::Geometry::new(geojson::Value::Point(vec![
50.1234, 10.4321,
])));
f.set_property("name", "Rathausplatz");
f
};
Location {
id: "https://oparl.example.org/location/0"
.parse()
.expect("value must be parseable as id"),
namespace: LocationNamespaceUrl::Identifier,
description: Some(
"Rathaus der Beispielstadt, Ratshausplatz 1, 12345 Beispielstadt".to_string(),
),
geojson: Some(geojson_feature.into()),
street_address: None,
room: None,
postal_code: None,
sub_locality: None,
locality: None,
bodies: vec![],
organizations: vec![],
persons: vec![],
meetings: vec![],
papers: vec![],
license: None,
keyword: vec![],
created: datetime!(2014-01-08 14:28:31 +01:00).into(),
modified: datetime!(2014-01-08 14:28:31 +01:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}
}
fn example_location_json() -> serde_json::Value {
json!({
"id": "https://oparl.example.org/location/0",
"type": "https://schema.oparl.org/1.1/Location",
"description": "Rathaus der Beispielstadt, Ratshausplatz 1, 12345 Beispielstadt",
"created": "2014-01-08T14:28:31+01:00",
"modified": "2014-01-08T14:28:31+01:00",
"geojson": {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
50.1234,
10.4321
]
},
"properties": {
"name": "Rathausplatz"
}
}
})
}
#[test]
fn serialize() {
assert_eq!(json!(example_location()), example_location_json());
}
#[test]
fn deserialize_good() {
let deserialized: Location = serde_json::from_value(example_location_json())
.expect("value must be deserializable as Location");
assert_eq!(deserialized, example_location());
}
#[test]
fn deserialize_bad() {
assert!(serde_json::from_value::<Location>(json!("xyzabcd")).is_err());
assert!(serde_json::from_value::<Location>(json!(true)).is_err());
assert!(serde_json::from_value::<Location>(json!(123)).is_err());
}
}