use crate::{
AgendaItemListUrl, BodyClassification, BodyUrl, ConsultationListUrl, DateTime, EmailAddress,
FileListUrl, Keyword, LegislativeTerm, LegislativeTermListUrl, Location, LocationListUrl,
MeetingListUrl, MembershipListUrl, Name, OrganizationListUrl, PaperListUrl, PersonListUrl,
SystemUrl, Url, namespace::BodyNamespaceUrl,
};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Body {
pub id: BodyUrl,
#[serde(rename = "type")]
pub namespace: BodyNamespaceUrl,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub system: Option<SystemUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub short_name: Option<Name>,
pub name: Name,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub website: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub license: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub license_valid_since: Option<DateTime>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub oparl_since: Option<DateTime>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ags: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rgs: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub equivalent: Vec<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub contact_email: Option<EmailAddress>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub contact_name: Option<Name>,
pub organization: OrganizationListUrl,
pub person: PersonListUrl,
pub meeting: MeetingListUrl,
pub paper: PaperListUrl,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub legislative_term: Vec<LegislativeTerm>,
pub agenda_item: AgendaItemListUrl,
pub consultation: ConsultationListUrl,
pub file: FileListUrl,
pub location_list: LocationListUrl,
pub legislative_term_list: LegislativeTermListUrl,
pub membership: MembershipListUrl,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub classification: Option<BodyClassification>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub location: Option<Location>,
#[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::Body;
use crate::{
LegislativeTerm, Location,
namespace::{BodyNamespaceUrl, LegislativeTermNamespaceUrl, LocationNamespaceUrl},
};
fn example_body() -> Body {
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
};
Body {
id: "https://oparl.example.org/body/0"
.parse()
.expect("value must be parseable as id"),
namespace: BodyNamespaceUrl::Identifier,
system: Some(
"https://oparl.example.org/"
.parse()
.expect("value must be parseable as url"),
),
short_name: Some("Köln".into()),
name: "Stadt Köln, kreisfreie Stadt".into(),
website: Some(
"http://www.beispielstadt.de/"
.parse()
.expect("value must be parseable as url"),
),
license: Some(
"http://creativecommons.org/licenses/by/4.0/"
.parse()
.expect("value must be parseable as url"),
),
license_valid_since: Some(datetime!(2014-01-01 00:00:00 +02:00).into()),
oparl_since: None,
ags: Some("05315000".to_string()),
rgs: Some("053150000000".to_string()),
equivalent: vec![
"http://d-nb.info/gnd/2015732-0"
.parse()
.expect("value must be parseable as url"),
"http://dbpedia.org/resource/Cologne"
.parse()
.expect("value must be parseable as url"),
],
contact_email: Some(
"ris@beispielstadt.de"
.parse()
.expect("value must be parseable as email"),
),
contact_name: Some(
"RIS-Betreuung"
.parse()
.expect("value must be parseable as name"),
),
organization: "https://oparl.example.org/body/0/organizations/"
.parse()
.expect("value must be parseable as url"),
person: "https://oparl.example.org/body/0/persons/"
.parse()
.expect("value must be parseable as url"),
meeting: "https://oparl.example.org/body/0/meetings/"
.parse()
.expect("value must be parseable as url"),
paper: "https://oparl.example.org/body/0/papers/"
.parse()
.expect("value must be parseable as url"),
legislative_term: vec![LegislativeTerm {
id: "https://oparl.example.org/term/21"
.parse()
.expect("value must be parseable as id"),
namespace: LegislativeTermNamespaceUrl::Identifier,
body: Some(
"https://oparl.example.org/body/0"
.parse()
.expect("value must be parseable as url"),
),
name: Some(
"21. Wahlperiode"
.parse()
.expect("value must be parseable as name"),
),
start_date: Some(
"2010-12-03"
.parse()
.expect("value must be parseable as date"),
),
end_date: Some(
"2013-12-03"
.parse()
.expect("value must be parseable as date"),
),
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(),
}],
agenda_item: "https://oparl.example.org/body/0/agendaItems/"
.parse()
.expect("value must be parseable as url"),
consultation: "https://oparl.example.org/body/0/consultations/"
.parse()
.expect("value must be parseable as url"),
file: "https://oparl.example.org/body/0/files/"
.parse()
.expect("value must be parseable as url"),
location_list: "https://oparl.example.org/body/0/location_list/"
.parse()
.expect("value must be parseable as url"),
legislative_term_list: "https://oparl.example.org/body/0/legislative_term_list/"
.parse()
.expect("value must be parseable as url"),
membership: "https://oparl.example.org/body/0/memberships/"
.parse()
.expect("value must be parseable as url"),
classification: Some("Kreisfreie Stadt".into()),
location: Some(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(),
}),
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_body_json() -> serde_json::Value {
json!({
"id": "https://oparl.example.org/body/0",
"type": "https://schema.oparl.org/1.1/Body",
"system": "https://oparl.example.org/",
"contactEmail": "ris@beispielstadt.de",
"contactName": "RIS-Betreuung",
"ags": "05315000",
"rgs": "053150000000",
"equivalent": [
"http://d-nb.info/gnd/2015732-0",
"http://dbpedia.org/resource/Cologne"
],
"shortName": "Köln",
"name": "Stadt Köln, kreisfreie Stadt",
"website": "http://www.beispielstadt.de/",
"license": "http://creativecommons.org/licenses/by/4.0/",
"licenseValidSince": "2014-01-01T00:00:00+02:00",
"organization": "https://oparl.example.org/body/0/organizations/",
"person": "https://oparl.example.org/body/0/persons/",
"meeting": "https://oparl.example.org/body/0/meetings/",
"paper": "https://oparl.example.org/body/0/papers/",
"agendaItem": "https://oparl.example.org/body/0/agendaItems/",
"consultation": "https://oparl.example.org/body/0/consultations/",
"file": "https://oparl.example.org/body/0/files/",
"locationList": "https://oparl.example.org/body/0/location_list/",
"membership": "https://oparl.example.org/body/0/memberships/",
"legislativeTermList": "https://oparl.example.org/body/0/legislative_term_list/",
"legislativeTerm": [
{
"id": "https://oparl.example.org/term/21",
"type": "https://schema.oparl.org/1.1/LegislativeTerm",
"body": "https://oparl.example.org/body/0",
"name": "21. Wahlperiode",
"startDate": "2010-12-03",
"endDate": "2013-12-03",
"created": "2014-01-08T14:28:31+01:00",
"modified": "2014-01-08T14:28:31+01:00"
}
],
"location": {
"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"
}
}
},
"classification": "Kreisfreie Stadt",
"created": "2014-01-08T14:28:31+01:00",
"modified": "2014-01-08T14:28:31+01:00"
})
}
#[test]
fn serialize() {
assert_eq!(json!(example_body()), example_body_json());
}
#[test]
fn deserialize_good() {
let deserialized: Body = serde_json::from_value(example_body_json())
.expect("value must be deserializable as Body");
assert_eq!(deserialized, example_body());
}
#[test]
fn deserialize_bad() {
assert!(serde_json::from_value::<Body>(json!("xyzabcd")).is_err());
assert!(serde_json::from_value::<Body>(json!(true)).is_err());
assert!(serde_json::from_value::<Body>(json!(123)).is_err());
}
}