summaryrefslogtreecommitdiffstats
path: root/vendor/spdx-rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
commit4547b622d8d29df964fa2914213088b148c498fc (patch)
tree9fc6b25f3c3add6b745be9a2400a6e96140046e9 /vendor/spdx-rs
parentReleasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz
rustc-4547b622d8d29df964fa2914213088b148c498fc.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/spdx-rs')
-rw-r--r--vendor/spdx-rs/.cargo-checksum.json1
-rw-r--r--vendor/spdx-rs/CHANGELOG.md66
-rw-r--r--vendor/spdx-rs/Cargo.toml62
-rw-r--r--vendor/spdx-rs/LICENSE.txt9
-rw-r--r--vendor/spdx-rs/LICENSES/CC0-1.0.txt121
-rw-r--r--vendor/spdx-rs/LICENSES/MIT.txt9
-rw-r--r--vendor/spdx-rs/README.md40
-rw-r--r--vendor/spdx-rs/src/error.rs33
-rw-r--r--vendor/spdx-rs/src/lib.rs17
-rw-r--r--vendor/spdx-rs/src/models/annotation.rs114
-rw-r--r--vendor/spdx-rs/src/models/checksum.rs46
-rw-r--r--vendor/spdx-rs/src/models/document_creation_information.rs295
-rw-r--r--vendor/spdx-rs/src/models/file_information.rs313
-rw-r--r--vendor/spdx-rs/src/models/mod.rs24
-rw-r--r--vendor/spdx-rs/src/models/other_licensing_information_detected.rs97
-rw-r--r--vendor/spdx-rs/src/models/package_information.rs555
-rw-r--r--vendor/spdx-rs/src/models/relationship.rs138
-rw-r--r--vendor/spdx-rs/src/models/snippet.rs237
-rw-r--r--vendor/spdx-rs/src/models/spdx_document.rs330
-rw-r--r--vendor/spdx-rs/src/parsers/mod.rs1100
-rw-r--r--vendor/spdx-rs/src/parsers/tag_value.rs669
21 files changed, 4276 insertions, 0 deletions
diff --git a/vendor/spdx-rs/.cargo-checksum.json b/vendor/spdx-rs/.cargo-checksum.json
new file mode 100644
index 000000000..a8e5213fc
--- /dev/null
+++ b/vendor/spdx-rs/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"CHANGELOG.md":"1bae675d114d04729ffbdf2a268d987dd07cdcf6a7df2ea5375079a666eca7a8","Cargo.toml":"f1238c1069f12770511a0a8e3d9c13aa6c65d76514414aa268df88d36e40efe8","LICENSE.txt":"b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0","LICENSES/CC0-1.0.txt":"a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499","LICENSES/MIT.txt":"b85dcd3e453d05982552c52b5fc9e0bdd6d23c6f8e844b984a88af32570b0cc0","README.md":"29147d67d15bd7abc2ed1e2b3fe6b82ce6477c9985234cb58d759c35f3ec252a","src/error.rs":"52d3645d8e9f6f330cd0de7262f332b99333b2d6095e4e53f0645d0de094095d","src/lib.rs":"8c4069be33582c176d96075a323985b9d368b2aee04a7d5e71dc97d9ad5d7807","src/models/annotation.rs":"f0986c54d02217b69453f594b9e923d6483aae697abadffdb6528378b01795c9","src/models/checksum.rs":"a5a2070b2293a81f7d102e3bbe2cd540a9feb0b22e4bd8620741359c1e4298f5","src/models/document_creation_information.rs":"90c86fefa95ed0626aaa6e7aa84ba3a03d225665da33c962c3dd0bfc32445883","src/models/file_information.rs":"5396f8e54b95b0d5d2847dd9b3291e1ebc6d098146d75dba1616a4ddb7fdadaf","src/models/mod.rs":"d7d5a669738c8a6d9131ac88512c53cc3215ed621771180340658391c1fe230e","src/models/other_licensing_information_detected.rs":"df0c984a9304f5ee1f4881ac44e0dde9ea0a2e8b12175457fd1970cc49227f02","src/models/package_information.rs":"2313113e35c3d130c89eada015189c7c3de32cc629c383d74acfba2dcabddda7","src/models/relationship.rs":"2ff0766d6aaefa40b082c9fb3be67a3ac7def3167244a1e0fa31faadac734d27","src/models/snippet.rs":"8bd1e67dd255e42a152602adccf69636c1a03d86509ceb1213d826ca1735ccdd","src/models/spdx_document.rs":"f5102b971e4689170e3d32559d13d3c94ec8bf8c16d4eb93914028f95497a223","src/parsers/mod.rs":"2539dafcb95b087806f5068a2f4914ca3915932fb98849d04ce1eabff0f6cd96","src/parsers/tag_value.rs":"ee6befa486a49344680cae689af4ef304e2661724d1f7df19dbeeede33ef065d"},"package":"b3c02f6eb7e7b4100c272f685a9ccaccaab302324e8c7ec3e2ee72340fb29ff3"} \ No newline at end of file
diff --git a/vendor/spdx-rs/CHANGELOG.md b/vendor/spdx-rs/CHANGELOG.md
new file mode 100644
index 000000000..556eaedec
--- /dev/null
+++ b/vendor/spdx-rs/CHANGELOG.md
@@ -0,0 +1,66 @@
+<!--
+SPDX-FileCopyrightText: 2021 HH Partners
+
+SPDX-License-Identifier: MIT
+ -->
+
+# Changelog
+
+## [0.5.1](https://github.com/doubleopen-project/spdx-rs/compare/v0.5.0...v0.5.1) (2022-07-05)
+
+
+### Miscellaneous Chores
+
+* release 0.5.1 ([3cda72b](https://github.com/doubleopen-project/spdx-rs/commit/3cda72b8264d3ffd6576f80febdc1d0845ab2650))
+
+## [0.5.0] - 2022-04-13
+
+### Changed
+
+- **Breaking:** Change field `license_information_in_file` of `FileInformation` to be a
+ `Vec<SimpleExpression>` instead of a `Vec<String>`.
+
+## [0.4.1] - 2022-04-12
+
+### Added
+
+- Implement `PartialEq` for SPDX.
+
+## [0.4.0] - 2022-04-12
+
+### Changed
+
+- **Breaking:** Change `SPDXExpression` to use the `spdx-expression` crate. Changes everything
+ around the expression handling.
+
+## [0.3.0] - 2021-10-21
+
+### Changed
+
+- **Breaking:** Refactor snippet byte range and line range.
+
+### Removed
+
+- **Breaking:** Moved the functionality for getting the SPDX License List to a utility crate. See
+ [spdx-toolkit](https://github.com/doubleopen-project/spdx-toolkit).
+- **Breaking:** Moved the functionality for for creating graphs from the relationships to a utility
+ crate. See [spdx-toolkit](https://github.com/doubleopen-project/spdx-toolkit).
+
+## [0.2.1] - 2021-10-14
+
+### Fixed
+
+- Accepts lowercase relationship types when parsing tag-value documents.
+
+## [0.2.0] - 2021-10-13
+
+### Changed
+
+- Started following semantic versioning and keeping a changelog.
+
+[0.5.0]: https://github.com/doubleopen-project/spdx-rs/compare/v0.4.1...v0.5.0
+[0.4.1]: https://github.com/doubleopen-project/spdx-rs/compare/v0.4.0...v0.4.1
+[0.4.0]: https://github.com/doubleopen-project/spdx-rs/compare/v0.3.0...v0.4.0
+[0.3.0]: https://github.com/doubleopen-project/spdx-rs/compare/v0.2.1...v0.3.0
+[0.2.1]: https://github.com/doubleopen-project/spdx-rs/compare/v0.2.0...v0.2.1
+[0.2.0]: https://github.com/doubleopen-project/spdx-rs/compare/v0.1.0...v0.2.0
diff --git a/vendor/spdx-rs/Cargo.toml b/vendor/spdx-rs/Cargo.toml
new file mode 100644
index 000000000..069cf2a1f
--- /dev/null
+++ b/vendor/spdx-rs/Cargo.toml
@@ -0,0 +1,62 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "spdx-rs"
+version = "0.5.1"
+authors = ["Mikko Murto <mikko.murto@hhpartners.fi>"]
+exclude = [
+ "/tests/",
+ "/.github/",
+ "/.gitignore",
+]
+description = "Parse and interact with SPDX Documents"
+homepage = "https://github.com/doubleopen-project/spdx-rs"
+documentation = "https://docs.rs/spdx-rs"
+readme = "README.md"
+keywords = ["SPDX"]
+categories = ["data-structures"]
+license = "MIT"
+repository = "https://github.com/doubleopen-project/spdx-rs"
+
+[dependencies.chrono]
+version = "0.4"
+features = ["serde"]
+
+[dependencies.log]
+version = "0.4"
+
+[dependencies.nom]
+version = "7"
+
+[dependencies.serde]
+version = "1"
+features = ["derive"]
+
+[dependencies.spdx-expression]
+version = "0.5.2"
+
+[dependencies.strum]
+version = "0.24"
+
+[dependencies.strum_macros]
+version = "0.24"
+
+[dependencies.thiserror]
+version = "1"
+
+[dependencies.uuid]
+version = "0.8"
+features = ["v4"]
+
+[dev-dependencies.serde_json]
+version = "1"
diff --git a/vendor/spdx-rs/LICENSE.txt b/vendor/spdx-rs/LICENSE.txt
new file mode 100644
index 000000000..2071b23b0
--- /dev/null
+++ b/vendor/spdx-rs/LICENSE.txt
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/spdx-rs/LICENSES/CC0-1.0.txt b/vendor/spdx-rs/LICENSES/CC0-1.0.txt
new file mode 100644
index 000000000..0e259d42c
--- /dev/null
+++ b/vendor/spdx-rs/LICENSES/CC0-1.0.txt
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/vendor/spdx-rs/LICENSES/MIT.txt b/vendor/spdx-rs/LICENSES/MIT.txt
new file mode 100644
index 000000000..2071b23b0
--- /dev/null
+++ b/vendor/spdx-rs/LICENSES/MIT.txt
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/spdx-rs/README.md b/vendor/spdx-rs/README.md
new file mode 100644
index 000000000..fe125e737
--- /dev/null
+++ b/vendor/spdx-rs/README.md
@@ -0,0 +1,40 @@
+<!--
+SPDX-FileCopyrightText: 2021 HH Partners
+
+SPDX-License-Identifier: MIT
+ -->
+
+# SPDX Documents in Rust
+
+[![crates.io-icon]][crates.io] [![docs.rs-icon]][docs.rs]
+
+[crates.io-icon]: https://img.shields.io/crates/v/spdx-rs
+[crates.io]: https://crates.io/crates/spdx-rs
+[docs.rs-icon]: https://img.shields.io/docsrs/spdx-rs
+[docs.rs]: https://docs.rs/spdx-rs
+
+`spdx-rs` parses [SPDX documents] in multiple data formats to Rust structs.
+
+## Data formats
+
+The library has been designed for working with SPDX documents in JSON. This is achieved with
+[Serde], so any data format supported by Serde should work, as long as the naming is consistent with
+that used in JSON SPDX documents.
+
+In addition to serializing and deserializing with Serde, deserializing documents in tag value format
+is supported with a custom parser.
+
+## Usage
+
+Simple usage examples for parsing documents from JSON and tag-value formats can be found in the
+[integration tests].
+
+[SPDX documents]: https://spdx.github.io/spdx-spec/
+[Serde]: https://serde.rs/
+[integration tests]: https://github.com/doubleopen-project/spdx-rs/tree/main/tests/integration.rs
+
+## License
+
+The project is licensed under the [MIT License][LICENSE.txt].
+
+[LICENSE.txt]: license.txt
diff --git a/vendor/spdx-rs/src/error.rs b/vendor/spdx-rs/src/error.rs
new file mode 100644
index 000000000..ae6c4b4e1
--- /dev/null
+++ b/vendor/spdx-rs/src/error.rs
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: 2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use std::io;
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum SpdxError {
+ #[error("Error parsing the SPDX Expression.")]
+ Parse {
+ #[from]
+ source: spdx_expression::SpdxExpressionError,
+ },
+
+ #[error("Path {0} doesn't have an extension.")]
+ PathExtension(String),
+
+ #[error("Error with file I/O.")]
+ Io {
+ #[from]
+ source: io::Error,
+ },
+
+ #[error("Error while parsing date.")]
+ DateTimeParse {
+ #[from]
+ source: chrono::ParseError,
+ },
+
+ #[error("Error parsing tag-value: {0}")]
+ TagValueParse(String),
+}
diff --git a/vendor/spdx-rs/src/lib.rs b/vendor/spdx-rs/src/lib.rs
new file mode 100644
index 000000000..906ff435b
--- /dev/null
+++ b/vendor/spdx-rs/src/lib.rs
@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: 2020 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+#![doc = include_str!("../README.md")]
+#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
+#![allow(
+ clippy::must_use_candidate,
+ clippy::module_name_repetitions,
+ clippy::non_ascii_literal,
+ clippy::missing_const_for_fn,
+ clippy::use_self
+)]
+
+pub mod error;
+pub mod models;
+pub mod parsers;
diff --git a/vendor/spdx-rs/src/models/annotation.rs b/vendor/spdx-rs/src/models/annotation.rs
new file mode 100644
index 000000000..f5396aa1d
--- /dev/null
+++ b/vendor/spdx-rs/src/models/annotation.rs
@@ -0,0 +1,114 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use chrono::{DateTime, Utc};
+use serde::{Deserialize, Serialize};
+
+/// <https://spdx.github.io/spdx-spec/8-annotations/>
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct Annotation {
+ /// <https://spdx.github.io/spdx-spec/8-annotations/#81-annotator>
+ pub annotator: String,
+
+ /// <https://spdx.github.io/spdx-spec/8-annotations/#82-annotation-date>
+ pub annotation_date: DateTime<Utc>,
+
+ /// <https://spdx.github.io/spdx-spec/8-annotations/#83-annotation-type>
+ pub annotation_type: AnnotationType,
+
+ /// <https://spdx.github.io/spdx-spec/8-annotations/#84-spdx-identifier-reference>
+ // TODO: According to the spec this is mandatory, but the example file doesn't
+ // have it.
+ pub spdx_identifier_reference: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/8-annotations/#85-annotation-comment>
+ #[serde(rename = "comment")]
+ pub annotation_comment: String,
+}
+
+impl Annotation {
+ pub fn new(
+ annotator: String,
+ annotation_date: DateTime<Utc>,
+ annotation_type: AnnotationType,
+ spdx_identifier_reference: Option<String>,
+ annotation_comment: String,
+ ) -> Self {
+ Self {
+ annotator,
+ annotation_date,
+ annotation_type,
+ spdx_identifier_reference,
+ annotation_comment,
+ }
+ }
+}
+
+/// <https://spdx.github.io/spdx-spec/8-annotations/#83-annotation-type>
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum AnnotationType {
+ Review,
+ Other,
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::read_to_string;
+
+ use chrono::TimeZone;
+
+ use crate::models::SPDX;
+
+ use super::*;
+
+ #[test]
+ fn annotator() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx_file.annotations[0].annotator,
+ "Person: Jane Doe ()".to_string()
+ );
+ }
+
+ #[test]
+ fn annotation_date() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx_file.annotations[0].annotation_date,
+ Utc.ymd(2010, 1, 29).and_hms(18, 30, 22)
+ );
+ }
+
+ #[test]
+ fn annotation_type() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx_file.annotations[0].annotation_type,
+ AnnotationType::Other
+ );
+ }
+
+ #[test]
+ fn annotation_comment() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx_file.annotations[0].annotation_comment,
+ "Document level annotation"
+ );
+ }
+}
diff --git a/vendor/spdx-rs/src/models/checksum.rs b/vendor/spdx-rs/src/models/checksum.rs
new file mode 100644
index 000000000..bd91f4c7a
--- /dev/null
+++ b/vendor/spdx-rs/src/models/checksum.rs
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use serde::{Deserialize, Serialize};
+
+/// Representation of SPDX's
+/// [Package Checksum](https://spdx.github.io/spdx-spec/3-package-information/#310-package-checksum)
+/// and
+/// [File Checksum](https://spdx.github.io/spdx-spec/4-file-information/#44-file-checksum).
+/// According to the spec, SHA1 is mandatory but we don't currently enforce that.
+#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, Clone)]
+pub struct Checksum {
+ /// Algorithm used to calculate the checksum
+ pub algorithm: Algorithm,
+
+ /// The checksum value.
+ #[serde(rename = "checksumValue")]
+ pub value: String,
+}
+
+impl Checksum {
+ /// Create new checksum.
+ pub fn new(algorithm: Algorithm, value: &str) -> Self {
+ Self {
+ algorithm,
+ value: value.to_lowercase(),
+ }
+ }
+}
+
+/// Possible algorithms to be used for SPDX's
+/// [package checksum](https://spdx.github.io/spdx-spec/3-package-information/#310-package-checksum)
+/// and [file checksum](https://spdx.github.io/spdx-spec/4-file-information/#44-file-checksum).
+#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, Clone, Copy)]
+pub enum Algorithm {
+ SHA1,
+ SHA224,
+ SHA256,
+ SHA384,
+ SHA512,
+ MD2,
+ MD4,
+ MD5,
+ MD6,
+}
diff --git a/vendor/spdx-rs/src/models/document_creation_information.rs b/vendor/spdx-rs/src/models/document_creation_information.rs
new file mode 100644
index 000000000..610803354
--- /dev/null
+++ b/vendor/spdx-rs/src/models/document_creation_information.rs
@@ -0,0 +1,295 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use chrono::{DateTime, Utc};
+use serde::{Deserialize, Serialize};
+
+use super::Checksum;
+
+/// ## Document Creation Information
+///
+/// SPDX's [Document Creation Information](https://spdx.github.io/spdx-spec/2-document-creation-information/)
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct DocumentCreationInformation {
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#21-spdx-version>
+ pub spdx_version: String,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#22-data-license>
+ pub data_license: String,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#23-spdx-identifier>
+ #[serde(rename = "SPDXID")]
+ pub spdx_identifier: String,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#24-document-name>
+ #[serde(rename = "name")]
+ pub document_name: String,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#25-spdx-document-namespace>
+ #[serde(rename = "documentNamespace")]
+ pub spdx_document_namespace: String,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#26-external-document-references>
+ #[serde(
+ rename = "externalDocumentRefs",
+ skip_serializing_if = "Vec::is_empty",
+ default
+ )]
+ pub external_document_references: Vec<ExternalDocumentReference>,
+
+ pub creation_info: CreationInfo,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#211-document-comment>
+ #[serde(rename = "comment", skip_serializing_if = "Option::is_none", default)]
+ pub document_comment: Option<String>,
+
+ /// Doesn't seem to be in spec, but the example contains it.
+ /// <https://github.com/spdx/spdx-spec/issues/395>
+ #[serde(skip_serializing_if = "Vec::is_empty", default)]
+ pub document_describes: Vec<String>,
+}
+
+impl Default for DocumentCreationInformation {
+ fn default() -> Self {
+ Self {
+ // Current version is 2.2. Might need to support more verisons
+ // in the future.
+ spdx_version: "SPDX-2.2".to_string(),
+ data_license: "CC0-1.0".to_string(),
+ spdx_identifier: "SPDXRef-DOCUMENT".to_string(),
+ document_name: "NOASSERTION".to_string(),
+ spdx_document_namespace: "NOASSERTION".to_string(),
+ external_document_references: Vec::new(),
+ document_comment: None,
+ creation_info: CreationInfo::default(),
+ document_describes: Vec::new(),
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct CreationInfo {
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#27-license-list-version>
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ pub license_list_version: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#28-creator>
+ pub creators: Vec<String>,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#29-created>
+ pub created: DateTime<Utc>,
+
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/#210-creator-comment>
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ #[serde(rename = "comment")]
+ pub creator_comment: Option<String>,
+}
+
+impl Default for CreationInfo {
+ fn default() -> Self {
+ Self {
+ license_list_version: None,
+ creators: vec![
+ "Person: Jane Doe ()".into(),
+ "Organization: ExampleCodeInspect ()".into(),
+ "Tool: LicenseFind-1.0".into(),
+ ],
+ created: chrono::offset::Utc::now(),
+ creator_comment: None,
+ }
+ }
+}
+
+/// <https://spdx.github.io/spdx-spec/2-document-creation-information/#26-external-document-references>
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
+pub struct ExternalDocumentReference {
+ /// Unique ID string of the reference.
+ #[serde(rename = "externalDocumentId")]
+ pub id_string: String,
+
+ /// Unique ID for the external document.
+ #[serde(rename = "spdxDocument")]
+ pub spdx_document_uri: String,
+
+ /// Checksum of the external document following the checksum format defined
+ /// in <https://spdx.github.io/spdx-spec/4-file-information/#44-file-checksum.>
+ pub checksum: Checksum,
+}
+
+impl ExternalDocumentReference {
+ pub const fn new(id_string: String, spdx_document_uri: String, checksum: Checksum) -> Self {
+ Self {
+ id_string,
+ spdx_document_uri,
+ checksum,
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::read_to_string;
+
+ use chrono::{TimeZone, Utc};
+
+ use super::*;
+ use crate::models::{Algorithm, SPDX};
+
+ #[test]
+ fn spdx_version() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+
+ assert_eq!(
+ spdx.document_creation_information.spdx_version,
+ "SPDX-2.2".to_string()
+ );
+ }
+ #[test]
+ fn data_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+
+ assert_eq!(spdx.document_creation_information.data_license, "CC0-1.0");
+ }
+ #[test]
+ fn spdx_identifier() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.document_creation_information.spdx_identifier,
+ "SPDXRef-DOCUMENT".to_string()
+ );
+ }
+ #[test]
+ fn document_name() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.document_creation_information.document_name,
+ "SPDX-Tools-v2.0".to_string()
+ );
+ }
+ #[test]
+ fn spdx_document_namespace() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.document_creation_information.spdx_document_namespace,
+ "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301"
+ .to_string()
+ );
+ }
+ #[test]
+ fn license_list_version() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.document_creation_information
+ .creation_info
+ .license_list_version,
+ Some("3.9".to_string())
+ );
+ }
+ #[test]
+ fn creators() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert!(spdx
+ .document_creation_information
+ .creation_info
+ .creators
+ .contains(&"Tool: LicenseFind-1.0".to_string()));
+ assert!(spdx
+ .document_creation_information
+ .creation_info
+ .creators
+ .contains(&"Organization: ExampleCodeInspect ()".to_string()));
+ assert!(spdx
+ .document_creation_information
+ .creation_info
+ .creators
+ .contains(&"Person: Jane Doe ()".to_string()));
+ }
+ #[test]
+ fn created() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.document_creation_information.creation_info.created,
+ Utc.ymd(2010, 1, 29).and_hms(18, 30, 22)
+ );
+ }
+ #[test]
+ fn creator_comment() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.document_creation_information
+ .creation_info
+ .creator_comment,
+ Some(
+ r#"This package has been shipped in source and binary form.
+The binaries were created with gcc 4.5.1 and expect to link to
+compatible system run time libraries."#
+ .to_string()
+ )
+ );
+ }
+ #[test]
+ fn document_comment() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.document_creation_information.document_comment,
+ Some(
+ "This document was created using SPDX 2.0 using licenses from the web site."
+ .to_string()
+ )
+ );
+ }
+
+ #[test]
+ fn external_document_references() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert!(spdx
+ .document_creation_information
+ .external_document_references
+ .contains(&ExternalDocumentReference {
+ id_string: "DocumentRef-spdx-tool-1.2".to_string(),
+ checksum: Checksum {
+ algorithm: Algorithm::SHA1,
+ value: "d6a770ba38583ed4bb4525bd96e50461655d2759".to_string()
+ },
+ spdx_document_uri:
+ "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"
+ .to_string()
+ }));
+ }
+}
diff --git a/vendor/spdx-rs/src/models/file_information.rs b/vendor/spdx-rs/src/models/file_information.rs
new file mode 100644
index 000000000..f2db6e030
--- /dev/null
+++ b/vendor/spdx-rs/src/models/file_information.rs
@@ -0,0 +1,313 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use serde::{Deserialize, Serialize};
+use spdx_expression::{SimpleExpression, SpdxExpression};
+
+use super::{Algorithm, Checksum};
+
+/// ## File Information
+///
+/// SPDX's [File Information](https://spdx.github.io/spdx-spec/4-file-information/)
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct FileInformation {
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#41-file-name>
+ pub file_name: String,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#42-file-spdx-identifier>
+ #[serde(rename = "SPDXID")]
+ pub file_spdx_identifier: String,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#43-file-type>
+ #[serde(rename = "fileTypes", skip_serializing_if = "Vec::is_empty", default)]
+ pub file_type: Vec<FileType>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#44-file-checksum>
+ #[serde(rename = "checksums")]
+ pub file_checksum: Vec<Checksum>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#45-concluded-license>
+ #[serde(rename = "licenseConcluded")]
+ pub concluded_license: SpdxExpression,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#46-license-information-in-file>
+ #[serde(rename = "licenseInfoInFiles")]
+ pub license_information_in_file: Vec<SimpleExpression>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#47-comments-on-license>
+ #[serde(
+ rename = "licenseComments",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub comments_on_license: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#48-copyright-text>
+ pub copyright_text: String,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#412-file-comment>
+ #[serde(rename = "comment", skip_serializing_if = "Option::is_none", default)]
+ pub file_comment: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#413-file-notice>
+ #[serde(
+ rename = "noticeText",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub file_notice: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#414-file-contributor>
+ #[serde(
+ rename = "fileContributors",
+ skip_serializing_if = "Vec::is_empty",
+ default
+ )]
+ pub file_contributor: Vec<String>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/#415-file-attribution-text>
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ pub file_attribution_text: Option<Vec<String>>,
+ // TODO: Snippet Information.
+}
+
+impl Default for FileInformation {
+ fn default() -> Self {
+ Self {
+ file_name: "NOASSERTION".to_string(),
+ file_spdx_identifier: "NOASSERTION".to_string(),
+ file_type: Vec::new(),
+ file_checksum: Vec::new(),
+ concluded_license: SpdxExpression::parse("NOASSERTION").expect("will always succeed"),
+ license_information_in_file: Vec::new(),
+ comments_on_license: None,
+ copyright_text: "NOASSERTION".to_string(),
+ file_comment: None,
+ file_notice: None,
+ file_contributor: Vec::new(),
+ file_attribution_text: None,
+ }
+ }
+}
+
+impl FileInformation {
+ /// Create new file.
+ pub fn new(name: &str, id: &mut i32) -> Self {
+ *id += 1;
+ Self {
+ file_name: name.to_string(),
+ file_spdx_identifier: format!("SPDXRef-{}", id),
+ ..Self::default()
+ }
+ }
+
+ /// Check if hash equals.
+ pub fn equal_by_hash(&self, algorithm: Algorithm, value: &str) -> bool {
+ let checksum = self
+ .file_checksum
+ .iter()
+ .find(|&checksum| checksum.algorithm == algorithm);
+
+ checksum.map_or(false, |checksum| {
+ checksum.value.to_ascii_lowercase() == value.to_ascii_lowercase()
+ })
+ }
+
+ /// Get checksum
+ pub fn checksum(&self, algorithm: Algorithm) -> Option<&str> {
+ let checksum = self
+ .file_checksum
+ .iter()
+ .find(|&checksum| checksum.algorithm == algorithm);
+
+ checksum.map(|checksum| checksum.value.as_str())
+ }
+}
+
+/// <https://spdx.github.io/spdx-spec/4-file-information/#43-file-type>
+#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, Clone, Copy)]
+#[serde(rename_all = "UPPERCASE")]
+pub enum FileType {
+ Source,
+ Binary,
+ Archive,
+ Application,
+ Audio,
+ Image,
+ Text,
+ Video,
+ Documentation,
+ SPDX,
+ Other,
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::read_to_string;
+
+ use super::*;
+ use crate::models::{Checksum, FileType, SPDX};
+
+ #[test]
+ fn checksum_equality() {
+ let mut id = 1;
+ let mut file_sha256 = FileInformation::new("sha256", &mut id);
+ file_sha256
+ .file_checksum
+ .push(Checksum::new(Algorithm::SHA256, "test"));
+
+ assert!(file_sha256.equal_by_hash(Algorithm::SHA256, "test"));
+ assert!(!file_sha256.equal_by_hash(Algorithm::SHA256, "no_test"));
+
+ let mut file_md5 = FileInformation::new("md5", &mut id);
+ file_md5
+ .file_checksum
+ .push(Checksum::new(Algorithm::MD5, "test"));
+ assert!(file_md5.equal_by_hash(Algorithm::MD5, "test"));
+ assert!(!file_md5.equal_by_hash(Algorithm::MD5, "no_test"));
+ assert!(!file_md5.equal_by_hash(Algorithm::SHA1, "test"));
+ }
+
+ #[test]
+ fn get_checksum() {
+ let mut id = 1;
+ let mut file_sha256 = FileInformation::new("sha256", &mut id);
+ file_sha256
+ .file_checksum
+ .push(Checksum::new(Algorithm::SHA256, "test"));
+
+ assert_eq!(file_sha256.checksum(Algorithm::SHA256), Some("test"));
+ assert_eq!(file_sha256.checksum(Algorithm::MD2), None);
+
+ let mut file_md5 = FileInformation::new("md5", &mut id);
+ file_md5
+ .file_checksum
+ .push(Checksum::new(Algorithm::MD5, "test"));
+
+ assert_eq!(file_md5.checksum(Algorithm::MD5), Some("test"));
+ }
+
+ #[test]
+ fn file_name() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[0].file_name,
+ "./src/org/spdx/parser/DOAPProject.java"
+ );
+ }
+ #[test]
+ fn file_spdx_identifier() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[0].file_spdx_identifier,
+ "SPDXRef-DoapSource"
+ );
+ }
+ #[test]
+ fn file_type() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(spdx.file_information[0].file_type, vec![FileType::Source]);
+ }
+ #[test]
+ fn file_checksum() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[0].file_checksum,
+ vec![Checksum {
+ algorithm: Algorithm::SHA1,
+ value: "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12".to_string()
+ }]
+ );
+ }
+ #[test]
+ fn concluded_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[0].concluded_license,
+ SpdxExpression::parse("Apache-2.0").unwrap()
+ );
+ }
+ #[test]
+ fn license_information_in_file() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[0].license_information_in_file,
+ vec![SimpleExpression::parse("Apache-2.0").unwrap()]
+ );
+ }
+ #[test]
+ fn comments_on_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[2].comments_on_license,
+ Some("This license is used by Jena".to_string())
+ );
+ }
+ #[test]
+ fn copyright_text() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[0].copyright_text,
+ "Copyright 2010, 2011 Source Auditor Inc.".to_string()
+ );
+ }
+ #[test]
+ fn file_comment() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[1].file_comment,
+ Some("This file is used by Jena".to_string())
+ );
+ }
+ #[test]
+ fn file_notice() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[1].file_notice,
+ Some("Apache Commons Lang\nCopyright 2001-2011 The Apache Software Foundation\n\nThis product includes software developed by\nThe Apache Software Foundation (http://www.apache.org/).\n\nThis product includes software from the Spring Framework,\nunder the Apache License 2.0 (see: StringUtils.containsWhitespace())".to_string())
+ );
+ }
+ #[test]
+ fn file_contributor() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.file_information[1].file_contributor,
+ vec!["Apache Software Foundation".to_string()]
+ );
+ }
+}
diff --git a/vendor/spdx-rs/src/models/mod.rs b/vendor/spdx-rs/src/models/mod.rs
new file mode 100644
index 000000000..2eed75db2
--- /dev/null
+++ b/vendor/spdx-rs/src/models/mod.rs
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+mod annotation;
+mod checksum;
+mod document_creation_information;
+mod file_information;
+mod other_licensing_information_detected;
+mod package_information;
+mod relationship;
+mod snippet;
+mod spdx_document;
+
+pub use annotation::*;
+pub use checksum::*;
+pub use document_creation_information::*;
+pub use file_information::*;
+pub use other_licensing_information_detected::*;
+pub use package_information::*;
+pub use relationship::*;
+pub use snippet::*;
+pub use spdx_document::*;
+pub use spdx_expression::*;
diff --git a/vendor/spdx-rs/src/models/other_licensing_information_detected.rs b/vendor/spdx-rs/src/models/other_licensing_information_detected.rs
new file mode 100644
index 000000000..30bd1d318
--- /dev/null
+++ b/vendor/spdx-rs/src/models/other_licensing_information_detected.rs
@@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use serde::{Deserialize, Serialize};
+
+/// <https://spdx.github.io/spdx-spec/6-other-licensing-information-detected/>
+#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub struct OtherLicensingInformationDetected {
+ /// <https://spdx.github.io/spdx-spec/6-other-licensing-information-detected/#61-license-identifier>
+ #[serde(rename = "licenseId")]
+ pub license_identifier: String,
+
+ /// <https://spdx.github.io/spdx-spec/6-other-licensing-information-detected/#62-extracted-text>
+ pub extracted_text: String,
+
+ /// <https://spdx.github.io/spdx-spec/6-other-licensing-information-detected/#63-license-name>
+ #[serde(rename = "name")]
+ #[serde(default = "default_noassertion")]
+ pub license_name: String,
+
+ /// <https://spdx.github.io/spdx-spec/6-other-licensing-information-detected/#64-license-cross-reference>
+ #[serde(rename = "seeAlsos", skip_serializing_if = "Vec::is_empty", default)]
+ pub license_cross_reference: Vec<String>,
+
+ /// <https://spdx.github.io/spdx-spec/6-other-licensing-information-detected/#65-license-comment>
+ #[serde(rename = "comment", skip_serializing_if = "Option::is_none", default)]
+ pub license_comment: Option<String>,
+}
+
+fn default_noassertion() -> String {
+ "NOASSERTION".into()
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::read_to_string;
+
+ use crate::models::SPDX;
+
+ #[test]
+ fn license_identifier() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.other_licensing_information_detected[0].license_identifier,
+ "LicenseRef-Beerware-4.2".to_string()
+ );
+ }
+ #[test]
+ fn extracted_text() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(spdx.other_licensing_information_detected[0].extracted_text, "\"THE BEER-WARE LICENSE\" (Revision 42):\nphk@FreeBSD.ORG wrote this file. As long as you retain this notice you\ncan do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp </\nLicenseName: Beer-Ware License (Version 42)\nLicenseCrossReference: http://people.freebsd.org/~phk/\nLicenseComment: \nThe beerware license has a couple of other standard variants.");
+ }
+ #[test]
+ fn license_name() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.other_licensing_information_detected[2].license_name,
+ "CyberNeko License".to_string()
+ );
+ }
+ #[test]
+ fn license_cross_reference() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.other_licensing_information_detected[2].license_cross_reference,
+ vec![
+ "http://people.apache.org/~andyc/neko/LICENSE".to_string(),
+ "http://justasample.url.com".to_string()
+ ]
+ );
+ }
+ #[test]
+ fn license_comment() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.other_licensing_information_detected[2].license_comment,
+ Some("This is tye CyperNeko License".to_string())
+ );
+ }
+}
diff --git a/vendor/spdx-rs/src/models/package_information.rs b/vendor/spdx-rs/src/models/package_information.rs
new file mode 100644
index 000000000..1cfd938f8
--- /dev/null
+++ b/vendor/spdx-rs/src/models/package_information.rs
@@ -0,0 +1,555 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use serde::{Deserialize, Serialize};
+use spdx_expression::SpdxExpression;
+
+use super::Annotation;
+
+use super::{Checksum, FileInformation};
+
+/// ## Package Information
+///
+/// SPDX's [Package Information](https://spdx.github.io/spdx-spec/3-package-information/).
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct PackageInformation {
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#31-package-name>
+ #[serde(rename = "name")]
+ pub package_name: String,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#32-package-spdx-identifier>
+ #[serde(rename = "SPDXID")]
+ pub package_spdx_identifier: String,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#33-package-version>
+ #[serde(
+ rename = "versionInfo",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub package_version: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#34-package-file-name>
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ pub package_file_name: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#35-package-supplier>
+ #[serde(rename = "supplier", skip_serializing_if = "Option::is_none", default)]
+ pub package_supplier: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#36-package-originator>
+ #[serde(
+ rename = "originator",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub package_originator: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#37-package-download-location>
+ #[serde(rename = "downloadLocation")]
+ pub package_download_location: String,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#38-files-analyzed>
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ pub files_analyzed: Option<bool>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#39-package-verification-code>
+ #[serde(skip_serializing_if = "Option::is_none", default)]
+ pub package_verification_code: Option<PackageVerificationCode>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#310-package-checksum>
+ #[serde(rename = "checksums", skip_serializing_if = "Vec::is_empty", default)]
+ pub package_checksum: Vec<Checksum>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#311-package-home-page>
+ #[serde(rename = "homepage", skip_serializing_if = "Option::is_none", default)]
+ pub package_home_page: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#312-source-information>
+ #[serde(
+ rename = "sourceInfo",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub source_information: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#313-concluded-license>
+ #[serde(rename = "licenseConcluded")]
+ pub concluded_license: SpdxExpression,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#314-all-licenses-information-from-files>
+ #[serde(
+ rename = "licenseInfoFromFiles",
+ skip_serializing_if = "Vec::is_empty",
+ default
+ )]
+ pub all_licenses_information_from_files: Vec<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#315-declared-license>
+ #[serde(rename = "licenseDeclared")]
+ pub declared_license: SpdxExpression,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#316-comments-on-license>
+ #[serde(
+ rename = "licenseComments",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub comments_on_license: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#317-copyright-text>
+ pub copyright_text: String,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#318-package-summary-description>
+ #[serde(rename = "summary", skip_serializing_if = "Option::is_none", default)]
+ pub package_summary_description: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#319-package-detailed-description>
+ #[serde(
+ rename = "description",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub package_detailed_description: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#320-package-comment>
+ #[serde(rename = "comment", skip_serializing_if = "Option::is_none", default)]
+ pub package_comment: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#321-external-reference>
+ #[serde(
+ rename = "externalRefs",
+ skip_serializing_if = "Vec::is_empty",
+ default
+ )]
+ pub external_reference: Vec<ExternalPackageReference>,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/#323-package-attribution-text>
+ #[serde(
+ rename = "attributionTexts",
+ skip_serializing_if = "Vec::is_empty",
+ default
+ )]
+ pub package_attribution_text: Vec<String>,
+
+ /// List of "files in the package". Not sure which relationship type this maps to.
+ /// Info: <https://github.com/spdx/spdx-spec/issues/487>
+ // Valid SPDX?
+ #[serde(rename = "hasFiles", skip_serializing_if = "Vec::is_empty", default)]
+ pub files: Vec<String>,
+
+ #[serde(skip_serializing_if = "Vec::is_empty", default)]
+ pub annotations: Vec<Annotation>,
+}
+
+impl Default for PackageInformation {
+ fn default() -> Self {
+ Self {
+ package_name: "NOASSERTION".to_string(),
+ package_spdx_identifier: "NOASSERTION".to_string(),
+ package_version: None,
+ package_file_name: None,
+ package_supplier: None,
+ package_originator: None,
+ package_download_location: "NOASSERTION".to_string(),
+ files_analyzed: None,
+ package_verification_code: None,
+ package_checksum: Vec::new(),
+ package_home_page: None,
+ source_information: None,
+ concluded_license: SpdxExpression::parse("NONE").expect("will always succeed"),
+ all_licenses_information_from_files: Vec::new(),
+ declared_license: SpdxExpression::parse("NONE").expect("will always succeed"),
+ comments_on_license: None,
+ copyright_text: "NOASSERTION".to_string(),
+ package_summary_description: None,
+ package_detailed_description: None,
+ package_comment: None,
+ external_reference: Vec::new(),
+ package_attribution_text: Vec::new(),
+ files: Vec::new(),
+ annotations: Vec::new(),
+ }
+ }
+}
+
+impl PackageInformation {
+ /// Create new package.
+ pub fn new(name: &str, id: &mut i32) -> Self {
+ *id += 1;
+ Self {
+ package_name: name.to_string(),
+ package_spdx_identifier: format!("SPDXRef-{}", id),
+ ..Self::default()
+ }
+ }
+
+ /// Find all files of the package.
+ pub fn find_files_for_package<'a>(
+ &'a self,
+ files: &'a [FileInformation],
+ ) -> Vec<&'a FileInformation> {
+ self.files
+ .iter()
+ .filter_map(|file| {
+ files
+ .iter()
+ .find(|file_information| &file_information.file_spdx_identifier == file)
+ })
+ .collect()
+ }
+}
+
+/// <https://spdx.github.io/spdx-spec/3-package-information/#39-package-verification-code>
+#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, Clone)]
+pub struct PackageVerificationCode {
+ /// Value of the verification code.
+ #[serde(rename = "packageVerificationCodeValue")]
+ pub value: String,
+
+ /// Files that were excluded when calculating the verification code.
+ #[serde(
+ rename = "packageVerificationCodeExcludedFiles",
+ skip_serializing_if = "Vec::is_empty",
+ default
+ )]
+ pub excludes: Vec<String>,
+}
+
+impl PackageVerificationCode {
+ pub fn new(value: String, excludes: Vec<String>) -> Self {
+ Self { value, excludes }
+ }
+}
+
+/// <https://spdx.github.io/spdx-spec/3-package-information/#321-external-reference>
+#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct ExternalPackageReference {
+ pub reference_category: ExternalPackageReferenceCategory,
+ pub reference_type: String,
+ pub reference_locator: String,
+ #[serde(rename = "comment")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(default)]
+ pub reference_comment: Option<String>,
+}
+
+impl ExternalPackageReference {
+ pub const fn new(
+ reference_category: ExternalPackageReferenceCategory,
+ reference_type: String,
+ reference_locator: String,
+ reference_comment: Option<String>,
+ ) -> Self {
+ Self {
+ reference_category,
+ reference_type,
+ reference_locator,
+ reference_comment,
+ }
+ }
+}
+
+/// <https://spdx.github.io/spdx-spec/3-package-information/#321-external-reference>
+#[derive(Serialize, Deserialize, Debug, PartialEq, PartialOrd, Clone)]
+#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
+pub enum ExternalPackageReferenceCategory {
+ Security,
+ PackageManager,
+ PersistentID,
+ Other,
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::read_to_string;
+
+ use crate::models::{Algorithm, SPDX};
+
+ use super::*;
+
+ #[test]
+ fn all_packages_are_deserialized() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(spdx.package_information.len(), 4);
+ }
+ #[test]
+ fn package_name() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_name,
+ "glibc".to_string()
+ );
+ }
+ #[test]
+ fn package_spdx_identifier() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_spdx_identifier,
+ "SPDXRef-Package".to_string()
+ );
+ }
+ #[test]
+ fn package_version() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_version,
+ Some("2.11.1".to_string())
+ );
+ }
+ #[test]
+ fn package_file_name() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_file_name,
+ Some("glibc-2.11.1.tar.gz".to_string())
+ );
+ }
+ #[test]
+ fn package_supplier() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_supplier,
+ Some("Person: Jane Doe (jane.doe@example.com)".to_string())
+ );
+ }
+ #[test]
+ fn package_originator() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_originator,
+ Some("Organization: ExampleCodeInspect (contact@example.com)".to_string())
+ );
+ }
+ #[test]
+ fn package_download_location() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_download_location,
+ "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz".to_string()
+ );
+ }
+ #[test]
+ fn files_analyzed() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(spdx.package_information[0].files_analyzed, Some(true));
+ }
+ #[test]
+ fn package_verification_code() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_verification_code,
+ Some(PackageVerificationCode {
+ value: "d6a770ba38583ed4bb4525bd96e50461655d2758".to_string(),
+ excludes: vec!["./package.spdx".to_string()]
+ })
+ );
+ }
+ #[test]
+ fn package_chekcsum() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert!(spdx.package_information[0]
+ .package_checksum
+ .contains(&Checksum::new(
+ Algorithm::SHA1,
+ "85ed0817af83a24ad8da68c2b5094de69833983c"
+ )));
+ assert!(spdx.package_information[0]
+ .package_checksum
+ .contains(&Checksum::new(
+ Algorithm::MD5,
+ "624c1abb3664f4b35547e7c73864ad24"
+ )));
+ assert!(spdx.package_information[0]
+ .package_checksum
+ .contains(&Checksum::new(
+ Algorithm::SHA256,
+ "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"
+ )));
+ }
+ #[test]
+ fn package_home_page() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_home_page,
+ Some("http://ftp.gnu.org/gnu/glibc".to_string())
+ );
+ }
+ #[test]
+ fn source_information() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].source_information,
+ Some("uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.".to_string())
+ );
+ }
+ #[test]
+ fn concluded_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].concluded_license,
+ SpdxExpression::parse("(LGPL-2.0-only OR LicenseRef-3)").unwrap()
+ );
+ }
+ #[test]
+ fn all_licenses_information_from_files() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert!(spdx.package_information[0]
+ .all_licenses_information_from_files
+ .contains(&"GPL-2.0-only".to_string()));
+ assert!(spdx.package_information[0]
+ .all_licenses_information_from_files
+ .contains(&"LicenseRef-2".to_string()));
+ assert!(spdx.package_information[0]
+ .all_licenses_information_from_files
+ .contains(&"LicenseRef-1".to_string()));
+ }
+ #[test]
+ fn declared_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].declared_license,
+ SpdxExpression::parse("(LGPL-2.0-only AND LicenseRef-3)").unwrap()
+ );
+ }
+ #[test]
+ fn comments_on_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].comments_on_license,
+ Some("The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change.".to_string())
+ );
+ }
+ #[test]
+ fn copyright_text() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].copyright_text,
+ "Copyright 2008-2010 John Smith".to_string()
+ );
+ }
+ #[test]
+ fn package_summary_description() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_summary_description,
+ Some("GNU C library.".to_string())
+ );
+ }
+ #[test]
+ fn package_detailed_description() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[0].package_detailed_description,
+ Some("The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems.".to_string())
+ );
+ }
+ #[test]
+ fn package_comment() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.package_information[1].package_comment,
+ Some("This package was converted from a DOAP Project by the same name".to_string())
+ );
+ }
+ #[test]
+ fn external_reference() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert!(
+ spdx.package_information[0].external_reference.contains(&ExternalPackageReference {
+ reference_comment: Some("This is the external ref for Acme".to_string()),
+ reference_category: ExternalPackageReferenceCategory::Other,
+ reference_locator: "acmecorp/acmenator/4.1.3-alpha".to_string(),
+ reference_type: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301#LocationRef-acmeforge".to_string()
+ })
+ );
+ assert!(spdx.package_information[0].external_reference.contains(
+ &ExternalPackageReference {
+ reference_comment: None,
+ reference_category: ExternalPackageReferenceCategory::Security,
+ reference_locator:
+ "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*".to_string(),
+ reference_type: "http://spdx.org/rdf/references/cpe23Type".to_string()
+ }
+ ));
+ }
+ #[test]
+ fn package_attribution_text() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert!(
+ spdx.package_information[0].package_attribution_text.contains(&"The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually.".to_string())
+ );
+ }
+}
diff --git a/vendor/spdx-rs/src/models/relationship.rs b/vendor/spdx-rs/src/models/relationship.rs
new file mode 100644
index 000000000..6430a796f
--- /dev/null
+++ b/vendor/spdx-rs/src/models/relationship.rs
@@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use serde::{Deserialize, Serialize};
+use strum_macros::AsRefStr;
+
+/// <https://spdx.github.io/spdx-spec/7-relationships-between-SPDX-elements/#71-relationship>
+#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq, Hash)]
+#[serde(rename_all = "camelCase")]
+pub struct Relationship {
+ /// SPDX ID of the element.
+ pub spdx_element_id: String,
+
+ /// SPDX ID of the related element.
+ pub related_spdx_element: String,
+
+ /// Type of the relationship.
+ pub relationship_type: RelationshipType,
+
+ /// <https://spdx.github.io/spdx-spec/7-relationships-between-SPDX-elements/#72-relationship-comment>
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(default)]
+ pub comment: Option<String>,
+}
+
+impl Relationship {
+ /// Create a new relationship.
+ pub fn new(
+ spdx_element_id: &str,
+ related_spdx_element: &str,
+ relationship_type: RelationshipType,
+ comment: Option<String>,
+ ) -> Self {
+ Self {
+ spdx_element_id: spdx_element_id.to_string(),
+ related_spdx_element: related_spdx_element.to_string(),
+ relationship_type,
+ comment,
+ }
+ }
+}
+
+/// <https://spdx.github.io/spdx-spec/7-relationships-between-SPDX-elements/#71-relationship>
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, AsRefStr, Eq, Hash)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum RelationshipType {
+ Describes,
+ DescribedBy,
+ Contains,
+ ContainedBy,
+ DependsOn,
+ DependencyOf,
+ DependencyManifestOf,
+ BuildDependencyOf,
+ DevDependencyOf,
+ OptionalDependencyOf,
+ ProvidedDependencyOf,
+ TestDependencyOf,
+ RuntimeDependencyOf,
+ ExampleOf,
+ Generates,
+ GeneratedFrom,
+ AncestorOf,
+ DescendantOf,
+ VariantOf,
+ DistributionArtifact,
+ PatchFor,
+ PatchApplied,
+ CopyOf,
+ FileAdded,
+ FileDeleted,
+ FileModified,
+ ExpandedFromArchive,
+ DynamicLink,
+ StaticLink,
+ DataFileOf,
+ TestCaseOf,
+ BuildToolOf,
+ DevToolOf,
+ TestOf,
+ TestToolOf,
+ DocumentationOf,
+ OptionalComponentOf,
+ MetafileOf,
+ PackageOf,
+ Amends,
+ PrerequisiteFor,
+ HasPrerequisite,
+ Other,
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::read_to_string;
+
+ use crate::models::SPDX;
+
+ use super::*;
+
+ #[test]
+ fn spdx_element_id() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.relationships[0].spdx_element_id,
+ "SPDXRef-DOCUMENT".to_string()
+ );
+ }
+ #[test]
+ fn related_spdx_element() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.relationships[0].related_spdx_element,
+ "SPDXRef-Package".to_string()
+ );
+ }
+ #[test]
+ fn relationship_type() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.relationships[0].relationship_type,
+ RelationshipType::Contains
+ );
+ assert_eq!(
+ spdx.relationships[2].relationship_type,
+ RelationshipType::CopyOf
+ );
+ }
+}
diff --git a/vendor/spdx-rs/src/models/snippet.rs b/vendor/spdx-rs/src/models/snippet.rs
new file mode 100644
index 000000000..e4a644ac5
--- /dev/null
+++ b/vendor/spdx-rs/src/models/snippet.rs
@@ -0,0 +1,237 @@
+// SPDX-FileCopyrightText: 2020-2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use serde::{Deserialize, Serialize};
+use spdx_expression::SpdxExpression;
+
+/// <https://spdx.github.io/spdx-spec/5-snippet-information/>
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)]
+pub struct Snippet {
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#51-snippet-spdx-identifier>
+ #[serde(rename = "SPDXID")]
+ pub snippet_spdx_identifier: String,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#52-snippet-from-file-spdx-identifier>
+ #[serde(rename = "snippetFromFile")]
+ pub snippet_from_file_spdx_identifier: String,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#53-snippet-byte-range>
+ pub ranges: Vec<Range>,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#55-snippet-concluded-license>
+ #[serde(rename = "licenseConcluded")]
+ pub snippet_concluded_license: SpdxExpression,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#56-license-information-in-snippet>
+ #[serde(
+ rename = "licenseInfoInSnippets",
+ skip_serializing_if = "Vec::is_empty",
+ default
+ )]
+ pub license_information_in_snippet: Vec<String>,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#57-snippet-comments-on-license>
+ #[serde(
+ rename = "licenseComments",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub snippet_comments_on_license: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#58-snippet-copyright-text>
+ #[serde(rename = "copyrightText")]
+ pub snippet_copyright_text: String,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#59-snippet-comment>
+ #[serde(rename = "comment", skip_serializing_if = "Option::is_none", default)]
+ pub snippet_comment: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#510-snippet-name>
+ #[serde(rename = "name", skip_serializing_if = "Option::is_none", default)]
+ pub snippet_name: Option<String>,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/#511-snippet-attribution-text>
+ #[serde(
+ rename = "attributionText",
+ skip_serializing_if = "Option::is_none",
+ default
+ )]
+ pub snippet_attribution_text: Option<String>,
+}
+
+/// <https://spdx.github.io/spdx-spec/5-snippet-information/#53-snippet-byte-range>
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct Range {
+ pub start_pointer: Pointer,
+ pub end_pointer: Pointer,
+}
+
+impl Range {
+ pub fn new(start_pointer: Pointer, end_pointer: Pointer) -> Self {
+ Self {
+ start_pointer,
+ end_pointer,
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
+#[serde(untagged)]
+pub enum Pointer {
+ Byte {
+ reference: Option<String>,
+ offset: i32,
+ },
+ Line {
+ reference: Option<String>,
+ #[serde(rename = "lineNumber")]
+ line_number: i32,
+ },
+}
+
+impl Pointer {
+ /// Create a new [`Pointer::Byte`].
+ pub fn new_byte(reference: Option<String>, offset: i32) -> Self {
+ Self::Byte { reference, offset }
+ }
+
+ /// Create a new [`Pointer::Line`].
+ pub fn new_line(reference: Option<String>, line_number: i32) -> Self {
+ Self::Line {
+ reference,
+ line_number,
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::read_to_string;
+
+ use crate::models::SPDX;
+
+ use super::*;
+
+ #[test]
+ fn snippet_spdx_identifier() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].snippet_spdx_identifier,
+ "SPDXRef-Snippet".to_string()
+ );
+ }
+ #[test]
+ fn snippet_from_file_spdx_identifier() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].snippet_from_file_spdx_identifier,
+ "SPDXRef-DoapSource".to_string()
+ );
+ }
+ #[test]
+ fn ranges() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].ranges,
+ vec![
+ Range {
+ end_pointer: Pointer::Line {
+ line_number: 23,
+ reference: Some("SPDXRef-DoapSource".to_string()),
+ },
+ start_pointer: Pointer::Line {
+ line_number: 5,
+ reference: Some("SPDXRef-DoapSource".to_string()),
+ },
+ },
+ Range {
+ end_pointer: Pointer::Byte {
+ offset: 420,
+ reference: Some("SPDXRef-DoapSource".to_string()),
+ },
+ start_pointer: Pointer::Byte {
+ offset: 310,
+ reference: Some("SPDXRef-DoapSource".to_string()),
+ },
+ },
+ ]
+ );
+ }
+ #[test]
+ fn snippet_concluded_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].snippet_concluded_license,
+ SpdxExpression::parse("GPL-2.0-only").unwrap()
+ );
+ }
+ #[test]
+ fn license_information_in_snippet() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].license_information_in_snippet,
+ vec!["GPL-2.0-only".to_string()]
+ );
+ }
+ #[test]
+ fn snippet_comments_on_license() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].snippet_comments_on_license,
+ 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())
+ );
+ }
+ #[test]
+ fn snippet_copyright_text() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].snippet_copyright_text,
+ "Copyright 2008-2010 John Smith".to_string()
+ );
+ }
+ #[test]
+ fn snippet_comment() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].snippet_comment,
+ 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())
+ );
+ }
+ #[test]
+ fn snippet_name() {
+ let spdx: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ assert_eq!(
+ spdx.snippet_information[0].snippet_name,
+ Some("from linux kernel".to_string())
+ );
+ }
+}
diff --git a/vendor/spdx-rs/src/models/spdx_document.rs b/vendor/spdx-rs/src/models/spdx_document.rs
new file mode 100644
index 000000000..d7db1009a
--- /dev/null
+++ b/vendor/spdx-rs/src/models/spdx_document.rs
@@ -0,0 +1,330 @@
+// SPDX-FileCopyrightText: 2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use std::collections::HashSet;
+
+use log::info;
+use serde::{Deserialize, Serialize};
+use uuid::Uuid;
+
+use super::{
+ Algorithm, Annotation, DocumentCreationInformation, FileInformation,
+ OtherLicensingInformationDetected, PackageInformation, Relationship, Snippet,
+};
+
+/// A representation of an [SPDX Document]
+///
+/// This is the main struct of this crate. The struct implements [`Serialize`] and [`Deserialize`]
+/// to allow it to be serialized into and deserialized from any data format supported by [Serde].
+///
+/// # SPDX specification version
+///
+/// The crate has been developed around SPDX version 2.2.1. Fields deprecated in 2.2.1, like
+/// [review information] are not supported. The plan is to support newer versions as they are
+/// released.
+///
+/// # Data formats
+///
+/// The crate has been developed for usage with JSON SPDX documents. The naming of the fields should
+/// conform to the spec for at least JSON. Other formats, like YAML may work, but no guarantees are
+/// made.
+///
+/// The crate also allows for deserializing the struct from SPDX documents in [tag-value format]
+/// with [`crate::parsers::spdx_from_tag_value`].
+///
+/// [SPDX Document]: https://spdx.github.io/spdx-spec/composition-of-an-SPDX-document/
+/// [Serde]: https://serde.rs
+/// [review information]: https://spdx.github.io/spdx-spec/review-information-deprecated/
+/// [tag-value format]: https://spdx.github.io/spdx-spec/conformance/
+#[derive(Serialize, Deserialize, Debug, PartialEq)]
+#[serde(rename_all = "camelCase", deny_unknown_fields)]
+pub struct SPDX {
+ /// <https://spdx.github.io/spdx-spec/2-document-creation-information/>
+ #[serde(flatten)]
+ pub document_creation_information: DocumentCreationInformation,
+
+ /// <https://spdx.github.io/spdx-spec/3-package-information/>
+ #[serde(rename = "packages")]
+ #[serde(default)]
+ pub package_information: Vec<PackageInformation>,
+
+ /// <https://spdx.github.io/spdx-spec/6-other-licensing-information-detected/>
+ #[serde(rename = "hasExtractedLicensingInfos")]
+ #[serde(default)]
+ pub other_licensing_information_detected: Vec<OtherLicensingInformationDetected>,
+
+ /// <https://spdx.github.io/spdx-spec/4-file-information/>
+ #[serde(rename = "files")]
+ #[serde(default)]
+ pub file_information: Vec<FileInformation>,
+
+ /// <https://spdx.github.io/spdx-spec/5-snippet-information/>
+ #[serde(rename = "snippets")]
+ #[serde(default)]
+ pub snippet_information: Vec<Snippet>,
+
+ /// <https://spdx.github.io/spdx-spec/7-relationships-between-SPDX-elements/>
+ #[serde(default)]
+ pub relationships: Vec<Relationship>,
+
+ /// <https://spdx.github.io/spdx-spec/8-annotations/>
+ #[serde(default)]
+ pub annotations: Vec<Annotation>,
+
+ /// Counter for creating SPDXRefs. Is not part of the spec, so don't serialize.
+ #[serde(skip)]
+ pub spdx_ref_counter: i32,
+}
+
+impl SPDX {
+ /// Create new SPDX struct.
+ pub fn new(name: &str) -> Self {
+ info!("Creating SPDX.");
+
+ Self {
+ document_creation_information: DocumentCreationInformation {
+ document_name: name.to_string(),
+ spdx_document_namespace: format!(
+ "http://spdx.org/spdxdocs/{}-{}",
+ name,
+ Uuid::new_v4()
+ ),
+ ..DocumentCreationInformation::default()
+ },
+ package_information: Vec::new(),
+ other_licensing_information_detected: Vec::new(),
+ file_information: Vec::new(),
+ relationships: Vec::new(),
+ spdx_ref_counter: 0,
+ annotations: Vec::new(),
+ snippet_information: Vec::new(),
+ }
+ }
+
+ /// Get unique hashes for all files the SPDX.
+ pub fn get_unique_hashes(&self, algorithm: Algorithm) -> HashSet<String> {
+ info!("Getting unique hashes for files in SPDX.");
+
+ let mut unique_hashes: HashSet<String> = HashSet::new();
+
+ for file_information in &self.file_information {
+ if let Some(checksum) = file_information.checksum(algorithm) {
+ unique_hashes.insert(checksum.to_string());
+ }
+ }
+
+ unique_hashes
+ }
+
+ /// Find related files of the package with the provided id.
+ pub fn get_files_for_package(
+ &self,
+ package_spdx_id: &str,
+ ) -> Vec<(&FileInformation, &Relationship)> {
+ info!("Finding related files for package {}.", &package_spdx_id);
+
+ let relationships = self
+ .relationships
+ .iter()
+ .filter(|relationship| relationship.spdx_element_id == package_spdx_id);
+
+ let mut result: Vec<(&FileInformation, &Relationship)> = Vec::new();
+
+ for relationship in relationships {
+ let file = self
+ .file_information
+ .iter()
+ .find(|file| file.file_spdx_identifier == relationship.related_spdx_element);
+ if let Some(file) = file {
+ result.push((file, relationship));
+ };
+ }
+
+ result
+ }
+
+ /// Get all license identifiers from the SPDX.
+ ///
+ /// # Errors
+ ///
+ /// Returns [`SpdxError`] if parsing of the expressions fails.
+ pub fn get_license_ids(&self) -> HashSet<String> {
+ info!("Getting all license identifiers from SPDX.");
+
+ let mut license_ids = HashSet::new();
+
+ for file in &self.file_information {
+ for license in &file.concluded_license.identifiers() {
+ if license != "NOASSERTION" && license != "NONE" {
+ license_ids.insert(license.clone());
+ }
+ }
+ }
+
+ license_ids
+ }
+
+ /// Get all relationships where the given SPDX ID is the SPDX element id.
+ pub fn relationships_for_spdx_id(&self, spdx_id: &str) -> Vec<&Relationship> {
+ self.relationships
+ .iter()
+ .filter(|relationship| relationship.spdx_element_id == spdx_id)
+ .collect()
+ }
+
+ /// Get all relationships where the given SPDX ID is the related SPDX element id.
+ pub fn relationships_for_related_spdx_id(&self, spdx_id: &str) -> Vec<&Relationship> {
+ self.relationships
+ .iter()
+ .filter(|relationship| relationship.related_spdx_element == spdx_id)
+ .collect()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::{fs::read_to_string, iter::FromIterator};
+
+ use spdx_expression::SpdxExpression;
+
+ use crate::models::RelationshipType;
+
+ use super::*;
+
+ #[test]
+ fn deserialize_simple_spdx() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+
+ assert_eq!(
+ spdx_file.document_creation_information.document_name,
+ "SPDX-Tools-v2.0".to_string()
+ );
+ }
+
+ #[test]
+ fn find_related_files_for_package() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+
+ let package_1_files = spdx_file.get_files_for_package("SPDXRef-Package");
+
+ assert_eq!(package_1_files.len(), 1);
+
+ let file = package_1_files
+ .iter()
+ .find(|package_and_relationship| {
+ package_and_relationship.0.file_name == *"./lib-source/jena-2.6.3-sources.jar"
+ })
+ .expect("Should always be found");
+
+ assert_eq!(file.0.file_spdx_identifier, "SPDXRef-JenaLib");
+ assert_eq!(file.1.relationship_type, RelationshipType::Contains);
+
+ assert_eq!(
+ file.0.concluded_license,
+ SpdxExpression::parse("LicenseRef-1").unwrap()
+ );
+ }
+
+ #[test]
+ fn get_all_licenses_from_spdx() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+
+ let actual = spdx_file.get_license_ids();
+
+ let expected = HashSet::from_iter([
+ "Apache-2.0".into(),
+ "LicenseRef-1".into(),
+ "LGPL-2.0-only".into(),
+ "LicenseRef-2".into(),
+ ]);
+
+ assert_eq!(expected, actual);
+ }
+
+ #[test]
+ fn get_relationships_for_spdx_id() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+
+ let relationships = spdx_file.relationships_for_spdx_id("SPDXRef-Package");
+ let relationship_1 = Relationship {
+ spdx_element_id: "SPDXRef-Package".into(),
+ related_spdx_element: "SPDXRef-Saxon".into(),
+ relationship_type: RelationshipType::DynamicLink,
+ comment: None,
+ };
+ let relationship_2 = Relationship {
+ spdx_element_id: "SPDXRef-Package".into(),
+ related_spdx_element: "SPDXRef-JenaLib".into(),
+ relationship_type: RelationshipType::Contains,
+ comment: None,
+ };
+ let expected_relationships = vec![&relationship_1, &relationship_2];
+
+ assert_eq!(relationships, expected_relationships);
+ }
+
+ #[test]
+ fn get_relationships_for_related_spdx_id() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+
+ let relationships = spdx_file.relationships_for_related_spdx_id("SPDXRef-Package");
+ let relationship_1 = Relationship {
+ spdx_element_id: "SPDXRef-DOCUMENT".into(),
+ related_spdx_element: "SPDXRef-Package".into(),
+ relationship_type: RelationshipType::Contains,
+ comment: None,
+ };
+ let relationship_2 = Relationship {
+ spdx_element_id: "SPDXRef-DOCUMENT".into(),
+ related_spdx_element: "SPDXRef-Package".into(),
+ relationship_type: RelationshipType::Describes,
+ comment: None,
+ };
+ let relationship_3 = Relationship {
+ spdx_element_id: "SPDXRef-JenaLib".into(),
+ related_spdx_element: "SPDXRef-Package".into(),
+ relationship_type: RelationshipType::Contains,
+ comment: None,
+ };
+ let expected_relationships = vec![&relationship_1, &relationship_2, &relationship_3];
+
+ assert_eq!(relationships, expected_relationships);
+ }
+
+ #[test]
+ fn get_unique_hashes_for_files() {
+ let spdx_file: SPDX = serde_json::from_str(
+ &read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json").unwrap(),
+ )
+ .unwrap();
+ let hashes = spdx_file.get_unique_hashes(Algorithm::SHA1);
+
+ let expected = [
+ "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12".to_string(),
+ "c2b4e1c67a2d28fced849ee1bb76e7391b93f125".to_string(),
+ "3ab4e1c67a2d28fced849ee1bb76e7391b93f125".to_string(),
+ "d6a770ba38583ed4bb4525bd96e50461655d2758".to_string(),
+ ]
+ .iter()
+ .cloned()
+ .collect::<HashSet<_>>();
+
+ assert_eq!(hashes, expected);
+ }
+}
diff --git a/vendor/spdx-rs/src/parsers/mod.rs b/vendor/spdx-rs/src/parsers/mod.rs
new file mode 100644
index 000000000..59ce03bae
--- /dev/null
+++ b/vendor/spdx-rs/src/parsers/mod.rs
@@ -0,0 +1,1100 @@
+// SPDX-FileCopyrightText: 2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+//! Parsers for deserializing [`SPDX`] from different data formats.
+//!
+//! The SPDX spec supports some data formats that are not supported by [Serde], so parsing from JSON
+//! (and YAML) is achieved with the data format specific crates:
+//!
+//! ```rust
+//! # use spdx_rs::error::SpdxError;
+//! use spdx_rs::models::SPDX;
+//! # fn main() -> Result<(), SpdxError> {
+//!
+//! let spdx_file = std::fs::read_to_string("tests/data/SPDXJSONExample-v2.2.spdx.json")?;
+//! let spdx_document: SPDX = serde_json::from_str(&spdx_file).unwrap();
+//!
+//! assert_eq!(
+//! spdx_document.document_creation_information.document_name,
+//! "SPDX-Tools-v2.0"
+//! );
+//! # Ok(())
+//! # }
+//! ```
+//!
+//! [Serde]: https://serde.rs
+
+use std::collections::HashSet;
+
+use chrono::{DateTime, Utc};
+use spdx_expression::{SimpleExpression, SpdxExpression};
+
+use crate::{
+ error::SpdxError,
+ models::{
+ Annotation, AnnotationType, DocumentCreationInformation, ExternalPackageReference,
+ FileInformation, OtherLicensingInformationDetected, PackageInformation, Pointer, Range,
+ Relationship, Snippet, SPDX,
+ },
+ parsers::tag_value::{atoms, Atom},
+};
+
+mod tag_value;
+
+/// Parse a tag-value SPDX document to [`SPDX`].
+///
+/// # Usage
+///
+/// ```
+/// # use spdx_rs::error::SpdxError;
+/// use spdx_rs::parsers::spdx_from_tag_value;
+/// # fn main() -> Result<(), SpdxError> {
+///
+/// let spdx_file = std::fs::read_to_string("tests/data/SPDXTagExample-v2.2.spdx")?;
+/// let spdx_document = spdx_from_tag_value(&spdx_file)?;
+///
+/// assert_eq!(
+/// spdx_document.document_creation_information.document_name,
+/// "SPDX-Tools-v2.0"
+/// );
+/// # Ok(())
+/// # }
+/// ```
+///
+/// # Errors
+///
+/// - If parsing of the tag-value fails.
+/// - If parsing of some of the values fail.
+pub fn spdx_from_tag_value(input: &str) -> Result<SPDX, SpdxError> {
+ let (_, atoms) = atoms(input).map_err(|err| SpdxError::TagValueParse(err.to_string()))?;
+
+ let spdx = spdx_from_atoms(&atoms)?;
+
+ Ok(spdx)
+}
+
+#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
+fn spdx_from_atoms(atoms: &[Atom]) -> Result<SPDX, SpdxError> {
+ let mut document_creation_information_in_progress =
+ Some(DocumentCreationInformation::default());
+ let mut document_creation_information_final: Option<DocumentCreationInformation> = None;
+
+ let mut package_information: Vec<PackageInformation> = Vec::new();
+ let mut package_in_progress: Option<PackageInformation> = None;
+ let mut external_package_ref_in_progress: Option<ExternalPackageReference> = None;
+
+ let mut other_licensing_information_detected: Vec<OtherLicensingInformationDetected> =
+ Vec::new();
+ let mut license_info_in_progress: Option<OtherLicensingInformationDetected> = None;
+
+ let mut file_information: Vec<FileInformation> = Vec::new();
+ let mut file_in_progress: Option<FileInformation> = None;
+
+ let mut snippet_information: Vec<Snippet> = Vec::new();
+ let mut snippet_in_progress: Option<Snippet> = None;
+
+ let mut relationships: HashSet<Relationship> = HashSet::new();
+ let mut relationship_in_progress: Option<Relationship> = None;
+
+ let mut annotations: Vec<Annotation> = Vec::new();
+ let mut annotation_in_progress = AnnotationInProgress::default();
+
+ for atom in atoms {
+ let document_creation_information = process_atom_for_document_creation_information(
+ atom,
+ &mut document_creation_information_in_progress,
+ )?;
+ if let Some(document_creation_information) = document_creation_information {
+ document_creation_information_final = Some(document_creation_information);
+ document_creation_information_in_progress = None;
+ }
+ process_atom_for_packages(
+ atom,
+ &mut package_information,
+ &mut package_in_progress,
+ &mut external_package_ref_in_progress,
+ );
+ process_atom_for_files(
+ atom,
+ &mut file_in_progress,
+ &mut file_information,
+ &package_in_progress,
+ &mut relationships,
+ );
+ process_atom_for_snippets(atom, &mut snippet_information, &mut snippet_in_progress);
+ process_atom_for_relationships(atom, &mut relationships, &mut relationship_in_progress);
+ process_atom_for_annotations(atom, &mut annotations, &mut annotation_in_progress)?;
+ process_atom_for_license_info(
+ atom,
+ &mut other_licensing_information_detected,
+ &mut license_info_in_progress,
+ )?;
+ }
+ if let Some(file) = file_in_progress {
+ file_information.push(file);
+ }
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet_information.push(snippet.clone());
+ }
+
+ if let Some(package) = package_in_progress {
+ package_information.push(package);
+ }
+
+ if let Some(relationship) = relationship_in_progress {
+ relationships.insert(relationship);
+ }
+
+ if let Some(license_info) = license_info_in_progress {
+ other_licensing_information_detected.push(license_info);
+ }
+
+ if document_creation_information_in_progress.is_some() {
+ document_creation_information_final = document_creation_information_in_progress;
+ }
+
+ process_annotation(&mut annotation_in_progress, &mut annotations);
+
+ Ok(SPDX {
+ document_creation_information: document_creation_information_final
+ // TODO: Proper error handling
+ .expect("If this doesn't exist, the document is not valid."),
+ package_information,
+ other_licensing_information_detected,
+ file_information,
+ snippet_information,
+ relationships: relationships.into_iter().collect(),
+ annotations,
+ // TODO: This should probably be removed.
+ spdx_ref_counter: 0,
+ })
+}
+
+fn process_atom_for_document_creation_information(
+ atom: &Atom,
+ mut document_creation_information_in_progress: &mut Option<DocumentCreationInformation>,
+) -> Result<Option<DocumentCreationInformation>, SpdxError> {
+ // Get document creation information.
+ let mut final_creation_information = None;
+ match atom {
+ Atom::SpdxVersion(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.spdx_version = value.to_string();
+ }
+ }
+ Atom::DataLicense(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.data_license = value.to_string();
+ }
+ }
+ Atom::SPDXID(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.spdx_identifier = value.to_string();
+ }
+ }
+ Atom::DocumentName(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.document_name = value.to_string();
+ }
+ }
+ Atom::DocumentNamespace(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.spdx_document_namespace = value.to_string();
+ }
+ }
+ Atom::ExternalDocumentRef(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information
+ .external_document_references
+ .push(value.clone());
+ }
+ }
+ Atom::LicenseListVersion(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information
+ .creation_info
+ .license_list_version = Some(value.to_string());
+ }
+ }
+ Atom::Creator(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information
+ .creation_info
+ .creators
+ .push(value.to_string());
+ }
+ }
+ Atom::Created(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.creation_info.created =
+ DateTime::parse_from_rfc3339(value)?.with_timezone(&Utc);
+ }
+ }
+ Atom::CreatorComment(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.creation_info.creator_comment =
+ Some(value.to_string());
+ }
+ }
+ Atom::DocumentComment(value) => {
+ if let Some(document_creation_information) =
+ &mut document_creation_information_in_progress
+ {
+ document_creation_information.document_comment = Some(value.to_string());
+ }
+ }
+ Atom::TVComment(_) => {}
+ _ => {
+ if let Some(document_creation_information) = document_creation_information_in_progress {
+ final_creation_information = Some(document_creation_information.clone());
+ }
+ }
+ }
+ Ok(final_creation_information)
+}
+
+#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
+fn process_atom_for_packages(
+ atom: &Atom,
+ packages: &mut Vec<PackageInformation>,
+ mut package_in_progress: &mut Option<PackageInformation>,
+ mut external_package_ref_in_progress: &mut Option<ExternalPackageReference>,
+) {
+ match atom {
+ Atom::PackageName(value) => {
+ if let Some(package) = &mut package_in_progress {
+ if let Some(pkg_ref) = &mut external_package_ref_in_progress {
+ package.external_reference.push(pkg_ref.clone());
+ *external_package_ref_in_progress = None;
+ }
+ packages.push(package.clone());
+ }
+ *package_in_progress = Some(PackageInformation::default());
+
+ if let Some(package) = &mut package_in_progress {
+ package.package_name = value.to_string();
+ }
+ }
+ Atom::SPDXID(value) => {
+ if let Some(package) = &mut package_in_progress {
+ if package.package_spdx_identifier == "NOASSERTION" {
+ package.package_spdx_identifier = value.to_string();
+ }
+ }
+ }
+ Atom::PackageVersion(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_version = Some(value.to_string());
+ }
+ }
+ Atom::PackageFileName(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_file_name = Some(value.to_string());
+ }
+ }
+ Atom::PackageSupplier(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_supplier = Some(value.to_string());
+ }
+ }
+ Atom::PackageOriginator(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_originator = Some(value.to_string());
+ }
+ }
+ Atom::PackageDownloadLocation(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_download_location = value.to_string();
+ }
+ }
+ Atom::PackageVerificationCode(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_verification_code = Some(value.clone());
+ }
+ }
+ Atom::PackageChecksum(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_checksum.push(value.clone());
+ }
+ }
+ Atom::PackageHomePage(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_home_page = Some(value.clone());
+ }
+ }
+ Atom::PackageSourceInfo(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.source_information = Some(value.clone());
+ }
+ }
+ Atom::PackageLicenseConcluded(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.concluded_license = SpdxExpression::parse(value).unwrap();
+ }
+ }
+ Atom::PackageLicenseInfoFromFiles(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package
+ .all_licenses_information_from_files
+ .push(value.clone());
+ }
+ }
+ Atom::PackageLicenseDeclared(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.declared_license = SpdxExpression::parse(value).unwrap();
+ }
+ }
+ Atom::PackageLicenseComments(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.comments_on_license = Some(value.clone());
+ }
+ }
+ Atom::PackageCopyrightText(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.copyright_text = value.clone();
+ }
+ }
+ Atom::PackageSummary(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_summary_description = Some(value.clone());
+ }
+ }
+ Atom::PackageDescription(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_detailed_description = Some(value.clone());
+ }
+ }
+ Atom::PackageAttributionText(value) => {
+ if let Some(package) = &mut package_in_progress {
+ package.package_attribution_text.push(value.clone());
+ }
+ }
+ Atom::ExternalRef(value) => {
+ if let Some(pkg_ref) = &mut external_package_ref_in_progress {
+ if let Some(package) = &mut package_in_progress {
+ package.external_reference.push(pkg_ref.clone());
+ }
+ }
+ *external_package_ref_in_progress = Some(value.clone());
+ }
+ Atom::ExternalRefComment(value) => {
+ if let Some(pkg_ref) = &mut external_package_ref_in_progress {
+ pkg_ref.reference_comment = Some(value.clone());
+ }
+ }
+ _ => {}
+ }
+}
+
+fn process_atom_for_files(
+ atom: &Atom,
+ mut file_in_progress: &mut Option<FileInformation>,
+ files: &mut Vec<FileInformation>,
+ package_in_progress: &Option<PackageInformation>,
+ relationships: &mut HashSet<Relationship>,
+) {
+ match atom {
+ Atom::PackageName(_) => {
+ if let Some(file) = &mut file_in_progress {
+ files.push(file.clone());
+ *file_in_progress = None;
+ }
+ }
+ Atom::FileName(value) => {
+ if let Some(file) = &mut file_in_progress {
+ files.push(file.clone());
+ }
+ *file_in_progress = Some(FileInformation::default());
+
+ if let Some(file) = &mut file_in_progress {
+ file.file_name = value.to_string();
+ }
+ }
+ Atom::SPDXID(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.file_spdx_identifier = value.to_string();
+ if let Some(package) = package_in_progress {
+ relationships.insert(Relationship::new(
+ &package.package_spdx_identifier,
+ value,
+ crate::models::RelationshipType::Contains,
+ None,
+ ));
+ }
+ }
+ }
+ Atom::FileComment(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.file_comment = Some(value.to_string());
+ }
+ }
+ Atom::FileType(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.file_type.push(*value);
+ }
+ }
+ Atom::FileChecksum(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.file_checksum.push(value.clone());
+ }
+ }
+ Atom::LicenseConcluded(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.concluded_license = SpdxExpression::parse(value).unwrap();
+ }
+ }
+ Atom::LicenseInfoInFile(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.license_information_in_file
+ .push(SimpleExpression::parse(value).unwrap());
+ }
+ }
+ Atom::LicenseComments(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.comments_on_license = Some(value.clone());
+ }
+ }
+ Atom::FileCopyrightText(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.copyright_text = value.clone();
+ }
+ }
+ Atom::FileNotice(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.file_notice = Some(value.clone());
+ }
+ }
+ Atom::FileContributor(value) => {
+ if let Some(file) = &mut file_in_progress {
+ file.file_contributor.push(value.clone());
+ }
+ }
+ _ => {}
+ }
+}
+
+fn process_atom_for_snippets(
+ atom: &Atom,
+ snippets: &mut Vec<Snippet>,
+ mut snippet_in_progress: &mut Option<Snippet>,
+) {
+ match atom {
+ Atom::SnippetSPDXID(value) => {
+ if let Some(snippet) = &snippet_in_progress {
+ snippets.push(snippet.clone());
+ }
+
+ *snippet_in_progress = Some(Snippet::default());
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_spdx_identifier = value.to_string();
+ }
+ }
+ Atom::SnippetFromFileSPDXID(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_from_file_spdx_identifier = value.to_string();
+ }
+ }
+ Atom::SnippetByteRange(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ let start_pointer = Pointer::new_byte(None, value.0);
+ let end_pointer = Pointer::new_byte(None, value.1);
+ let range = Range::new(start_pointer, end_pointer);
+ snippet.ranges.push(range);
+ }
+ }
+ Atom::SnippetLineRange(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ let start_pointer = Pointer::new_line(None, value.0);
+ let end_pointer = Pointer::new_line(None, value.1);
+ let range = Range::new(start_pointer, end_pointer);
+ snippet.ranges.push(range);
+ }
+ }
+ Atom::SnippetLicenseConcluded(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_concluded_license = SpdxExpression::parse(value).unwrap();
+ }
+ }
+ Atom::LicenseInfoInSnippet(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet
+ .license_information_in_snippet
+ .push(value.to_string());
+ }
+ }
+ Atom::SnippetLicenseComments(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_comments_on_license = Some(value.to_string());
+ }
+ }
+ Atom::SnippetCopyrightText(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_copyright_text = value.to_string();
+ }
+ }
+ Atom::SnippetComment(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_comment = Some(value.to_string());
+ }
+ }
+ Atom::SnippetName(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_name = Some(value.to_string());
+ }
+ }
+ Atom::SnippetAttributionText(value) => {
+ if let Some(snippet) = &mut snippet_in_progress {
+ snippet.snippet_attribution_text = Some(value.to_string());
+ }
+ }
+ _ => {}
+ }
+}
+
+#[allow(clippy::unnecessary_wraps)]
+fn process_atom_for_relationships(
+ atom: &Atom,
+ relationships: &mut HashSet<Relationship>,
+ mut relationship_in_progress: &mut Option<Relationship>,
+) {
+ match atom {
+ Atom::Relationship(value) => {
+ if let Some(relationship) = relationship_in_progress {
+ relationships.insert(relationship.clone());
+ }
+ *relationship_in_progress = Some(value.clone());
+ }
+ Atom::RelationshipComment(value) => {
+ if let Some(relationship) = &mut relationship_in_progress {
+ relationship.comment = Some(value.to_string());
+ }
+ }
+ _ => {}
+ }
+}
+
+#[derive(Debug, Default)]
+struct AnnotationInProgress {
+ annotator_in_progress: Option<String>,
+ date_in_progress: Option<DateTime<Utc>>,
+ comment_in_progress: Option<String>,
+ type_in_progress: Option<AnnotationType>,
+ spdxref_in_progress: Option<String>,
+}
+
+fn process_annotation(
+ mut annotation_in_progress: &mut AnnotationInProgress,
+
+ annotations: &mut Vec<Annotation>,
+) {
+ if let AnnotationInProgress {
+ annotator_in_progress: Some(annotator),
+ date_in_progress: Some(date),
+ comment_in_progress: Some(comment),
+ type_in_progress: Some(annotation_type),
+ spdxref_in_progress: Some(spdxref),
+ } = &mut annotation_in_progress
+ {
+ let annotation = Annotation::new(
+ annotator.clone(),
+ *date,
+ *annotation_type,
+ Some(spdxref.clone()),
+ comment.clone(),
+ );
+ *annotation_in_progress = AnnotationInProgress {
+ annotator_in_progress: None,
+ comment_in_progress: None,
+ date_in_progress: None,
+ spdxref_in_progress: None,
+ type_in_progress: None,
+ };
+ annotations.push(annotation);
+ }
+}
+
+fn process_atom_for_annotations(
+ atom: &Atom,
+ annotations: &mut Vec<Annotation>,
+ mut annotation_in_progress: &mut AnnotationInProgress,
+) -> Result<(), SpdxError> {
+ process_annotation(annotation_in_progress, annotations);
+
+ match atom {
+ Atom::Annotator(value) => {
+ annotation_in_progress.annotator_in_progress = Some(value.clone());
+ }
+ Atom::AnnotationDate(value) => {
+ annotation_in_progress.date_in_progress =
+ Some(DateTime::parse_from_rfc3339(value)?.with_timezone(&Utc));
+ }
+ Atom::AnnotationComment(value) => {
+ annotation_in_progress.comment_in_progress = Some(value.clone());
+ }
+ Atom::AnnotationType(value) => {
+ annotation_in_progress.type_in_progress = Some(*value);
+ }
+ Atom::SPDXREF(value) => {
+ annotation_in_progress.spdxref_in_progress = Some(value.clone());
+ }
+ _ => {}
+ }
+
+ Ok(())
+}
+
+#[allow(clippy::unnecessary_wraps)]
+fn process_atom_for_license_info(
+ atom: &Atom,
+ license_infos: &mut Vec<OtherLicensingInformationDetected>,
+ mut license_info_in_progress: &mut Option<OtherLicensingInformationDetected>,
+) -> Result<(), SpdxError> {
+ match atom {
+ Atom::LicenseID(value) => {
+ if let Some(license_info) = &mut license_info_in_progress {
+ license_infos.push(license_info.clone());
+ }
+ *license_info_in_progress = Some(OtherLicensingInformationDetected::default());
+
+ if let Some(license_info) = &mut license_info_in_progress {
+ license_info.license_identifier = value.to_string();
+ }
+ }
+ Atom::ExtractedText(value) => {
+ if let Some(license_info) = &mut license_info_in_progress {
+ license_info.extracted_text = value.to_string();
+ }
+ }
+ Atom::LicenseName(value) => {
+ if let Some(license_info) = &mut license_info_in_progress {
+ license_info.license_name = value.to_string();
+ }
+ }
+ Atom::LicenseCrossReference(value) => {
+ if let Some(license_info) = &mut license_info_in_progress {
+ license_info.license_cross_reference.push(value.to_string());
+ }
+ }
+ Atom::LicenseComment(value) => {
+ if let Some(license_info) = &mut license_info_in_progress {
+ license_info.license_comment = Some(value.to_string());
+ }
+ }
+ _ => {}
+ }
+
+ Ok(())
+}
+
+#[cfg(test)]
+#[allow(clippy::too_many_lines)]
+mod test_super {
+ use std::{fs::read_to_string, iter::FromIterator};
+
+ use chrono::TimeZone;
+
+ use crate::models::{
+ Algorithm, Checksum, ExternalDocumentReference, ExternalPackageReferenceCategory, FileType,
+ };
+
+ use super::*;
+
+ #[test]
+ fn whole_spdx_is_parsed() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ assert_eq!(spdx.package_information.len(), 4);
+ assert_eq!(spdx.file_information.len(), 4);
+ }
+
+ #[test]
+ fn spdx_creation_info_is_retrieved() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ let document_creation_information = spdx.document_creation_information;
+ assert_eq!(document_creation_information.spdx_version, "SPDX-2.2");
+ assert_eq!(document_creation_information.data_license, "CC0-1.0");
+ assert_eq!(
+ document_creation_information.spdx_document_namespace,
+ "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301"
+ );
+ assert_eq!(
+ document_creation_information.document_name,
+ "SPDX-Tools-v2.0"
+ );
+ assert_eq!(
+ document_creation_information.spdx_identifier,
+ "SPDXRef-DOCUMENT"
+ );
+ assert_eq!(
+ document_creation_information.document_comment,
+ Some(
+ "This document was created using SPDX 2.0 using licenses from the web site."
+ .to_string()
+ )
+ );
+ assert_eq!(
+ document_creation_information.external_document_references,
+ vec![ExternalDocumentReference::new(
+ "spdx-tool-1.2".to_string(),
+ "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"
+ .to_string(),
+ Checksum::new(Algorithm::SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2759")
+ )]
+ );
+ assert!(document_creation_information
+ .creation_info
+ .creators
+ .contains(&"Tool: LicenseFind-1.0".to_string()));
+ assert!(document_creation_information
+ .creation_info
+ .creators
+ .contains(&"Organization: ExampleCodeInspect ()".to_string()));
+ assert!(document_creation_information
+ .creation_info
+ .creators
+ .contains(&"Person: Jane Doe ()".to_string()));
+ assert_eq!(
+ document_creation_information.creation_info.created,
+ Utc.ymd(2010, 1, 29).and_hms(18, 30, 22)
+ );
+ assert_eq!(
+ document_creation_information.creation_info.creator_comment,
+ Some(
+ "This package has been shipped in source and binary form.
+The binaries were created with gcc 4.5.1 and expect to link to
+compatible system run time libraries."
+ .to_string()
+ )
+ );
+ assert_eq!(
+ document_creation_information
+ .creation_info
+ .license_list_version,
+ Some("3.9".to_string())
+ );
+ }
+
+ #[test]
+ fn package_info_is_retrieved() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ let packages = spdx.package_information;
+ assert_eq!(packages.len(), 4);
+
+ let glibc = packages.iter().find(|p| p.package_name == "glibc").unwrap();
+ assert_eq!(glibc.package_spdx_identifier, "SPDXRef-Package");
+ assert_eq!(glibc.package_version, Some("2.11.1".to_string()));
+ assert_eq!(
+ glibc.package_file_name,
+ Some("glibc-2.11.1.tar.gz".to_string())
+ );
+ assert_eq!(
+ glibc.package_supplier,
+ Some("Person: Jane Doe (jane.doe@example.com)".to_string())
+ );
+ assert_eq!(
+ glibc.package_originator,
+ Some("Organization: ExampleCodeInspect (contact@example.com)".to_string())
+ );
+ assert_eq!(
+ glibc.package_download_location,
+ "http://ftp.gnu.org/gnu/glibc/glibc-ports-2.15.tar.gz".to_string()
+ );
+ assert_eq!(
+ glibc.package_verification_code.as_ref().unwrap().value,
+ "d6a770ba38583ed4bb4525bd96e50461655d2758".to_string()
+ );
+ assert_eq!(
+ glibc.package_verification_code.as_ref().unwrap().excludes,
+ vec!["./package.spdx"]
+ );
+ assert_eq!(
+ glibc.package_checksum,
+ vec![
+ Checksum::new(Algorithm::MD5, "624c1abb3664f4b35547e7c73864ad24"),
+ Checksum::new(Algorithm::SHA1, "85ed0817af83a24ad8da68c2b5094de69833983c"),
+ Checksum::new(
+ Algorithm::SHA256,
+ "11b6d3ee554eedf79299905a98f9b9a04e498210b59f15094c916c91d150efcd"
+ ),
+ ]
+ );
+ assert_eq!(
+ glibc.package_home_page,
+ Some("http://ftp.gnu.org/gnu/glibc".to_string())
+ );
+ assert_eq!(
+ glibc.source_information,
+ Some("uses glibc-2_11-branch from git://sourceware.org/git/glibc.git.".to_string())
+ );
+ assert_eq!(
+ glibc.concluded_license.identifiers(),
+ HashSet::from_iter(["LGPL-2.0-only".to_string(), "LicenseRef-3".to_string()])
+ );
+ assert_eq!(
+ glibc.all_licenses_information_from_files,
+ vec!["GPL-2.0-only", "LicenseRef-2", "LicenseRef-1"]
+ );
+ assert_eq!(
+ glibc.declared_license.identifiers(),
+ HashSet::from_iter(["LGPL-2.0-only".to_string(), "LicenseRef-3".to_string()])
+ );
+ assert_eq!(glibc.comments_on_license, Some("The license for this project changed with the release of version x.y. The version of the project included here post-dates the license change.".to_string()));
+ assert_eq!(glibc.copyright_text, "Copyright 2008-2010 John Smith");
+ assert_eq!(
+ glibc.package_summary_description,
+ Some("GNU C library.".to_string())
+ );
+ assert_eq!(glibc.package_detailed_description, Some("The GNU C Library defines functions that are specified by the ISO C standard, as well as additional features specific to POSIX and other derivatives of the Unix operating system, and extensions specific to GNU systems.".to_string()));
+ assert_eq!(
+ glibc.package_attribution_text,
+ vec!["The GNU C Library is free software. See the file COPYING.LIB for copying conditions, and LICENSES for notices about a few contributions that require these additional notices to be distributed. License copyright years may be listed using range notation, e.g., 1996-2015, indicating that every year in the range, inclusive, is a copyrightable year that would otherwise be listed individually.".to_string()]
+ );
+ assert_eq!(
+ glibc.external_reference,
+ vec![
+ ExternalPackageReference::new(
+ ExternalPackageReferenceCategory::Security,
+ "cpe23Type".to_string(),
+ "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*".to_string(),
+ None
+ ),
+ ExternalPackageReference::new(
+ ExternalPackageReferenceCategory::Other,
+ "LocationRef-acmeforge".to_string(),
+ "acmecorp/acmenator/4.1.3-alpha".to_string(),
+ Some("This is the external ref for Acme".to_string())
+ ),
+ ]
+ );
+ let jena = packages.iter().find(|p| p.package_name == "Jena").unwrap();
+ assert_eq!(jena.package_spdx_identifier, "SPDXRef-fromDoap-0");
+ assert_eq!(
+ jena.external_reference,
+ vec![ExternalPackageReference::new(
+ ExternalPackageReferenceCategory::PackageManager,
+ "purl".to_string(),
+ "pkg:maven/org.apache.jena/apache-jena@3.12.0".to_string(),
+ None
+ ),]
+ );
+ }
+
+ #[test]
+ fn file_info_is_retrieved() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ let files = spdx.file_information;
+ assert_eq!(files.len(), 4);
+
+ let fooc = files
+ .iter()
+ .find(|p| p.file_name == "./package/foo.c")
+ .unwrap();
+ assert_eq!(fooc.file_spdx_identifier, "SPDXRef-File");
+ assert_eq!(fooc.file_comment, Some("The concluded license was taken from the package level that the file was included in.
+This information was found in the COPYING.txt file in the xyz directory.".to_string()));
+ assert_eq!(fooc.file_type, vec![FileType::Source]);
+ assert_eq!(
+ fooc.file_checksum,
+ vec![
+ Checksum::new(Algorithm::SHA1, "d6a770ba38583ed4bb4525bd96e50461655d2758"),
+ Checksum::new(Algorithm::MD5, "624c1abb3664f4b35547e7c73864ad24")
+ ]
+ );
+ assert_eq!(
+ fooc.concluded_license.identifiers(),
+ HashSet::from_iter(["LGPL-2.0-only".to_string(), "LicenseRef-2".to_string(),])
+ );
+ assert_eq!(
+ fooc.license_information_in_file,
+ vec![
+ SimpleExpression::parse("GPL-2.0-only").unwrap(),
+ SimpleExpression::parse("LicenseRef-2").unwrap()
+ ]
+ );
+ assert_eq!(fooc.comments_on_license, Some("The concluded license was taken from the package level that the file was included in.".to_string()));
+ assert_eq!(
+ fooc.copyright_text,
+ "Copyright 2008-2010 John Smith".to_string()
+ );
+ assert_eq!(
+ fooc.file_notice,
+ Some("Copyright (c) 2001 Aaron Lehmann aaroni@vitelus.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the �Software�), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED �AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.".to_string())
+ );
+ assert_eq!(
+ fooc.file_contributor,
+ vec![
+ "The Regents of the University of California".to_string(),
+ "Modified by Paul Mundt lethal@linux-sh.org".to_string(),
+ "IBM Corporation".to_string(),
+ ]
+ );
+ let doap = files
+ .iter()
+ .find(|p| p.file_name == "./src/org/spdx/parser/DOAPProject.java")
+ .unwrap();
+
+ assert_eq!(doap.file_spdx_identifier, "SPDXRef-DoapSource");
+ }
+
+ #[test]
+ fn snippet_info_is_retrieved() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ let snippets = spdx.snippet_information;
+ assert_eq!(snippets.len(), 1);
+
+ let snippet = snippets[0].clone();
+
+ assert_eq!(snippet.snippet_spdx_identifier, "SPDXRef-Snippet");
+ assert_eq!(
+ snippet.snippet_from_file_spdx_identifier,
+ "SPDXRef-DoapSource"
+ );
+ assert_eq!(snippet.ranges.len(), 2);
+ assert!(snippet
+ .ranges
+ .iter()
+ .any(|snip| snip.start_pointer == Pointer::new_byte(None, 310)));
+ assert!(snippet
+ .ranges
+ .iter()
+ .any(|snip| snip.end_pointer == Pointer::new_byte(None, 420)));
+ assert!(snippet
+ .ranges
+ .iter()
+ .any(|snip| snip.start_pointer == Pointer::new_line(None, 5)));
+ assert!(snippet
+ .ranges
+ .iter()
+ .any(|snip| snip.end_pointer == Pointer::new_line(None, 23)));
+ assert_eq!(
+ snippet.snippet_concluded_license,
+ SpdxExpression::parse("GPL-2.0-only").unwrap()
+ );
+ assert_eq!(snippet.license_information_in_snippet, vec!["GPL-2.0-only"]);
+ assert_eq!(snippet.snippet_comments_on_license, 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()));
+ assert_eq!(
+ snippet.snippet_copyright_text,
+ "Copyright 2008-2010 John Smith"
+ );
+ assert_eq!(snippet.snippet_comment, 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()));
+ assert_eq!(snippet.snippet_name, Some("from linux kernel".to_string()));
+ }
+
+ #[test]
+ fn relationships_are_retrieved() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ let relationships = spdx.relationships;
+ assert_eq!(relationships.len(), 11);
+
+ assert!(relationships.contains(&Relationship::new(
+ "SPDXRef-DOCUMENT",
+ "SPDXRef-Package",
+ crate::models::RelationshipType::Contains,
+ None
+ )));
+ assert!(relationships.contains(&Relationship::new(
+ "SPDXRef-CommonsLangSrc",
+ "NOASSERTION",
+ crate::models::RelationshipType::GeneratedFrom,
+ None
+ )));
+
+ // Implied relationship by the file following the package in tag-value.
+ assert!(relationships.contains(&Relationship::new(
+ "SPDXRef-Package",
+ "SPDXRef-DoapSource",
+ crate::models::RelationshipType::Contains,
+ None
+ )));
+ }
+
+ #[test]
+ fn annotations_are_retrieved() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ let annotations = spdx.annotations;
+ assert_eq!(annotations.len(), 5);
+
+ assert_eq!(
+ annotations[2],
+ Annotation::new(
+ "Person: Suzanne Reviewer".to_string(),
+ Utc.ymd(2011, 3, 13).and_hms(0, 0, 0),
+ AnnotationType::Review,
+ Some("SPDXRef-DOCUMENT".to_string()),
+ "Another example reviewer.".to_string()
+ )
+ );
+ }
+
+ #[test]
+ fn license_info_is_retrieved() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+ let license_info = spdx.other_licensing_information_detected;
+ assert_eq!(license_info.len(), 5);
+ assert_eq!(license_info[1].license_identifier, "LicenseRef-2");
+ assert_eq!(
+ license_info[3].license_name,
+ "Beer-Ware License (Version 42)"
+ );
+ assert_eq!(
+ license_info[3].license_cross_reference,
+ vec!["http://people.freebsd.org/~phk/"]
+ );
+ assert_eq!(
+ license_info[3].license_comment,
+ Some("The beerware license has a couple of other standard variants.".to_string())
+ );
+ assert!(license_info[3]
+ .extracted_text
+ .starts_with(r#""THE BEER-WARE"#));
+ assert!(license_info[3]
+ .extracted_text
+ .ends_with("Poul-Henning Kamp"));
+ }
+
+ #[test]
+ fn tag_value_is_parsed() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let spdx = spdx_from_tag_value(&file).unwrap();
+
+ assert_eq!(spdx.package_information.len(), 4);
+ assert_eq!(spdx.file_information.len(), 4);
+ assert_eq!(spdx.snippet_information.len(), 1);
+ assert_eq!(spdx.relationships.len(), 11);
+ assert_eq!(spdx.annotations.len(), 5);
+ assert_eq!(spdx.other_licensing_information_detected.len(), 5);
+ }
+}
diff --git a/vendor/spdx-rs/src/parsers/tag_value.rs b/vendor/spdx-rs/src/parsers/tag_value.rs
new file mode 100644
index 000000000..0c8fdb4e3
--- /dev/null
+++ b/vendor/spdx-rs/src/parsers/tag_value.rs
@@ -0,0 +1,669 @@
+// SPDX-FileCopyrightText: 2021 HH Partners
+//
+// SPDX-License-Identifier: MIT
+
+use std::{num::ParseIntError, str::FromStr};
+
+use nom::{
+ branch::alt,
+ bytes::complete::{tag, take_until, take_while},
+ character::complete::{alphanumeric0, char, digit1, multispace0, not_line_ending},
+ combinator::{map, map_res, opt},
+ error::{ParseError, VerboseError},
+ multi::many0,
+ sequence::{delimited, preceded, separated_pair, tuple},
+ AsChar, IResult,
+};
+
+use crate::models::{
+ Algorithm, AnnotationType, Checksum, ExternalDocumentReference, ExternalPackageReference,
+ ExternalPackageReferenceCategory, FileType, PackageVerificationCode, Relationship,
+ RelationshipType,
+};
+
+#[derive(Debug, Clone, PartialEq)]
+#[allow(clippy::upper_case_acronyms)]
+pub(super) enum Atom {
+ // Document Creation Information
+ SpdxVersion(String),
+ DataLicense(String),
+ SPDXID(String),
+ DocumentName(String),
+ DocumentNamespace(String),
+ ExternalDocumentRef(ExternalDocumentReference),
+ LicenseListVersion(String),
+ Creator(String),
+ Created(String),
+ CreatorComment(String),
+ DocumentComment(String),
+
+ // Package Information
+ PackageName(String),
+ PackageVersion(String),
+ PackageFileName(String),
+ PackageSupplier(String),
+ PackageOriginator(String),
+ PackageDownloadLocation(String),
+ FilesAnalyzed(String),
+ PackageVerificationCode(PackageVerificationCode),
+ PackageChecksum(Checksum),
+ PackageHomePage(String),
+ PackageSourceInfo(String),
+ PackageLicenseConcluded(String),
+ PackageLicenseInfoFromFiles(String),
+ PackageLicenseDeclared(String),
+ PackageLicenseComments(String),
+ PackageCopyrightText(String),
+ PackageSummary(String),
+ PackageDescription(String),
+ PackageComment(String),
+ ExternalRef(ExternalPackageReference),
+ ExternalRefComment(String),
+ PackageAttributionText(String),
+
+ // File Information
+ FileName(String),
+ FileType(FileType),
+ FileChecksum(Checksum),
+ LicenseConcluded(String),
+ LicenseInfoInFile(String),
+ LicenseComments(String),
+ FileCopyrightText(String),
+ FileComment(String),
+ FileNotice(String),
+ FileContributor(String),
+ FileAttributionText(String),
+
+ // Snippet Information
+ SnippetSPDXID(String),
+ SnippetFromFileSPDXID(String),
+ SnippetByteRange((i32, i32)),
+ SnippetLineRange((i32, i32)),
+ SnippetLicenseConcluded(String),
+ LicenseInfoInSnippet(String),
+ SnippetLicenseComments(String),
+ SnippetCopyrightText(String),
+ SnippetComment(String),
+ SnippetName(String),
+ SnippetAttributionText(String),
+
+ // Other Licensing Information Detected
+ LicenseID(String),
+ ExtractedText(String),
+ LicenseName(String),
+ LicenseCrossReference(String),
+ LicenseComment(String),
+
+ // Relationship
+ Relationship(Relationship),
+ RelationshipComment(String),
+
+ // Annotation
+ Annotator(String),
+ AnnotationDate(String),
+ AnnotationType(AnnotationType),
+ SPDXREF(String),
+ AnnotationComment(String),
+
+ /// Comment in the document. Not part of the final SPDX.
+ TVComment(String),
+}
+
+pub(super) fn atoms(i: &str) -> IResult<&str, Vec<Atom>, VerboseError<&str>> {
+ many0(alt((ws(tv_comment), ws(tag_value_to_atom))))(i)
+}
+
+fn tag_value_to_atom(i: &str) -> IResult<&str, Atom, VerboseError<&str>> {
+ let (i, key_value) = tag_value(i)?;
+ match key_value.0 {
+ // Document Creation Information
+ "SPDXVersion" => Ok((i, Atom::SpdxVersion(key_value.1.to_string()))),
+ "DataLicense" => Ok((i, Atom::DataLicense(key_value.1.to_string()))),
+ "SPDXID" => Ok((i, Atom::SPDXID(key_value.1.to_string()))),
+ "DocumentName" => Ok((i, Atom::DocumentName(key_value.1.to_string()))),
+ "DocumentNamespace" => Ok((i, Atom::DocumentNamespace(key_value.1.to_string()))),
+ "ExternalDocumentRef" => {
+ let (_, value) = external_document_reference(key_value.1)?;
+ Ok((i, Atom::ExternalDocumentRef(value)))
+ }
+ "LicenseListVersion" => Ok((i, Atom::LicenseListVersion(key_value.1.to_string()))),
+ "Creator" => Ok((i, Atom::Creator(key_value.1.to_string()))),
+ "Created" => Ok((i, Atom::Created(key_value.1.to_string()))),
+ "CreatorComment" => Ok((i, Atom::CreatorComment(key_value.1.to_string()))),
+ "DocumentComment" => Ok((i, Atom::DocumentComment(key_value.1.to_string()))),
+
+ // Package Information
+ "PackageName" => Ok((i, Atom::PackageName(key_value.1.to_string()))),
+ "PackageVersion" => Ok((i, Atom::PackageVersion(key_value.1.to_string()))),
+ "PackageFileName" => Ok((i, Atom::PackageFileName(key_value.1.to_string()))),
+ "PackageSupplier" => Ok((i, Atom::PackageSupplier(key_value.1.to_string()))),
+ "PackageOriginator" => Ok((i, Atom::PackageOriginator(key_value.1.to_string()))),
+ "PackageDownloadLocation" => {
+ Ok((i, Atom::PackageDownloadLocation(key_value.1.to_string())))
+ }
+ "FilesAnalyzed" => Ok((i, Atom::FilesAnalyzed(key_value.1.to_string()))),
+ "PackageVerificationCode" => {
+ let (_, value) = package_verification_code(key_value.1)?;
+ Ok((i, Atom::PackageVerificationCode(value)))
+ }
+ "PackageChecksum" => Ok((i, Atom::PackageChecksum(checksum(key_value.1)?.1))),
+ "PackageHomePage" => Ok((i, Atom::PackageHomePage(key_value.1.to_string()))),
+ "PackageSourceInfo" => Ok((i, Atom::PackageSourceInfo(key_value.1.to_string()))),
+ "PackageLicenseConcluded" => {
+ Ok((i, Atom::PackageLicenseConcluded(key_value.1.to_string())))
+ }
+ "PackageLicenseInfoFromFiles" => Ok((
+ i,
+ Atom::PackageLicenseInfoFromFiles(key_value.1.to_string()),
+ )),
+ "PackageLicenseDeclared" => Ok((i, Atom::PackageLicenseDeclared(key_value.1.to_string()))),
+ "PackageLicenseComments" => Ok((i, Atom::PackageLicenseComments(key_value.1.to_string()))),
+ "PackageCopyrightText" => Ok((i, Atom::PackageCopyrightText(key_value.1.to_string()))),
+ "PackageSummary" => Ok((i, Atom::PackageSummary(key_value.1.to_string()))),
+ "PackageDescription" => Ok((i, Atom::PackageDescription(key_value.1.to_string()))),
+ "PackageComment" => Ok((i, Atom::PackageComment(key_value.1.to_string()))),
+ "ExternalRef" => Ok((
+ i,
+ Atom::ExternalRef(external_package_reference(key_value.1)?.1),
+ )),
+ "ExternalRefComment" => Ok((i, Atom::ExternalRefComment(key_value.1.to_string()))),
+ "PackageAttributionText" => Ok((i, Atom::PackageAttributionText(key_value.1.to_string()))),
+
+ // File Information
+ "FileName" => Ok((i, Atom::FileName(key_value.1.to_string()))),
+ "FileType" => Ok((i, Atom::FileType(file_type(key_value.1)?.1))),
+ "FileChecksum" => Ok((i, Atom::FileChecksum(checksum(key_value.1)?.1))),
+ "LicenseConcluded" => Ok((i, Atom::LicenseConcluded(key_value.1.to_string()))),
+ "LicenseInfoInFile" => Ok((i, Atom::LicenseInfoInFile(key_value.1.to_string()))),
+ "LicenseComments" => Ok((i, Atom::LicenseComments(key_value.1.to_string()))),
+ "FileCopyrightText" => Ok((i, Atom::FileCopyrightText(key_value.1.to_string()))),
+ "FileComment" => Ok((i, Atom::FileComment(key_value.1.to_string()))),
+ "FileNotice" => Ok((i, Atom::FileNotice(key_value.1.to_string()))),
+ "FileContributor" => Ok((i, Atom::FileContributor(key_value.1.to_string()))),
+ "FileAttributionText" => Ok((i, Atom::FileAttributionText(key_value.1.to_string()))),
+
+ // Snippet Information
+ "SnippetSPDXID" => Ok((i, Atom::SnippetSPDXID(key_value.1.to_string()))),
+ "SnippetFromFileSPDXID" => Ok((i, Atom::SnippetFromFileSPDXID(key_value.1.to_string()))),
+ "SnippetByteRange" => Ok((i, Atom::SnippetByteRange(range(key_value.1)?.1))),
+ "SnippetLineRange" => Ok((i, Atom::SnippetLineRange(range(key_value.1)?.1))),
+ "SnippetLicenseConcluded" => {
+ Ok((i, Atom::SnippetLicenseConcluded(key_value.1.to_string())))
+ }
+ "LicenseInfoInSnippet" => Ok((i, Atom::LicenseInfoInSnippet(key_value.1.to_string()))),
+ "SnippetLicenseComments" => Ok((i, Atom::SnippetLicenseComments(key_value.1.to_string()))),
+ "SnippetCopyrightText" => Ok((i, Atom::SnippetCopyrightText(key_value.1.to_string()))),
+ "SnippetComment" => Ok((i, Atom::SnippetComment(key_value.1.to_string()))),
+ "SnippetName" => Ok((i, Atom::SnippetName(key_value.1.to_string()))),
+ "SnippetAttributionText" => Ok((i, Atom::SnippetAttributionText(key_value.1.to_string()))),
+
+ // Other Licensing Information Detected
+ "LicenseID" => Ok((i, Atom::LicenseID(key_value.1.to_string()))),
+ "ExtractedText" => Ok((i, Atom::ExtractedText(key_value.1.to_string()))),
+ "LicenseName" => Ok((i, Atom::LicenseName(key_value.1.to_string()))),
+ "LicenseCrossReference" => Ok((i, Atom::LicenseCrossReference(key_value.1.to_string()))),
+ "LicenseComment" => Ok((i, Atom::LicenseComment(key_value.1.to_string()))),
+
+ // Relationship
+ "Relationship" => Ok((i, Atom::Relationship(relationship(key_value.1)?.1))),
+ "RelationshipComment" => Ok((i, Atom::RelationshipComment(key_value.1.to_string()))),
+
+ // Annotation
+ "Annotator" => Ok((i, Atom::Annotator(key_value.1.to_string()))),
+ "AnnotationDate" => Ok((i, Atom::AnnotationDate(key_value.1.to_string()))),
+ "AnnotationType" => Ok((i, Atom::AnnotationType(annotation_type(key_value.1)?.1))),
+ "SPDXREF" => Ok((i, Atom::SPDXREF(key_value.1.to_string()))),
+ "AnnotationComment" => Ok((i, Atom::AnnotationComment(key_value.1.to_string()))),
+ v => {
+ dbg!(v);
+ unimplemented!()
+ }
+ }
+}
+
+fn external_document_reference(
+ i: &str,
+) -> IResult<&str, ExternalDocumentReference, VerboseError<&str>> {
+ map(
+ tuple((
+ document_ref,
+ ws(take_while(|c: char| !c.is_whitespace())),
+ ws(checksum),
+ )),
+ |(id_string, spdx_document_uri, checksum)| {
+ ExternalDocumentReference::new(
+ id_string.to_string(),
+ spdx_document_uri.to_string(),
+ checksum,
+ )
+ },
+ )(i)
+}
+
+fn annotation_type(i: &str) -> IResult<&str, AnnotationType, VerboseError<&str>> {
+ match ws(not_line_ending)(i) {
+ Ok((i, value)) => match value {
+ "REVIEW" => Ok((i, AnnotationType::Review)),
+ "OTHER" => Ok((i, AnnotationType::Other)),
+ // Proper error
+ _ => todo!(),
+ },
+ Err(err) => Err(err),
+ }
+}
+
+fn file_type(i: &str) -> IResult<&str, FileType, VerboseError<&str>> {
+ match ws(not_line_ending)(i) {
+ Ok((i, value)) => match value {
+ "SOURCE" => Ok((i, FileType::Source)),
+ "BINARY" => Ok((i, FileType::Binary)),
+ "ARCHIVE" => Ok((i, FileType::Archive)),
+ "APPLICATION" => Ok((i, FileType::Application)),
+ "AUDIO" => Ok((i, FileType::Audio)),
+ "IMAGE" => Ok((i, FileType::Image)),
+ "TEXT" => Ok((i, FileType::Text)),
+ "VIDEO" => Ok((i, FileType::Video)),
+ "DOCUMENTATION" => Ok((i, FileType::Documentation)),
+ "SPDX" => Ok((i, FileType::SPDX)),
+ "OTHER" => Ok((i, FileType::Other)),
+ // Proper error
+ _ => todo!(),
+ },
+ Err(err) => Err(err),
+ }
+}
+
+fn document_ref<'a>(i: &'a str) -> IResult<&'a str, &str, VerboseError<&'a str>> {
+ preceded(tag("DocumentRef-"), ws(idstring))(i)
+}
+
+fn relationship(i: &str) -> IResult<&str, Relationship, VerboseError<&str>> {
+ map(
+ tuple((
+ ws(take_while(|c: char| !c.is_whitespace())),
+ ws(take_while(|c: char| !c.is_whitespace())),
+ ws(not_line_ending),
+ )),
+ |(item1, relationship_type, item2)| {
+ let relationship_type = relationship_type.to_uppercase();
+ let relationship_type = match relationship_type.as_str() {
+ "DESCRIBES" => RelationshipType::Describes,
+ "DESCRIBED_BY" => RelationshipType::DescribedBy,
+ "CONTAINS" => RelationshipType::Contains,
+ "CONTAINED_BY" => RelationshipType::ContainedBy,
+ "DEPENDS_ON" => RelationshipType::DependsOn,
+ "DEPENDENCY_OF" => RelationshipType::DependencyOf,
+ "DEPENDENCY_MANIFEST_OF" => RelationshipType::DependencyManifestOf,
+ "BUILD_DEPENDENCY_OF" => RelationshipType::BuildDependencyOf,
+ "DEV_DEPENDENCY_OF" => RelationshipType::DevDependencyOf,
+ "OPTIONAL_DEPENDENCY_OF" => RelationshipType::OptionalDependencyOf,
+ "PROVIDED_DEPENDENCY_OF" => RelationshipType::ProvidedDependencyOf,
+ "TEST_DEPENDENCY_OF" => RelationshipType::TestDependencyOf,
+ "RUNTIME_DEPENDENCY_OF" => RelationshipType::RuntimeDependencyOf,
+ "EXAMPLE_OF" => RelationshipType::ExampleOf,
+ "GENERATES" => RelationshipType::Generates,
+ "GENERATED_FROM" => RelationshipType::GeneratedFrom,
+ "ANCESTOR_OF" => RelationshipType::AncestorOf,
+ "DESCENDANT_OF" => RelationshipType::DescendantOf,
+ "VARIANT_OF" => RelationshipType::VariantOf,
+ "DISTRIBUTION_ARTIFACT" => RelationshipType::DistributionArtifact,
+ "PATCH_FOR" => RelationshipType::PatchFor,
+ "PATCH_APPLIED" => RelationshipType::PatchApplied,
+ "COPY_OF" => RelationshipType::CopyOf,
+ "FILE_ADDED" => RelationshipType::FileAdded,
+ "FILE_DELETED" => RelationshipType::FileDeleted,
+ "FILE_MODIFIED" => RelationshipType::FileModified,
+ "EXPANDED_FROM_ARCHIVE" => RelationshipType::ExpandedFromArchive,
+ "DYNAMIC_LINK" => RelationshipType::DynamicLink,
+ "STATIC_LINK" => RelationshipType::StaticLink,
+ "DATA_FILE_OF" => RelationshipType::DataFileOf,
+ "TEST_CASE_OF" => RelationshipType::TestCaseOf,
+ "BUILD_TOOL_OF" => RelationshipType::BuildToolOf,
+ "DEV_TOOL_OF" => RelationshipType::DevToolOf,
+ "TEST_OF" => RelationshipType::TestOf,
+ "TEST_TOOL_OF" => RelationshipType::TestToolOf,
+ "DOCUMENTATION_OF" => RelationshipType::DocumentationOf,
+ "OPTIONAL_COMPONENT_OF" => RelationshipType::OptionalComponentOf,
+ "METAFILE_OF" => RelationshipType::MetafileOf,
+ "PACKAGE_OF" => RelationshipType::PackageOf,
+ "AMENDS" => RelationshipType::Amends,
+ "PREREQUISITE_FOR" => RelationshipType::PrerequisiteFor,
+ "HAS_PREREQUISITE" => RelationshipType::HasPrerequisite,
+ "OTHER" => RelationshipType::Other,
+ // TODO: Proper error.
+ _ => {
+ dbg!(relationship_type);
+ todo!()
+ }
+ };
+ Relationship::new(item1, item2, relationship_type, None)
+ },
+ )(i)
+}
+
+fn external_package_reference(
+ i: &str,
+) -> IResult<&str, ExternalPackageReference, VerboseError<&str>> {
+ map(
+ tuple((
+ ws(take_while(|c: char| !c.is_whitespace())),
+ ws(take_while(|c: char| !c.is_whitespace())),
+ ws(not_line_ending),
+ )),
+ |(category, ref_type, locator)| {
+ let category = match category {
+ "SECURITY" => ExternalPackageReferenceCategory::Security,
+ "PACKAGE-MANAGER" => ExternalPackageReferenceCategory::PackageManager,
+ "PERSISTENT-ID" => ExternalPackageReferenceCategory::PersistentID,
+ "OTHER" => ExternalPackageReferenceCategory::Other,
+ // TODO: Proper error handling
+ _ => todo!(),
+ };
+ ExternalPackageReference::new(category, ref_type.to_string(), locator.to_string(), None)
+ },
+ )(i)
+}
+
+fn package_verification_code(
+ i: &str,
+) -> IResult<&str, PackageVerificationCode, VerboseError<&str>> {
+ map(
+ alt((
+ separated_pair(
+ ws(take_until("(excludes:")),
+ ws(tag("(excludes:")),
+ opt(take_until(")")),
+ ),
+ map(ws(not_line_ending), |v| (v, None)),
+ )),
+ |(value, exclude)| {
+ #[allow(clippy::option_if_let_else)]
+ let excludes = if let Some(exclude) = exclude {
+ vec![exclude.to_string()]
+ } else {
+ Vec::new()
+ };
+ PackageVerificationCode::new(value.to_string(), excludes)
+ },
+ )(i)
+}
+
+fn range(i: &str) -> IResult<&str, (i32, i32), VerboseError<&str>> {
+ map_res::<_, _, _, _, ParseIntError, _, _>(
+ separated_pair(digit1, char(':'), digit1),
+ |(left, right)| Ok((i32::from_str(left)?, i32::from_str(right)?)),
+ )(i)
+}
+
+fn idstring<'a>(i: &'a str) -> IResult<&'a str, &str, VerboseError<&'a str>> {
+ take_while(|c: char| c.is_alphanum() || c == '.' || c == '-' || c == '+')(i)
+}
+
+fn checksum(i: &str) -> IResult<&str, Checksum, VerboseError<&str>> {
+ map(
+ separated_pair(ws(take_until(":")), char(':'), ws(not_line_ending)),
+ |(algorithm, value)| {
+ let checksum_algorithm = match algorithm {
+ "SHA1" => Algorithm::SHA1,
+ "SHA224" => Algorithm::SHA224,
+ "SHA256" => Algorithm::SHA256,
+ "SHA384" => Algorithm::SHA384,
+ "SHA512" => Algorithm::SHA512,
+ "MD2" => Algorithm::MD2,
+ "MD4" => Algorithm::MD4,
+ "MD5" => Algorithm::MD5,
+ "MD6" => Algorithm::MD6,
+ // TODO: Use proper error.
+ _ => todo!(),
+ };
+ Checksum::new(checksum_algorithm, value)
+ },
+ )(i)
+}
+
+fn tv_comment(i: &str) -> IResult<&str, Atom, VerboseError<&str>> {
+ map(preceded(ws(tag("#")), ws(not_line_ending)), |v| {
+ Atom::TVComment(v.to_string())
+ })(i)
+}
+
+fn tag_value<'a>(i: &'a str) -> IResult<&'a str, (&str, &str), VerboseError<&'a str>> {
+ separated_pair(
+ ws(alphanumeric0),
+ tag(":"),
+ alt((ws(multiline_text), ws(not_line_ending))),
+ )(i)
+}
+
+fn multiline_text<'a>(i: &'a str) -> IResult<&'a str, &str, VerboseError<&'a str>> {
+ delimited(tag("<text>"), take_until("</text>"), tag("</text>"))(i)
+}
+
+/// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and
+/// trailing whitespace, returning the output of `inner`.
+fn ws<'a, F, O, E: ParseError<&'a str>>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
+where
+ F: Fn(&'a str) -> IResult<&'a str, O, E> + 'a,
+{
+ delimited(multispace0, inner, multispace0)
+}
+
+#[cfg(test)]
+mod tests {
+ use std::fs::read_to_string;
+
+ use crate::{
+ models::{Algorithm, AnnotationType, ExternalPackageReferenceCategory, Relationship},
+ parsers::tag_value::{
+ annotation_type, checksum, document_ref, external_document_reference,
+ external_package_reference, package_verification_code, range, relationship,
+ },
+ };
+
+ use super::{atoms, tag_value, tag_value_to_atom, Atom};
+
+ #[test]
+ fn version_can_be_parsed() {
+ let (_, value) = tag_value_to_atom("SPDXVersion: SPDX-1.2").unwrap();
+ assert_eq!(value, Atom::SpdxVersion("SPDX-1.2".to_string()));
+ }
+
+ #[test]
+ fn range_can_be_parsed() {
+ let (_, value) = range("310:420").unwrap();
+ assert_eq!(value, (310, 420));
+ }
+
+ #[test]
+ fn annotation_type_can_be_parsed() {
+ let (_, value) = annotation_type("REVIEW").unwrap();
+ assert_eq!(value, AnnotationType::Review);
+ let (_, value) = annotation_type("OTHER").unwrap();
+ assert_eq!(value, AnnotationType::Other);
+ }
+
+ #[test]
+ fn relationship_can_be_parsed() {
+ let (_, value) = relationship("SPDXRef-JenaLib CONTAINS SPDXRef-Package").unwrap();
+ let expected = Relationship::new(
+ "SPDXRef-JenaLib",
+ "SPDXRef-Package",
+ crate::models::RelationshipType::Contains,
+ None,
+ );
+ assert_eq!(value, expected);
+ }
+
+ #[test]
+ fn data_license_can_be_parsed() {
+ let (_, value) = tag_value_to_atom("DataLicense: CC0-1.0").unwrap();
+ assert_eq!(value, Atom::DataLicense("CC0-1.0".to_string()));
+ }
+
+ #[test]
+ fn package_verification_code_can_be_parsed() {
+ let (_, value) = package_verification_code(
+ "d6a770ba38583ed4bb4525bd96e50461655d2758(excludes: ./package.spdx)",
+ )
+ .unwrap();
+ assert_eq!(value.value, "d6a770ba38583ed4bb4525bd96e50461655d2758");
+ assert_eq!(value.excludes, vec!["./package.spdx"]);
+ }
+
+ #[test]
+ fn package_verification_code_without_excludes_can_be_parsed() {
+ let (_, value) =
+ package_verification_code("d6a770ba38583ed4bb4525bd96e50461655d2758").unwrap();
+ assert_eq!(value.value, "d6a770ba38583ed4bb4525bd96e50461655d2758");
+ let expected: Vec<String> = Vec::new();
+ assert_eq!(value.excludes, expected);
+ }
+
+ #[test]
+ fn external_package_ref_can_be_parsed() {
+ let (_, value) = external_package_reference(
+ "SECURITY cpe23Type cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*",
+ )
+ .unwrap();
+ assert_eq!(
+ value.reference_category,
+ ExternalPackageReferenceCategory::Security
+ );
+ assert_eq!(value.reference_type, "cpe23Type");
+ assert_eq!(
+ value.reference_locator,
+ "cpe:2.3:a:pivotal_software:spring_framework:4.1.0:*:*:*:*:*:*:*"
+ );
+ }
+
+ #[test]
+ fn external_document_reference_can_be_parsed() {
+ let (_, value) = external_document_reference("DocumentRef-spdx-tool-1.2 http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301 SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759").unwrap();
+ assert_eq!(value.id_string, "spdx-tool-1.2");
+ assert_eq!(
+ value.spdx_document_uri,
+ "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301"
+ );
+ assert_eq!(value.checksum.algorithm, Algorithm::SHA1);
+ assert_eq!(
+ value.checksum.value,
+ "d6a770ba38583ed4bb4525bd96e50461655d2759"
+ );
+ }
+
+ #[test]
+ fn document_ref_can_be_parsed() {
+ let (_, value) = document_ref("DocumentRef-spdx-tool-1.2").unwrap();
+ assert_eq!(value, "spdx-tool-1.2");
+ }
+
+ #[test]
+ fn checksum_can_be_parsed() {
+ let (_, value) = checksum("SHA1: d6a770ba38583ed4bb4525bd96e50461655d2759").unwrap();
+ assert_eq!(value.algorithm, Algorithm::SHA1);
+ assert_eq!(value.value, "d6a770ba38583ed4bb4525bd96e50461655d2759");
+ }
+
+ #[test]
+ fn document_comment_can_be_parsed() {
+ let (_, value) = tag_value_to_atom("DocumentComment: <text>Sample Comment</text>").unwrap();
+ assert_eq!(value, Atom::DocumentComment("Sample Comment".to_string()));
+ }
+
+ #[test]
+ fn multiline_document_comment_can_be_parsed() {
+ let (_, value) = tag_value_to_atom(
+ "DocumentComment: <text>Sample
+Comment</text>",
+ )
+ .unwrap();
+ assert_eq!(value, Atom::DocumentComment("Sample\nComment".to_string()));
+ }
+
+ #[test]
+ fn multiple_key_values_can_be_parsed() {
+ let input = "SPDXVersion: SPDX-1.2
+ DataLicense: CC0-1.0
+ DocumentComment: <text>Sample Comment</text>";
+
+ let (_, value) = atoms(input).unwrap();
+ assert_eq!(
+ value,
+ vec![
+ Atom::SpdxVersion("SPDX-1.2".to_string()),
+ Atom::DataLicense("CC0-1.0".to_string()),
+ Atom::DocumentComment("Sample Comment".to_string())
+ ]
+ );
+ }
+
+ #[test]
+ fn multiple_key_values_with_comment_can_be_parsed() {
+ let input = "SPDXVersion: SPDX-1.2
+ # A comment
+ DataLicense: CC0-1.0
+ DocumentComment: <text>Sample Comment</text>";
+
+ let (_, value) = atoms(input).unwrap();
+ assert_eq!(
+ value,
+ vec![
+ Atom::SpdxVersion("SPDX-1.2".to_string()),
+ Atom::TVComment("A comment".to_string()),
+ Atom::DataLicense("CC0-1.0".to_string()),
+ Atom::DocumentComment("Sample Comment".to_string())
+ ]
+ );
+ }
+
+ #[test]
+ fn multiple_key_values_with_space_can_be_parsed() {
+ let input = "SPDXVersion: SPDX-1.2
+
+ DataLicense: CC0-1.0
+ DocumentComment: <text>Sample Comment</text>";
+
+ let (_, value) = atoms(input).unwrap();
+ assert_eq!(
+ value,
+ vec![
+ Atom::SpdxVersion("SPDX-1.2".to_string()),
+ Atom::DataLicense("CC0-1.0".to_string()),
+ Atom::DocumentComment("Sample Comment".to_string())
+ ]
+ );
+ }
+
+ #[test]
+ fn key_value_pair_is_detected() {
+ let (_, value) = tag_value("SPDXVersion: SPDX-1.2").unwrap();
+ assert_eq!(value, ("SPDXVersion", "SPDX-1.2"));
+ }
+
+ #[test]
+ fn get_tag_values_from_simple_example_file() {
+ let file = read_to_string("tests/data/SPDXSimpleTag.tag").unwrap();
+ let (remains, result) = atoms(&file).unwrap();
+ assert_eq!(remains.len(), 0);
+ assert!(result.contains(&Atom::SpdxVersion("SPDX-1.2".to_string())));
+ assert!(result.contains(&Atom::PackageName("Test".to_string())));
+ assert!(result.contains(&Atom::PackageDescription("A package.".to_string())));
+ }
+
+ #[test]
+ fn get_tag_values_from_example_file() {
+ let file = read_to_string("tests/data/SPDXTagExample-v2.2.spdx").unwrap();
+ let (remains, result) = atoms(&file).unwrap();
+ assert_eq!(remains.len(), 0);
+ assert!(result.contains(&Atom::SpdxVersion("SPDX-2.2".to_string())));
+ assert!(result.contains(&Atom::LicenseListVersion("3.9".to_string())));
+ assert!(result.contains(&Atom::PackageLicenseDeclared("MPL-1.0".to_string())));
+ }
+
+ #[test]
+ fn relationship_case() {
+ relationship("SPDXRef-DOCUMENT DESCRIBES SPDXRef-File").expect("Caps is expected");
+ relationship("SPDXRef-DOCUMENT describes SPDXRef-File")
+ .expect("At least reuse-tool emits lowercase");
+ }
+}