use crate::{
BodyUrl, ConsultationListUrl, DateTime, Keyword, Location, MeetingListUrl, MembershipUrl, Name,
OrganizationClassification, OrganizationType, OrganizationUrl, Post, Url, date::Date,
namespace::OrganizationNamespaceUrl,
};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Organization {
pub id: OrganizationUrl,
#[serde(rename = "type")]
pub namespace: OrganizationNamespaceUrl,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub body: Option<BodyUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<Name>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub membership: Vec<MembershipUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub meeting: Option<MeetingListUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub consultation: Option<ConsultationListUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub short_name: Option<Name>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub post: Vec<Post>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sub_organization_of: Option<OrganizationUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub organization_type: Option<OrganizationType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub classification: Option<OrganizationClassification>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_date: Option<Date>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end_date: Option<Date>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub website: Option<Url>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub location: Option<Location>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub external_body: Option<BodyUrl>,
#[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::{date, datetime};
use super::Organization;
use crate::{
Location, OrganizationType,
namespace::{LocationNamespaceUrl, OrganizationNamespaceUrl},
};
fn example_organization() -> Organization {
let geojson_feature = {
let mut f =
geojson::Feature::from(geojson::Geometry::new(geojson::GeometryValue::Point {
coordinates: [50.1234, 10.4321].into(),
}));
f.set_property("name", "Rathausplatz");
f
};
Organization {
id: "https://oparl.example.org/organization/34"
.parse()
.expect("value must be parseable as id"),
namespace: OrganizationNamespaceUrl::Identifier,
body: Some(
"https://oparl.example.org/bodies/1"
.parse()
.expect("value must be parseable as url"),
),
name: Some("Ausschuss für Haushalt und Finanzen".into()),
membership: vec![
"https://oparl.example.org/membership/27"
.parse()
.expect("value must be parseable as url"),
"https://oparl.example.org/membership/48"
.parse()
.expect("value must be parseable as url"),
"https://oparl.example.org/membership/57"
.parse()
.expect("value must be parseable as url"),
],
meeting: Some(
"https://oparl.example.org/organization/34/meetings"
.parse()
.expect("value must be parseable as url"),
),
consultation: None,
short_name: Some("Finanzausschuss".into()),
post: vec![
"Vorsitzender".into(),
"1. Stellvertreter".into(),
"Mitglied".into(),
],
sub_organization_of: None,
organization_type: Some(OrganizationType::Council),
classification: Some("Ausschuss".into()),
start_date: Some(date!(2012 - 07 - 17).into()),
end_date: None,
website: None,
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!(2012-01-06 12:01:00 +01:00).into(),
modified: datetime!(2012-01-08 14:05:27 +01:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}),
external_body: None,
license: None,
keyword: vec!["finanzen".into(), "haushalt".into()],
created: datetime!(2012-07-16 00:00:00 +02:00).into(),
modified: datetime!(2012-08-16 12:34:56 +02:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}
}
fn example_organization_json() -> serde_json::Value {
json!({
"id": "https://oparl.example.org/organization/34",
"type": "https://schema.oparl.org/1.1/Organization",
"body": "https://oparl.example.org/bodies/1",
"name": "Ausschuss für Haushalt und Finanzen",
"shortName": "Finanzausschuss",
"startDate": "2012-07-17",
"organizationType": "Gremium",
"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": "2012-01-06T12:01:00+01:00",
"modified": "2012-01-08T14:05:27+01:00",
"geojson": {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
50.1234,
10.4321
]
},
"properties": {
"name": "Rathausplatz"
}
}
},
"post": [
"Vorsitzender",
"1. Stellvertreter",
"Mitglied"
],
"meeting": "https://oparl.example.org/organization/34/meetings",
"membership": [
"https://oparl.example.org/membership/27",
"https://oparl.example.org/membership/48",
"https://oparl.example.org/membership/57"
],
"classification": "Ausschuss",
"keyword": [
"finanzen",
"haushalt"
],
"created": "2012-07-16T00:00:00+02:00",
"modified": "2012-08-16T12:34:56+02:00"
})
}
#[test]
fn serialize() {
assert_eq!(json!(example_organization()), example_organization_json());
}
#[test]
fn deserialize_good() {
let deserialized: Organization = serde_json::from_value(example_organization_json())
.expect("value must be deserializable as Organization");
assert_eq!(deserialized, example_organization());
}
#[test]
fn deserialize_bad() {
assert!(serde_json::from_value::<Organization>(json!("xyzabcd")).is_err());
assert!(serde_json::from_value::<Organization>(json!(true)).is_err());
assert!(serde_json::from_value::<Organization>(json!(123)).is_err());
}
}