1// SPDX-FileCopyrightText: 2020-2021 HH Partners
2//
3// SPDX-License-Identifier: MIT
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8use super::Checksum;
9
10/// ## Document Creation Information
11///
12/// SPDX's [Document Creation Information](https://spdx.github.io/spdx-spec/2-document-creation-information/)
13#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
14#[serde(rename_all = "camelCase")]
15pub struct DocumentCreationInformation {
16 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#21-spdx-version>
17 pub spdx_version: String,
18
19 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#22-data-license>
20 pub data_license: String,
21
22 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#23-spdx-identifier>
23 #[serde(rename = "SPDXID")]
24 pub spdx_identifier: String,
25
26 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#24-document-name>
27 #[serde(rename = "name")]
28 pub document_name: String,
29
30 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#25-spdx-document-namespace>
31 #[serde(rename = "documentNamespace")]
32 pub spdx_document_namespace: String,
33
34 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#26-external-document-references>
35 #[serde(
36 rename = "externalDocumentRefs",
37 skip_serializing_if = "Vec::is_empty",
38 default
39 )]
40 pub external_document_references: Vec<ExternalDocumentReference>,
41
42 pub creation_info: CreationInfo,
43
44 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#211-document-comment>
45 #[serde(rename = "comment", skip_serializing_if = "Option::is_none", default)]
46 pub document_comment: Option<String>,
47
48 /// Doesn't seem to be in spec, but the example contains it.
49 /// <https://github.com/spdx/spdx-spec/issues/395>
50 #[serde(skip_serializing_if = "Vec::is_empty", default)]
51 pub document_describes: Vec<String>,
52}
53
54impl Default for DocumentCreationInformation {
55 fn default() -> Self {
56 Self {
57 // Current version is 2.2. Might need to support more verisons
58 // in the future.
59 spdx_version: "SPDX-2.2".to_string(),
60 data_license: "CC0-1.0".to_string(),
61 spdx_identifier: "SPDXRef-DOCUMENT".to_string(),
62 document_name: "NOASSERTION".to_string(),
63 spdx_document_namespace: "NOASSERTION".to_string(),
64 external_document_references: Vec::new(),
65 document_comment: None,
66 creation_info: CreationInfo::default(),
67 document_describes: Vec::new(),
68 }
69 }
70}
71
72#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
73#[serde(rename_all = "camelCase")]
74pub struct CreationInfo {
75 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#27-license-list-version>
76 #[serde(skip_serializing_if = "Option::is_none", default)]
77 pub license_list_version: Option<String>,
78
79 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#28-creator>
80 pub creators: Vec<String>,
81
82 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#29-created>
83 pub created: DateTime<Utc>,
84
85 /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#210-creator-comment>
86 #[serde(skip_serializing_if = "Option::is_none", default)]
87 #[serde(rename = "comment")]
88 pub creator_comment: Option<String>,
89}
90
91impl Default for CreationInfo {
92 fn default() -> Self {
93 Self {
94 license_list_version: None,
95 creators: vec![
96 "Person: Jane Doe ()".into(),
97 "Organization: ExampleCodeInspect ()".into(),
98 "Tool: LicenseFind-1.0".into(),
99 ],
100 created: chrono::offset::Utc::now(),
101 creator_comment: None,
102 }
103 }
104}
105
106/// <https://spdx.github.io/spdx-spec/2-document-creation-information/#26-external-document-references>
107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd)]
108pub struct ExternalDocumentReference {
109 /// Unique ID string of the reference.
110 #[serde(rename = "externalDocumentId")]
111 pub id_string: String,
112
113 /// Unique ID for the external document.
114 #[serde(rename = "spdxDocument")]
115 pub spdx_document_uri: String,
116
117 /// Checksum of the external document following the checksum format defined
118 /// in <https://spdx.github.io/spdx-spec/4-file-information/#44-file-checksum.>
119 pub checksum: Checksum,
120}
121
122impl ExternalDocumentReference {
123 pub const fn new(id_string: String, spdx_document_uri: String, checksum: Checksum) -> Self {
124 Self {
125 id_string,
126 spdx_document_uri,
127 checksum,
128 }
129 }
130}
131
132#[cfg(test)]
133mod test {
134 use std::fs::read_to_string;
135
136 use chrono::{TimeZone, Utc};
137
138 use super::*;
139 use crate::models::{Algorithm, SPDX};
140
141 #[test]
142 fn spdx_version() {
143 let spdx: SPDX = serde_json::from_str(
144 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
145 )
146 .unwrap();
147
148 assert_eq!(
149 spdx.document_creation_information.spdx_version,
150 "SPDX-2.2".to_string()
151 );
152 }
153 #[test]
154 fn data_license() {
155 let spdx: SPDX = serde_json::from_str(
156 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
157 )
158 .unwrap();
159
160 assert_eq!(spdx.document_creation_information.data_license, "CC0-1.0");
161 }
162 #[test]
163 fn spdx_identifier() {
164 let spdx: SPDX = serde_json::from_str(
165 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
166 )
167 .unwrap();
168 assert_eq!(
169 spdx.document_creation_information.spdx_identifier,
170 "SPDXRef-DOCUMENT".to_string()
171 );
172 }
173 #[test]
174 fn document_name() {
175 let spdx: SPDX = serde_json::from_str(
176 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
177 )
178 .unwrap();
179 assert_eq!(
180 spdx.document_creation_information.document_name,
181 "SPDX-Tools-v2.0".to_string()
182 );
183 }
184 #[test]
185 fn spdx_document_namespace() {
186 let spdx: SPDX = serde_json::from_str(
187 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
188 )
189 .unwrap();
190 assert_eq!(
191 spdx.document_creation_information.spdx_document_namespace,
192 "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301"
193 .to_string()
194 );
195 }
196 #[test]
197 fn license_list_version() {
198 let spdx: SPDX = serde_json::from_str(
199 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
200 )
201 .unwrap();
202 assert_eq!(
203 spdx.document_creation_information
204 .creation_info
205 .license_list_version,
206 Some("3.9".to_string())
207 );
208 }
209 #[test]
210 fn creators() {
211 let spdx: SPDX = serde_json::from_str(
212 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
213 )
214 .unwrap();
215 assert!(spdx
216 .document_creation_information
217 .creation_info
218 .creators
219 .contains(&"Tool: LicenseFind-1.0".to_string()));
220 assert!(spdx
221 .document_creation_information
222 .creation_info
223 .creators
224 .contains(&"Organization: ExampleCodeInspect ()".to_string()));
225 assert!(spdx
226 .document_creation_information
227 .creation_info
228 .creators
229 .contains(&"Person: Jane Doe ()".to_string()));
230 }
231 #[test]
232 fn created() {
233 let spdx: SPDX = serde_json::from_str(
234 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
235 )
236 .unwrap();
237 assert_eq!(
238 spdx.document_creation_information.creation_info.created,
239 Utc.with_ymd_and_hms(2010, 1, 29, 18, 30, 22).unwrap()
240 );
241 }
242 #[test]
243 fn creator_comment() {
244 let spdx: SPDX = serde_json::from_str(
245 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
246 )
247 .unwrap();
248 assert_eq!(
249 spdx.document_creation_information
250 .creation_info
251 .creator_comment,
252 Some(
253 r#"This package has been shipped in source and binary form.
254The binaries were created with gcc 4.5.1 and expect to link to
255compatible system run time libraries."#
256 .to_string()
257 )
258 );
259 }
260 #[test]
261 fn document_comment() {
262 let spdx: SPDX = serde_json::from_str(
263 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
264 )
265 .unwrap();
266 assert_eq!(
267 spdx.document_creation_information.document_comment,
268 Some(
269 "This document was created using SPDX 2.0 using licenses from the web site."
270 .to_string()
271 )
272 );
273 }
274
275 #[test]
276 fn external_document_references() {
277 let spdx: SPDX = serde_json::from_str(
278 &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
279 )
280 .unwrap();
281 assert!(spdx
282 .document_creation_information
283 .external_document_references
284 .contains(&ExternalDocumentReference {
285 id_string: "DocumentRef-spdx-tool-1.2".to_string(),
286 checksum: Checksum {
287 algorithm: Algorithm::SHA1,
288 value: "d6a770ba38583ed4bb4525bd96e50461655d2759".to_string()
289 },
290 spdx_document_uri:
291 "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"
292 .to_string()
293 }));
294 }
295}
296