diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:21 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:18:21 +0000 |
commit | 4e8199b572f2035b7749cba276ece3a26630d23e (patch) | |
tree | f09feeed6a0fe39d027b1908aa63ea6b35e4b631 /vendor/spdx-rs/src/parsers/tag_value.rs | |
parent | Adding upstream version 1.66.0+dfsg1. (diff) | |
download | rustc-4e8199b572f2035b7749cba276ece3a26630d23e.tar.xz rustc-4e8199b572f2035b7749cba276ece3a26630d23e.zip |
Adding upstream version 1.67.1+dfsg1.upstream/1.67.1+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/spdx-rs/src/parsers/tag_value.rs')
-rw-r--r-- | vendor/spdx-rs/src/parsers/tag_value.rs | 669 |
1 files changed, 669 insertions, 0 deletions
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"); + } +} |