use crate::{
BodyUrl, Consultation, Date, DateTime, File, Keyword, Location, Name, OrganizationUrl,
PaperUrl, PersonUrl, Url, namespace::PaperNamespaceUrl,
};
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Paper {
pub id: PaperUrl,
#[serde(rename = "type")]
pub namespace: PaperNamespaceUrl,
#[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 = "Option::is_none")]
pub reference: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub date: Option<Date>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub paper_type: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub related_paper: Vec<PaperUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub superordinated_paper: Vec<PaperUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub subordinated_paper: Vec<PaperUrl>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub main_file: Option<File>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub auxiliary_file: Vec<File>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub location: Vec<Location>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub originator_person: Vec<PersonUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub under_direction_of: Vec<OrganizationUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub originator_organization: Vec<OrganizationUrl>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub consultation: Vec<Consultation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub license: Option<String>,
#[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::Paper;
use crate::{
Consultation, File, Location, Sha1Sum,
namespace::{
ConsultationNamespaceUrl, FileNamespaceUrl, LocationNamespaceUrl, PaperNamespaceUrl,
},
};
fn example_paper() -> Paper {
let geojson_feature = geojson::Value::Point(vec![7.03291, 50.98249]);
Paper {
id: "https://oparl.example.org/paper/749"
.parse()
.expect("value must be parseable as id"),
namespace: PaperNamespaceUrl::Identifier,
body: Some(
"https://oparl.example.org/bodies/1"
.parse()
.expect("value must be parseable as url"),
),
name: Some("Antwort auf Anfrage 1200/2014".into()),
reference: Some("1234/2014".into()),
date: Some(date!(2014 - 04 - 04).into()),
paper_type: Some("Beantwortung einer Anfrage".into()),
related_paper: vec![
"https://oparl.example.org/paper/699"
.parse()
.expect("value must be parseable as url"),
],
superordinated_paper: vec![],
subordinated_paper: vec![],
main_file: Some(File {
id: "https://oparl.example.org/files/57737"
.parse()
.expect("value must be parseable as id"),
namespace: FileNamespaceUrl::Identifier,
file_name: Some("anlage_1_zur_anfrage.pdf".into()),
name: Some("Anlage 1 zur Anfrage".into()),
mime_type: Some("application/pdf".into()),
date: Some(date!(2013 - 01 - 04).into()),
size: Some(82930),
sha1_checksum: Some(Sha1Sum::from([
0xd7, 0x49, 0x75, 0x1a, 0xf4, 0x4a, 0x32, 0xc8, 0x18, 0xb9, 0xb1, 0xe1, 0x51,
0x52, 0x51, 0xc6, 0x77, 0x34, 0xf5, 0xd2,
])),
sha512_checksum: None,
text: None,
access_url: "https://oparl.example.org/files/57737.pdf"
.parse()
.expect("value must be parseable as url"),
download_url: Some(
"https://oparl.example.org/files/download/57737.pdf"
.parse()
.expect("value must be parseable as url"),
),
external_service_url: None,
master_file: None,
derivative_file: vec![],
file_license: None,
meeting: vec![],
agenda_item: vec![],
paper: vec![],
license: Some(
"http://www.opendefinition.org/licenses/cc-by"
.parse()
.expect("value must be parseable as url"),
),
keyword: vec![],
created: datetime!(2013-01-04 07:54:13 +01:00).into(),
modified: datetime!(2013-01-04 07:54:13 +01:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}),
auxiliary_file: vec![File {
id: "https://oparl.example.org/files/57739"
.parse()
.expect("value must be parseable as id"),
namespace: FileNamespaceUrl::Identifier,
file_name: Some("anlage.pdf".into()),
name: Some("Anlage 1 zur Anfrage".into()),
mime_type: Some("application/pdf".into()),
date: Some(date!(2013 - 01 - 04).into()),
size: Some(82930),
sha1_checksum: Some(Sha1Sum::from([
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95,
0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
])),
sha512_checksum: None,
text: Some("Der Übersichtsplan zeigt alle Ebenen des ...".into()),
access_url: "https://oparl.example.org/files/57739.pdf"
.parse()
.expect("value must be parseable as url"),
download_url: Some(
"https://oparl.example.org/files/download/57739.pdf"
.parse()
.expect("value must be parseable as url"),
),
external_service_url: None,
master_file: Some(
"https://oparl.example.org/files/57738"
.parse()
.expect("value must be parseable as id"),
),
derivative_file: vec![],
file_license: None,
meeting: vec![],
agenda_item: vec![],
paper: vec![],
license: Some(
"http://www.opendefinition.org/licenses/cc-by"
.parse()
.expect("value must be parseable as url"),
),
keyword: vec![],
created: datetime!(2013-01-04 07:54:13 +01:00).into(),
modified: datetime!(2013-01-04 07:54:13 +01:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}],
location: vec![Location {
id: "https://oparl.example.org/locations/29856"
.parse()
.expect("value must be parseable as id"),
namespace: LocationNamespaceUrl::Identifier,
description: Some("Honschaftsstraße 312, Köln".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-08 14:05:27 +01:00).into(),
modified: datetime!(2012-01-08 14:05:27 +01:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}],
originator_person: vec![
"https://oparl.example.org/person/2000"
.parse()
.expect("value must be parseable as url"),
"https://oparl.example.org/person/1000"
.parse()
.expect("value must be parseable as url"),
],
under_direction_of: vec![
"https://oparl.example.org/organization/2000"
.parse()
.expect("value must be parseable as url"),
],
originator_organization: vec![
"https://oparl.example.org/organization/2000"
.parse()
.expect("value must be parseable as url"),
"https://oparl.example.org/organization/1000"
.parse()
.expect("value must be parseable as url"),
],
consultation: vec![Consultation {
id: "https://oparl.example.org/consultation/47594"
.parse()
.expect("value must be parseable as id"),
namespace: ConsultationNamespaceUrl::Identifier,
paper: None,
agenda_item: Some(
"https://oparl.example.org/agendaitem/15569"
.parse()
.expect("value must be parseable as url"),
),
meeting: Some(
"https://oparl.example.org/meeting/243"
.parse()
.expect("value must be parseable as url"),
),
organization: vec![
"https://oparl.example.org/organization/96"
.parse()
.expect("value must be parseable as url"),
],
authoritative: Some(false),
role: Some("Beschlussfassung".into()),
license: None,
keyword: vec![],
created: datetime!(2012-01-08 14:05:27 +01:00).into(),
modified: datetime!(2012-01-08 14:05:27 +01:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}],
license: None,
keyword: vec![],
created: datetime!(2013-01-08 12:05:27 +01:00).into(),
modified: datetime!(2013-01-08 12:05:27 +01:00).into(),
web: None,
deleted: None,
extensions: serde_json::Map::new(),
}
}
fn example_paper_json() -> serde_json::Value {
json!({
"id": "https://oparl.example.org/paper/749",
"type": "https://schema.oparl.org/1.1/Paper",
"body": "https://oparl.example.org/bodies/1",
"name": "Antwort auf Anfrage 1200/2014",
"reference": "1234/2014",
"date": "2014-04-04",
"paperType": "Beantwortung einer Anfrage",
"relatedPaper": [
"https://oparl.example.org/paper/699"
],
"mainFile": {
"id": "https://oparl.example.org/files/57737",
"type": "https://schema.oparl.org/1.1/File",
"name": "Anlage 1 zur Anfrage",
"fileName": "anlage_1_zur_anfrage.pdf",
"mimeType": "application/pdf",
"date": "2013-01-04",
"sha1Checksum": "d749751af44a32c818b9b1e1515251c67734f5d2",
"size": 82930,
"accessUrl": "https://oparl.example.org/files/57737.pdf",
"downloadUrl": "https://oparl.example.org/files/download/57737.pdf",
"license": "http://www.opendefinition.org/licenses/cc-by",
"created": "2013-01-04T07:54:13+01:00",
"modified": "2013-01-04T07:54:13+01:00"
},
"auxiliaryFile": [
{
"id": "https://oparl.example.org/files/57739",
"type": "https://schema.oparl.org/1.1/File",
"name": "Anlage 1 zur Anfrage",
"fileName": "anlage.pdf",
"mimeType": "application/pdf",
"date": "2013-01-04",
"sha1Checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
"size": 82930,
"accessUrl": "https://oparl.example.org/files/57739.pdf",
"downloadUrl": "https://oparl.example.org/files/download/57739.pdf",
"text": "Der Übersichtsplan zeigt alle Ebenen des ...",
"masterFile": "https://oparl.example.org/files/57738",
"license": "http://www.opendefinition.org/licenses/cc-by",
"created": "2013-01-04T07:54:13+01:00",
"modified": "2013-01-04T07:54:13+01:00"
}
],
"location": [
{
"id": "https://oparl.example.org/locations/29856",
"type": "https://schema.oparl.org/1.1/Location",
"description": "Honschaftsstraße 312, Köln",
"created": "2012-01-08T14:05:27+01:00",
"modified": "2012-01-08T14:05:27+01:00",
"geojson": {
"type": "Point",
"coordinates": [
7.03291,
50.98249
]
}
}
],
"originatorPerson": [
"https://oparl.example.org/person/2000",
"https://oparl.example.org/person/1000"
],
"originatorOrganization": [
"https://oparl.example.org/organization/2000",
"https://oparl.example.org/organization/1000"
],
"consultation": [
{
"id": "https://oparl.example.org/consultation/47594",
"type": "https://schema.oparl.org/1.1/Consultation",
"agendaItem": "https://oparl.example.org/agendaitem/15569",
"meeting": "https://oparl.example.org/meeting/243",
"organization": [
"https://oparl.example.org/organization/96"
],
"authoritative": false,
"role": "Beschlussfassung",
"created": "2012-01-08T14:05:27+01:00",
"modified": "2012-01-08T14:05:27+01:00"
}
],
"underDirectionOf": [
"https://oparl.example.org/organization/2000"
],
"created": "2013-01-08T12:05:27+01:00",
"modified": "2013-01-08T12:05:27+01:00"})
}
#[test]
fn serialize() {
assert_eq!(json!(example_paper()), example_paper_json());
}
#[test]
fn deserialize_good() {
let deserialized: Paper = serde_json::from_value(example_paper_json())
.expect("value must be deserializable as Paper");
assert_eq!(deserialized, example_paper());
}
#[test]
fn deserialize_bad() {
assert!(serde_json::from_value::<Paper>(json!("xyzabcd")).is_err());
assert!(serde_json::from_value::<Paper>(json!(true)).is_err());
assert!(serde_json::from_value::<Paper>(json!(123)).is_err());
}
}