oparl_types/
file.rs

1// SPDX-FileCopyrightText: Politik im Blick developers
2// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2
5
6use url::Url;
7
8use crate::{
9    date::Date, namespace::FileNamespaceUrl, AgendaItemId, DateTime, FileId, Keyword, MeetingId,
10    Name, PaperId, Sha1Sum, Sha512Sum,
11};
12
13#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct File {
16    pub id: FileId,
17
18    #[serde(rename = "type")]
19    pub namespace: FileNamespaceUrl,
20
21    #[serde(default, skip_serializing_if = "Option::is_none")]
22    pub name: Option<Name>,
23
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub file_name: Option<Name>,
26
27    #[serde(default, skip_serializing_if = "Option::is_none")]
28    pub mime_type: 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 size: Option<u64>,
35
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub sha1_checksum: Option<Sha1Sum>,
38
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub sha512_checksum: Option<Sha512Sum>,
41
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub text: Option<String>,
44
45    pub access_url: Url,
46
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub download_url: Option<Url>,
49
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub external_service_url: Option<Url>,
52
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub master_file: Option<FileId>,
55
56    #[serde(default, skip_serializing_if = "Vec::is_empty")]
57    pub derivative_file: Vec<FileId>,
58
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub file_license: Option<Url>,
61
62    #[serde(default, skip_serializing_if = "Vec::is_empty")]
63    pub meeting: Vec<MeetingId>,
64
65    #[serde(default, skip_serializing_if = "Vec::is_empty")]
66    pub agenda_item: Vec<AgendaItemId>,
67
68    #[serde(default, skip_serializing_if = "Vec::is_empty")]
69    pub paper: Vec<PaperId>,
70
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub license: Option<Url>,
73
74    #[serde(default, skip_serializing_if = "Vec::is_empty")]
75    pub keyword: Vec<Keyword>,
76
77    pub created: DateTime,
78
79    pub modified: DateTime,
80
81    #[serde(default, skip_serializing_if = "Option::is_none")]
82    pub web: Option<Url>,
83
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub deleted: Option<bool>,
86}
87
88#[cfg(test)]
89mod serde_tests {
90    use super::File;
91    use crate::{namespace::FileNamespaceUrl, Sha1Sum};
92
93    use pretty_assertions::assert_eq;
94    use serde_json::json;
95    use time::macros::{date, datetime};
96
97    fn example_file() -> File {
98        File {
99            id: "https://oparl.example.org/files/57737"
100                .parse()
101                .expect("value must be parseable as id"),
102            namespace: FileNamespaceUrl::Identifier,
103            file_name: Some("anlage_1_zur_anfrage.pdf".into()),
104            name: Some("Anlage 1 zur Anfrage".into()),
105            mime_type: Some("application/pdf".into()),
106            date: Some(date!(2013 - 01 - 04).into()),
107            size: Some(82930),
108            sha1_checksum: Some(Sha1Sum::from([
109                0xd7, 0x49, 0x75, 0x1a, 0xf4, 0x4a, 0x32, 0xc8, 0x18, 0xb9, 0xb1, 0xe1, 0x51, 0x52,
110                0x51, 0xc6, 0x77, 0x34, 0xf5, 0xd2,
111            ])),
112            sha512_checksum: None,
113            text: None,
114            access_url: "https://oparl.example.org/files/57737.pdf"
115                .parse()
116                .expect("value must be parseable as url"),
117            download_url: Some(
118                "https://oparl.example.org/files/download/57737.pdf"
119                    .parse()
120                    .expect("value must be parseable as url"),
121            ),
122            external_service_url: None,
123            master_file: None,
124            derivative_file: vec!["https://oparl.example.org/files/57739"
125                .parse()
126                .expect("value must be parseable as url")],
127            file_license: Some(
128                "http://www.opendefinition.org/licenses/cc-by"
129                    .parse()
130                    .expect("value must be parseable as url"),
131            ),
132            meeting: vec![],
133            agenda_item: vec![],
134            paper: vec![],
135            license: None,
136            keyword: vec![],
137            created: datetime!(2013-01-04 07:54:13 +01:00).into(),
138            modified: datetime!(2013-01-04 07:54:13 +01:00).into(),
139            web: None,
140            deleted: None,
141        }
142    }
143
144    fn example_file_json() -> serde_json::Value {
145        json!({
146            "id": "https://oparl.example.org/files/57737",
147            "type": "https://schema.oparl.org/1.1/File",
148            "name": "Anlage 1 zur Anfrage",
149            "fileName": "anlage_1_zur_anfrage.pdf",
150            "mimeType": "application/pdf",
151            "date": "2013-01-04",
152            "size": 82930,
153            "sha1Checksum": "d749751af44a32c818b9b1e1515251c67734f5d2",
154            "accessUrl": "https://oparl.example.org/files/57737.pdf",
155            "downloadUrl": "https://oparl.example.org/files/download/57737.pdf",
156            "derivativeFile": [
157                "https://oparl.example.org/files/57739"
158            ],
159            "fileLicense": "http://www.opendefinition.org/licenses/cc-by",
160            "created": "2013-01-04T07:54:13+01:00",
161            "modified": "2013-01-04T07:54:13+01:00"
162        })
163    }
164
165    #[test]
166    fn serialize() {
167        assert_eq!(json!(example_file()), example_file_json());
168    }
169
170    #[test]
171    fn deserialize_good() {
172        let deserialized: File = serde_json::from_value(example_file_json())
173            .expect("value must be deserializable as File");
174        assert_eq!(deserialized, example_file());
175    }
176
177    #[test]
178    fn deserialize_bad() {
179        assert!(serde_json::from_value::<File>(json!("xyzabcd")).is_err());
180        assert!(serde_json::from_value::<File>(json!(true)).is_err());
181        assert!(serde_json::from_value::<File>(json!(123)).is_err());
182    }
183}