spdx_rs/models/
snippet.rs

1// SPDX-FileCopyrightText: 2020-2021 HH Partners
2//
3// SPDX-License-Identifier: MIT
4
5use serde::{Deserialize, Serialize};
6use spdx_expression::SpdxExpression;
7
8/// <https://spdx.github.io/spdx-spec/5-snippet-information/>
9#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
10pub struct Snippet {
11    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#51-snippet-spdx-identifier>
12    #[serde(rename = "SPDXID")]
13    pub snippet_spdx_identifier: String,
14
15    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#52-snippet-from-file-spdx-identifier>
16    #[serde(rename = "snippetFromFile")]
17    pub snippet_from_file_spdx_identifier: String,
18
19    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#53-snippet-byte-range>
20    pub ranges: Vec<Range>,
21
22    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#55-snippet-concluded-license>
23    #[serde(
24        rename = "licenseConcluded",
25        skip_serializing_if = "Option::is_none",
26        default
27    )]
28    pub snippet_concluded_license: Option<SpdxExpression>,
29
30    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#56-license-information-in-snippet>
31    #[serde(
32        rename = "licenseInfoInSnippets",
33        skip_serializing_if = "Vec::is_empty",
34        default
35    )]
36    pub license_information_in_snippet: Vec<String>,
37
38    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#57-snippet-comments-on-license>
39    #[serde(
40        rename = "licenseComments",
41        skip_serializing_if = "Option::is_none",
42        default
43    )]
44    pub snippet_comments_on_license: Option<String>,
45
46    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#58-snippet-copyright-text>
47    #[serde(
48        rename = "copyrightText",
49        skip_serializing_if = "Option::is_none",
50        default
51    )]
52    pub snippet_copyright_text: Option<String>,
53
54    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#59-snippet-comment>
55    #[serde(rename = "comment", skip_serializing_if = "Option::is_none", default)]
56    pub snippet_comment: Option<String>,
57
58    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#510-snippet-name>
59    #[serde(rename = "name", skip_serializing_if = "Option::is_none", default)]
60    pub snippet_name: Option<String>,
61
62    /// <https://spdx.github.io/spdx-spec/5-snippet-information/#511-snippet-attribution-text>
63    #[serde(
64        rename = "attributionText",
65        skip_serializing_if = "Option::is_none",
66        default
67    )]
68    pub snippet_attribution_text: Option<String>,
69}
70
71/// <https://spdx.github.io/spdx-spec/5-snippet-information/#53-snippet-byte-range>
72#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
73#[serde(rename_all = "camelCase")]
74pub struct Range {
75    pub start_pointer: Pointer,
76    pub end_pointer: Pointer,
77}
78
79impl Range {
80    pub fn new(start_pointer: Pointer, end_pointer: Pointer) -> Self {
81        Self {
82            start_pointer,
83            end_pointer,
84        }
85    }
86}
87
88#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
89#[serde(untagged)]
90pub enum Pointer {
91    Byte {
92        reference: Option<String>,
93        offset: i32,
94    },
95    Line {
96        reference: Option<String>,
97        #[serde(rename = "lineNumber")]
98        line_number: i32,
99    },
100}
101
102impl Pointer {
103    /// Create a new [`Pointer::Byte`].
104    pub fn new_byte(reference: Option<String>, offset: i32) -> Self {
105        Self::Byte { reference, offset }
106    }
107
108    /// Create a new [`Pointer::Line`].
109    pub fn new_line(reference: Option<String>, line_number: i32) -> Self {
110        Self::Line {
111            reference,
112            line_number,
113        }
114    }
115}
116
117#[cfg(test)]
118mod test {
119    use std::fs::read_to_string;
120
121    use crate::models::SPDX;
122
123    use super::*;
124
125    #[test]
126    fn snippet_spdx_identifier() {
127        let spdx: SPDX = serde_json::from_str(
128            &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
129        )
130        .unwrap();
131        assert_eq!(
132            spdx.snippet_information[0].snippet_spdx_identifier,
133            "SPDXRef-Snippet".to_string()
134        );
135    }
136    #[test]
137    fn snippet_from_file_spdx_identifier() {
138        let spdx: SPDX = serde_json::from_str(
139            &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
140        )
141        .unwrap();
142        assert_eq!(
143            spdx.snippet_information[0].snippet_from_file_spdx_identifier,
144            "SPDXRef-DoapSource".to_string()
145        );
146    }
147    #[test]
148    fn ranges() {
149        let spdx: SPDX = serde_json::from_str(
150            &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
151        )
152        .unwrap();
153        assert_eq!(
154            spdx.snippet_information[0].ranges,
155            vec![
156                Range {
157                    end_pointer: Pointer::Line {
158                        line_number: 23,
159                        reference: Some("SPDXRef-DoapSource".to_string()),
160                    },
161                    start_pointer: Pointer::Line {
162                        line_number: 5,
163                        reference: Some("SPDXRef-DoapSource".to_string()),
164                    },
165                },
166                Range {
167                    end_pointer: Pointer::Byte {
168                        offset: 420,
169                        reference: Some("SPDXRef-DoapSource".to_string()),
170                    },
171                    start_pointer: Pointer::Byte {
172                        offset: 310,
173                        reference: Some("SPDXRef-DoapSource".to_string()),
174                    },
175                },
176            ]
177        );
178    }
179    #[test]
180    fn snippet_concluded_license() {
181        let spdx: SPDX = serde_json::from_str(
182            &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
183        )
184        .unwrap();
185        assert_eq!(
186            spdx.snippet_information[0]
187                .snippet_concluded_license
188                .as_ref()
189                .unwrap()
190                .clone(),
191            SpdxExpression::parse("GPL-2.0-only").unwrap()
192        );
193    }
194    #[test]
195    fn license_information_in_snippet() {
196        let spdx: SPDX = serde_json::from_str(
197            &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
198        )
199        .unwrap();
200        assert_eq!(
201            spdx.snippet_information[0].license_information_in_snippet,
202            vec!["GPL-2.0-only".to_string()]
203        );
204    }
205    #[test]
206    fn snippet_comments_on_license() {
207        let spdx: SPDX = serde_json::from_str(
208            &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
209        )
210        .unwrap();
211        assert_eq!(
212                    spdx.snippet_information[0].snippet_comments_on_license,
213                    Some("The concluded license was taken from package xyz, from which the snippet was copied into the current file. The concluded license information was found in the COPYING.txt file in package xyz.".to_string())
214                );
215    }
216    #[test]
217    fn snippet_copyright_text() {
218        let spdx: SPDX = serde_json::from_str(
219            &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
220        )
221        .unwrap();
222        assert_eq!(
223            spdx.snippet_information[0]
224                .snippet_copyright_text
225                .as_ref()
226                .unwrap()
227                .clone(),
228            "Copyright 2008-2010 John Smith".to_string()
229        );
230    }
231    #[test]
232    fn snippet_comment() {
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.snippet_information[0].snippet_comment,
239                    Some("This snippet was identified as significant and highlighted in this Apache-2.0 file, when a commercial scanner identified it as being derived from file foo.c in package xyz which is licensed under GPL-2.0.".to_string())
240                );
241    }
242    #[test]
243    fn snippet_name() {
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.snippet_information[0].snippet_name,
250            Some("from linux kernel".to_string())
251        );
252    }
253}