1use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7use super::datasource_id::OutputDatasourceId;
8use super::license_detection::OutputLicenseDetection;
9use super::package_type::OutputPackageType;
10use super::party::OutputParty;
11use super::serde_helpers::serialize_optional_map_as_object;
12
13#[derive(Serialize, Deserialize, Debug, Clone)]
14pub struct OutputPackage {
15 #[serde(rename = "type")]
16 pub package_type: Option<OutputPackageType>,
17 pub namespace: Option<String>,
18 pub name: Option<String>,
19 pub version: Option<String>,
20 #[serde(default, serialize_with = "serialize_optional_map_as_object")]
21 pub qualifiers: Option<HashMap<String, String>>,
22 pub subpath: Option<String>,
23 pub primary_language: Option<String>,
24 pub description: Option<String>,
25 pub release_date: Option<String>,
26 #[serde(default)]
27 pub parties: Vec<OutputParty>,
28 #[serde(default)]
29 pub keywords: Vec<String>,
30 pub homepage_url: Option<String>,
31 pub download_url: Option<String>,
32 pub size: Option<u64>,
33 pub sha1: Option<String>,
34 pub md5: Option<String>,
35 pub sha256: Option<String>,
36 pub sha512: Option<String>,
37 pub bug_tracking_url: Option<String>,
38 pub code_view_url: Option<String>,
39 pub vcs_url: Option<String>,
40 pub copyright: Option<String>,
41 pub holder: Option<String>,
42 pub declared_license_expression: Option<String>,
43 pub declared_license_expression_spdx: Option<String>,
44 #[serde(default)]
45 pub license_detections: Vec<OutputLicenseDetection>,
46 pub other_license_expression: Option<String>,
47 pub other_license_expression_spdx: Option<String>,
48 #[serde(default)]
49 pub other_license_detections: Vec<OutputLicenseDetection>,
50 pub extracted_license_statement: Option<String>,
51 pub notice_text: Option<String>,
52 #[serde(default)]
53 pub source_packages: Vec<String>,
54 #[serde(default)]
55 pub is_private: bool,
56 #[serde(default)]
57 pub is_virtual: bool,
58 #[serde(default, serialize_with = "serialize_optional_map_as_object")]
59 pub extra_data: Option<HashMap<String, serde_json::Value>>,
60 pub repository_homepage_url: Option<String>,
61 pub repository_download_url: Option<String>,
62 pub api_data_url: Option<String>,
63 pub purl: Option<String>,
64 pub package_uid: String,
65 pub datafile_paths: Vec<String>,
66 pub datasource_ids: Vec<OutputDatasourceId>,
67}
68
69impl From<&crate::models::Package> for OutputPackage {
70 fn from(value: &crate::models::Package) -> Self {
71 Self {
72 package_type: value.package_type.map(OutputPackageType::from),
73 namespace: value.namespace.clone(),
74 name: value.name.clone(),
75 version: value.version.clone(),
76 qualifiers: value.qualifiers.clone(),
77 subpath: value.subpath.clone(),
78 primary_language: value.primary_language.clone(),
79 description: value.description.clone(),
80 release_date: value.release_date.clone(),
81 parties: value.parties.iter().map(OutputParty::from).collect(),
82 keywords: value.keywords.clone(),
83 homepage_url: value.homepage_url.clone(),
84 download_url: value.download_url.clone(),
85 size: value.size,
86 sha1: value.sha1.as_ref().map(|d| d.as_hex()),
87 md5: value.md5.as_ref().map(|d| d.as_hex()),
88 sha256: value.sha256.as_ref().map(|d| d.as_hex()),
89 sha512: value.sha512.as_ref().map(|d| d.as_hex()),
90 bug_tracking_url: value.bug_tracking_url.clone(),
91 code_view_url: value.code_view_url.clone(),
92 vcs_url: value.vcs_url.clone(),
93 copyright: value.copyright.clone(),
94 holder: value.holder.clone(),
95 declared_license_expression: value.declared_license_expression.clone(),
96 declared_license_expression_spdx: value.declared_license_expression_spdx.clone(),
97 license_detections: value
98 .license_detections
99 .iter()
100 .map(OutputLicenseDetection::from)
101 .collect(),
102 other_license_expression: value.other_license_expression.clone(),
103 other_license_expression_spdx: value.other_license_expression_spdx.clone(),
104 other_license_detections: value
105 .other_license_detections
106 .iter()
107 .map(OutputLicenseDetection::from)
108 .collect(),
109 extracted_license_statement: value.extracted_license_statement.clone(),
110 notice_text: value.notice_text.clone(),
111 source_packages: value.source_packages.clone(),
112 is_private: value.is_private,
113 is_virtual: value.is_virtual,
114 extra_data: value.extra_data.clone(),
115 repository_homepage_url: value.repository_homepage_url.clone(),
116 repository_download_url: value.repository_download_url.clone(),
117 api_data_url: value.api_data_url.clone(),
118 purl: value.purl.as_deref().map(crate::models::normalize_purl),
119 package_uid: value.package_uid.to_string(),
120 datafile_paths: value.datafile_paths.clone(),
121 datasource_ids: value
122 .datasource_ids
123 .iter()
124 .map(OutputDatasourceId::from)
125 .collect(),
126 }
127 }
128}
129
130impl TryFrom<&OutputPackage> for crate::models::Package {
131 type Error = String;
132 fn try_from(value: &OutputPackage) -> Result<Self, Self::Error> {
133 let mut parties = Vec::with_capacity(value.parties.len());
134 for p in &value.parties {
135 parties.push(crate::models::Party::try_from(p)?);
136 }
137 let mut license_detections = Vec::with_capacity(value.license_detections.len());
138 for d in &value.license_detections {
139 license_detections.push(crate::models::LicenseDetection::try_from(d)?);
140 }
141 let mut other_license_detections = Vec::with_capacity(value.other_license_detections.len());
142 for d in &value.other_license_detections {
143 other_license_detections.push(crate::models::LicenseDetection::try_from(d)?);
144 }
145 Ok(Self {
146 package_type: value
147 .package_type
148 .as_ref()
149 .map(crate::models::PackageType::try_from)
150 .transpose()?,
151 namespace: value.namespace.clone(),
152 name: value.name.clone(),
153 version: value.version.clone(),
154 qualifiers: value.qualifiers.clone(),
155 subpath: value.subpath.clone(),
156 primary_language: value.primary_language.clone(),
157 description: value.description.clone(),
158 release_date: value.release_date.clone(),
159 parties,
160 keywords: value.keywords.clone(),
161 homepage_url: value.homepage_url.clone(),
162 download_url: value.download_url.clone(),
163 size: value.size,
164 sha1: value
165 .sha1
166 .as_ref()
167 .map(|s| crate::models::Sha1Digest::from_hex(s))
168 .transpose()
169 .map_err(|e| format!("invalid sha1: {}", e))?,
170 md5: value
171 .md5
172 .as_ref()
173 .map(|s| crate::models::Md5Digest::from_hex(s))
174 .transpose()
175 .map_err(|e| format!("invalid md5: {}", e))?,
176 sha256: value
177 .sha256
178 .as_ref()
179 .map(|s| crate::models::Sha256Digest::from_hex(s))
180 .transpose()
181 .map_err(|e| format!("invalid sha256: {}", e))?,
182 sha512: value
183 .sha512
184 .as_ref()
185 .map(|s| crate::models::Sha512Digest::from_hex(s))
186 .transpose()
187 .map_err(|e| format!("invalid sha512: {}", e))?,
188 bug_tracking_url: value.bug_tracking_url.clone(),
189 code_view_url: value.code_view_url.clone(),
190 vcs_url: value.vcs_url.clone(),
191 copyright: value.copyright.clone(),
192 holder: value.holder.clone(),
193 declared_license_expression: value.declared_license_expression.clone(),
194 declared_license_expression_spdx: value.declared_license_expression_spdx.clone(),
195 license_detections,
196 other_license_expression: value.other_license_expression.clone(),
197 other_license_expression_spdx: value.other_license_expression_spdx.clone(),
198 other_license_detections,
199 extracted_license_statement: value.extracted_license_statement.clone(),
200 notice_text: value.notice_text.clone(),
201 source_packages: value.source_packages.clone(),
202 is_private: value.is_private,
203 is_virtual: value.is_virtual,
204 extra_data: value.extra_data.clone(),
205 repository_homepage_url: value.repository_homepage_url.clone(),
206 repository_download_url: value.repository_download_url.clone(),
207 api_data_url: value.api_data_url.clone(),
208 purl: value.purl.clone(),
209 package_uid: crate::models::PackageUid::from_raw(value.package_uid.clone()),
210 datafile_paths: value.datafile_paths.clone(),
211 datasource_ids: value
212 .datasource_ids
213 .iter()
214 .map(crate::models::DatasourceId::try_from)
215 .collect::<Result<Vec<_>, _>>()
216 .map_err(|e| format!("invalid datasource_id: {}", e))?,
217 })
218 }
219}