1use url::Url;
7
8use crate::{
9 namespace::PaperNamespaceUrl, BodyId, Consultation, Date, DateTime, File, Keyword, Location,
10 Name, OrganizationId, PaperId, PersonId,
11};
12
13#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct Paper {
16 pub id: PaperId,
17
18 #[serde(rename = "type")]
19 pub namespace: PaperNamespaceUrl,
20
21 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub body: Option<BodyId>,
23
24 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub name: Option<Name>,
26
27 #[serde(default, skip_serializing_if = "Option::is_none")]
28 pub reference: Option<String>,
29
30 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub date: Option<Date>,
32
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub paper_type: Option<String>,
35
36 #[serde(default, skip_serializing_if = "Vec::is_empty")]
37 pub related_paper: Vec<PaperId>,
38
39 #[serde(default, skip_serializing_if = "Vec::is_empty")]
40 pub superordinated_paper: Vec<PaperId>,
41
42 #[serde(default, skip_serializing_if = "Vec::is_empty")]
43 pub subordinated_paper: Vec<PaperId>,
44
45 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub main_file: Option<File>,
47
48 #[serde(default, skip_serializing_if = "Vec::is_empty")]
49 pub auxiliary_file: Vec<File>,
50
51 #[serde(default, skip_serializing_if = "Vec::is_empty")]
52 pub location: Vec<Location>,
53
54 #[serde(default, skip_serializing_if = "Vec::is_empty")]
55 pub originator_person: Vec<PersonId>,
56
57 #[serde(default, skip_serializing_if = "Vec::is_empty")]
58 pub under_direction_of: Vec<OrganizationId>,
59
60 #[serde(default, skip_serializing_if = "Vec::is_empty")]
61 pub originator_organization: Vec<OrganizationId>,
62
63 #[serde(default, skip_serializing_if = "Vec::is_empty")]
64 pub consultation: Vec<Consultation>,
65
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub license: Option<String>,
68
69 #[serde(default, skip_serializing_if = "Vec::is_empty")]
70 pub keyword: Vec<Keyword>,
71
72 pub created: DateTime,
73
74 pub modified: DateTime,
75
76 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub web: Option<Url>,
78
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub deleted: Option<bool>,
81}
82
83#[cfg(test)]
84mod serde_tests {
85 use super::Paper;
86 use crate::{
87 namespace::{
88 ConsultationNamespaceUrl, FileNamespaceUrl, LocationNamespaceUrl, PaperNamespaceUrl,
89 },
90 Consultation, File, Location, Sha1Sum,
91 };
92
93 use pretty_assertions::assert_eq;
94 use serde_json::json;
95 use time::macros::{date, datetime};
96
97 fn example_paper() -> Paper {
98 let geojson_feature = geojson::Value::Point(vec![7.03291, 50.98249]);
99
100 Paper {
101 id: "https://oparl.example.org/paper/749"
102 .parse()
103 .expect("value must be parseable as id"),
104 namespace: PaperNamespaceUrl::Identifier,
105 body: Some(
106 "https://oparl.example.org/bodies/1"
107 .parse()
108 .expect("value must be parseable as url"),
109 ),
110 name: Some("Antwort auf Anfrage 1200/2014".into()),
111 reference: Some("1234/2014".into()),
112 date: Some(date!(2014 - 04 - 04).into()),
113 paper_type: Some("Beantwortung einer Anfrage".into()),
114 related_paper: vec!["https://oparl.example.org/paper/699"
115 .parse()
116 .expect("value must be parseable as url")],
117 superordinated_paper: vec![],
118 subordinated_paper: vec![],
119 main_file: Some(File {
120 id: "https://oparl.example.org/files/57737"
121 .parse()
122 .expect("value must be parseable as id"),
123 namespace: FileNamespaceUrl::Identifier,
124 file_name: Some("anlage_1_zur_anfrage.pdf".into()),
125 name: Some("Anlage 1 zur Anfrage".into()),
126 mime_type: Some("application/pdf".into()),
127 date: Some(date!(2013 - 01 - 04).into()),
128 size: Some(82930),
129 sha1_checksum: Some(Sha1Sum::from([
130 0xd7, 0x49, 0x75, 0x1a, 0xf4, 0x4a, 0x32, 0xc8, 0x18, 0xb9, 0xb1, 0xe1, 0x51,
131 0x52, 0x51, 0xc6, 0x77, 0x34, 0xf5, 0xd2,
132 ])),
133 sha512_checksum: None,
134 text: None,
135 access_url: "https://oparl.example.org/files/57737.pdf"
136 .parse()
137 .expect("value must be parseable as url"),
138 download_url: Some(
139 "https://oparl.example.org/files/download/57737.pdf"
140 .parse()
141 .expect("value must be parseable as url"),
142 ),
143 external_service_url: None,
144 master_file: None,
145 derivative_file: vec![],
146 file_license: None,
147 meeting: vec![],
148 agenda_item: vec![],
149 paper: vec![],
150 license: Some(
151 "http://www.opendefinition.org/licenses/cc-by"
152 .parse()
153 .expect("value must be parseable as url"),
154 ),
155 keyword: vec![],
156 created: datetime!(2013-01-04 07:54:13 +01:00).into(),
157 modified: datetime!(2013-01-04 07:54:13 +01:00).into(),
158 web: None,
159 deleted: None,
160 }),
161 auxiliary_file: vec![File {
162 id: "https://oparl.example.org/files/57739"
163 .parse()
164 .expect("value must be parseable as id"),
165 namespace: FileNamespaceUrl::Identifier,
166 file_name: Some("anlage.pdf".into()),
167 name: Some("Anlage 1 zur Anfrage".into()),
168 mime_type: Some("application/pdf".into()),
169 date: Some(date!(2013 - 01 - 04).into()),
170 size: Some(82930),
171 sha1_checksum: Some(Sha1Sum::from([
172 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95,
173 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
174 ])),
175 sha512_checksum: None,
176 text: Some("Der Übersichtsplan zeigt alle Ebenen des ...".into()),
177 access_url: "https://oparl.example.org/files/57739.pdf"
178 .parse()
179 .expect("value must be parseable as url"),
180 download_url: Some(
181 "https://oparl.example.org/files/download/57739.pdf"
182 .parse()
183 .expect("value must be parseable as url"),
184 ),
185 external_service_url: None,
186 master_file: Some(
187 "https://oparl.example.org/files/57738"
188 .parse()
189 .expect("value must be parseable as id"),
190 ),
191 derivative_file: vec![],
192 file_license: None,
193 meeting: vec![],
194 agenda_item: vec![],
195 paper: vec![],
196 license: Some(
197 "http://www.opendefinition.org/licenses/cc-by"
198 .parse()
199 .expect("value must be parseable as url"),
200 ),
201 keyword: vec![],
202 created: datetime!(2013-01-04 07:54:13 +01:00).into(),
203 modified: datetime!(2013-01-04 07:54:13 +01:00).into(),
204 web: None,
205 deleted: None,
206 }],
207 location: vec![Location {
208 id: "https://oparl.example.org/locations/29856"
209 .parse()
210 .expect("value must be parseable as id"),
211 namespace: LocationNamespaceUrl::Identifier,
212 description: Some("Honschaftsstraße 312, Köln".to_string()),
213 geojson: Some(geojson_feature.into()),
214 street_address: None,
215 room: None,
216 postal_code: None,
217 sub_locality: None,
218 locality: None,
219 bodies: vec![],
220 organizations: vec![],
221 persons: vec![],
222 meetings: vec![],
223 papers: vec![],
224 license: None,
225 keyword: vec![],
226 created: datetime!(2012-01-08 14:05:27 +01:00).into(),
227 modified: datetime!(2012-01-08 14:05:27 +01:00).into(),
228 web: None,
229 deleted: None,
230 }],
231 originator_person: vec![
232 "https://oparl.example.org/person/2000"
233 .parse()
234 .expect("value must be parseable as url"),
235 "https://oparl.example.org/person/1000"
236 .parse()
237 .expect("value must be parseable as url"),
238 ],
239 under_direction_of: vec!["https://oparl.example.org/organization/2000"
240 .parse()
241 .expect("value must be parseable as url")],
242 originator_organization: vec![
243 "https://oparl.example.org/organization/2000"
244 .parse()
245 .expect("value must be parseable as url"),
246 "https://oparl.example.org/organization/1000"
247 .parse()
248 .expect("value must be parseable as url"),
249 ],
250 consultation: vec![Consultation {
251 id: "https://oparl.example.org/consultation/47594"
252 .parse()
253 .expect("value must be parseable as id"),
254 namespace: ConsultationNamespaceUrl::Identifier,
255 paper: None,
256 agenda_item: Some(
257 "https://oparl.example.org/agendaitem/15569"
258 .parse()
259 .expect("value must be parseable as url"),
260 ),
261 meeting: Some(
262 "https://oparl.example.org/meeting/243"
263 .parse()
264 .expect("value must be parseable as url"),
265 ),
266 organization: vec!["https://oparl.example.org/organization/96"
267 .parse()
268 .expect("value must be parseable as url")],
269 authoritative: Some(false),
270 role: Some("Beschlussfassung".into()),
271 license: None,
272 keyword: vec![],
273 created: datetime!(2012-01-08 14:05:27 +01:00).into(),
274 modified: datetime!(2012-01-08 14:05:27 +01:00).into(),
275 web: None,
276 deleted: None,
277 }],
278 license: None,
279 keyword: vec![],
280 created: datetime!(2013-01-08 12:05:27 +01:00).into(),
281 modified: datetime!(2013-01-08 12:05:27 +01:00).into(),
282 web: None,
283 deleted: None,
284 }
285 }
286
287 fn example_paper_json() -> serde_json::Value {
288 json!({
289 "id": "https://oparl.example.org/paper/749",
290 "type": "https://schema.oparl.org/1.1/Paper",
291 "body": "https://oparl.example.org/bodies/1",
292 "name": "Antwort auf Anfrage 1200/2014",
293 "reference": "1234/2014",
294 "date": "2014-04-04",
295 "paperType": "Beantwortung einer Anfrage",
296 "relatedPaper": [
297 "https://oparl.example.org/paper/699"
298 ],
299 "mainFile": {
300 "id": "https://oparl.example.org/files/57737",
301 "type": "https://schema.oparl.org/1.1/File",
302 "name": "Anlage 1 zur Anfrage",
303 "fileName": "anlage_1_zur_anfrage.pdf",
304 "mimeType": "application/pdf",
305 "date": "2013-01-04",
306 "sha1Checksum": "d749751af44a32c818b9b1e1515251c67734f5d2",
307 "size": 82930,
308 "accessUrl": "https://oparl.example.org/files/57737.pdf",
309 "downloadUrl": "https://oparl.example.org/files/download/57737.pdf",
310 "license": "http://www.opendefinition.org/licenses/cc-by",
311 "created": "2013-01-04T07:54:13+01:00",
312 "modified": "2013-01-04T07:54:13+01:00"
313 },
314 "auxiliaryFile": [
315 {
316 "id": "https://oparl.example.org/files/57739",
317 "type": "https://schema.oparl.org/1.1/File",
318 "name": "Anlage 1 zur Anfrage",
319 "fileName": "anlage.pdf",
320 "mimeType": "application/pdf",
321 "date": "2013-01-04",
322 "sha1Checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
323 "size": 82930,
324 "accessUrl": "https://oparl.example.org/files/57739.pdf",
325 "downloadUrl": "https://oparl.example.org/files/download/57739.pdf",
326 "text": "Der Übersichtsplan zeigt alle Ebenen des ...",
327 "masterFile": "https://oparl.example.org/files/57738",
328 "license": "http://www.opendefinition.org/licenses/cc-by",
329 "created": "2013-01-04T07:54:13+01:00",
330 "modified": "2013-01-04T07:54:13+01:00"
331 }
332 ],
333 "location": [
334 {
335 "id": "https://oparl.example.org/locations/29856",
336 "type": "https://schema.oparl.org/1.1/Location",
337 "description": "Honschaftsstraße 312, Köln",
338 "created": "2012-01-08T14:05:27+01:00",
339 "modified": "2012-01-08T14:05:27+01:00",
340 "geojson": {
341 "type": "Point",
342 "coordinates": [
343 7.03291,
344 50.98249
345 ]
346 }
347 }
348 ],
349 "originatorPerson": [
350 "https://oparl.example.org/person/2000",
351 "https://oparl.example.org/person/1000"
352 ],
353 "originatorOrganization": [
354 "https://oparl.example.org/organization/2000",
355 "https://oparl.example.org/organization/1000"
356 ],
357 "consultation": [
358 {
359 "id": "https://oparl.example.org/consultation/47594",
360 "type": "https://schema.oparl.org/1.1/Consultation",
361 "agendaItem": "https://oparl.example.org/agendaitem/15569",
362 "meeting": "https://oparl.example.org/meeting/243",
363 "organization": [
364 "https://oparl.example.org/organization/96"
365 ],
366 "authoritative": false,
367 "role": "Beschlussfassung",
368 "created": "2012-01-08T14:05:27+01:00",
369 "modified": "2012-01-08T14:05:27+01:00"
370 }
371 ],
372 "underDirectionOf": [
373 "https://oparl.example.org/organization/2000"
374 ],
375 "created": "2013-01-08T12:05:27+01:00",
376 "modified": "2013-01-08T12:05:27+01:00"})
377 }
378
379 #[test]
380 fn serialize() {
381 assert_eq!(json!(example_paper()), example_paper_json());
382 }
383
384 #[test]
385 fn deserialize_good() {
386 let deserialized: Paper = serde_json::from_value(example_paper_json())
387 .expect("value must be deserializable as Paper");
388 assert_eq!(deserialized, example_paper());
389 }
390
391 #[test]
392 fn deserialize_bad() {
393 assert!(serde_json::from_value::<Paper>(json!("xyzabcd")).is_err());
394 assert!(serde_json::from_value::<Paper>(json!(true)).is_err());
395 assert!(serde_json::from_value::<Paper>(json!(123)).is_err());
396 }
397}