1mod apps_and_features_entry;
2mod architecture;
3mod command;
4mod dependencies;
5mod elevation_requirement;
6mod expected_return_codes;
7mod file_extension;
8mod install_modes;
9mod installation_metadata;
10mod installer_return_code;
11mod installer_type;
12mod markets;
13mod minimum_os_version;
14mod nested;
15mod platform;
16mod protocol;
17mod repair_behavior;
18mod return_response;
19mod scope;
20pub mod switches;
21mod unsupported_arguments;
22mod unsupported_os_architectures;
23mod upgrade_behavior;
24
25use std::collections::BTreeSet;
26
27use chrono::NaiveDate;
28use const_format::formatc;
29use itertools::Itertools;
30use nested::installer_type::NestedInstallerType;
31use package_family_name::PackageFamilyName;
32use serde::{Deserialize, Serialize};
33use serde_with::skip_serializing_none;
34
35pub use crate::installer::{
36 apps_and_features_entry::AppsAndFeaturesEntry,
37 architecture::{Architecture, VALID_FILE_EXTENSIONS},
38 command::Command,
39 dependencies::Dependencies,
40 elevation_requirement::ElevationRequirement,
41 expected_return_codes::ExpectedReturnCodes,
42 file_extension::FileExtension,
43 install_modes::InstallModes,
44 installation_metadata::InstallationMetadata,
45 installer_return_code::{InstallerReturnCode, InstallerSuccessCode},
46 installer_type::InstallerType,
47 markets::Markets,
48 minimum_os_version::MinimumOSVersion,
49 nested::installer_files::NestedInstallerFiles,
50 platform::Platform,
51 protocol::Protocol,
52 repair_behavior::RepairBehavior,
53 scope::Scope,
54 switches::InstallerSwitches,
55 unsupported_arguments::UnsupportedArguments,
56 unsupported_os_architectures::UnsupportedOSArchitecture,
57 upgrade_behavior::UpgradeBehavior,
58};
59use crate::{
60 shared::{
61 LanguageTag, ManifestType, ManifestVersion, PackageIdentifier, PackageVersion,
62 Sha256String, url::DecodedUrl,
63 },
64 traits::Manifest,
65};
66
67#[skip_serializing_none]
68#[derive(Serialize, Deserialize, Default)]
69#[serde(rename_all = "PascalCase")]
70pub struct InstallerManifest {
71 pub package_identifier: PackageIdentifier,
72 pub package_version: PackageVersion,
73 pub channel: Option<String>,
74 #[serde(rename = "InstallerLocale")]
75 pub locale: Option<LanguageTag>,
76 pub platform: Option<Platform>,
77 #[serde(rename = "MinimumOSVersion")]
78 pub minimum_os_version: Option<MinimumOSVersion>,
79 #[serde(rename = "InstallerType")]
80 pub r#type: Option<InstallerType>,
81 pub nested_installer_type: Option<NestedInstallerType>,
82 pub nested_installer_files: Option<BTreeSet<NestedInstallerFiles>>,
83 pub scope: Option<Scope>,
84 pub install_modes: Option<InstallModes>,
85 #[serde(rename = "InstallerSwitches")]
86 pub switches: Option<InstallerSwitches>,
87 #[serde(rename = "InstallerSuccessCodes")]
88 pub success_codes: Option<BTreeSet<InstallerSuccessCode>>,
89 pub expected_return_codes: Option<BTreeSet<ExpectedReturnCodes>>,
90 pub upgrade_behavior: Option<UpgradeBehavior>,
91 pub commands: Option<BTreeSet<Command>>,
92 pub protocols: Option<BTreeSet<Protocol>>,
93 pub file_extensions: Option<BTreeSet<FileExtension>>,
94 pub dependencies: Option<Dependencies>,
95 pub package_family_name: Option<PackageFamilyName>,
96 pub product_code: Option<String>,
97 pub capabilities: Option<BTreeSet<String>>,
98 pub restricted_capabilities: Option<BTreeSet<String>>,
99 pub markets: Option<Markets>,
100 #[serde(rename = "InstallerAbortsTerminal")]
101 pub aborts_terminal: Option<bool>,
102 pub release_date: Option<NaiveDate>,
103 pub install_location_required: Option<bool>,
104 pub require_explicit_upgrade: Option<bool>,
105 pub display_install_warnings: Option<bool>,
106 #[serde(rename = "UnsupportedOSArchitectures")]
107 pub unsupported_os_architectures: Option<UnsupportedOSArchitecture>,
108 pub unsupported_arguments: Option<UnsupportedArguments>,
109 pub apps_and_features_entries: Option<Vec<AppsAndFeaturesEntry>>,
110 pub elevation_requirement: Option<ElevationRequirement>,
111 pub installation_metadata: Option<InstallationMetadata>,
112 pub download_command_prohibited: Option<bool>,
113 pub repair_behavior: Option<RepairBehavior>,
114 pub archive_binaries_depend_on_path: Option<bool>,
115 pub installers: Vec<Installer>,
116 pub manifest_type: ManifestType,
117 #[serde(default)]
118 pub manifest_version: ManifestVersion,
119}
120
121impl Manifest for InstallerManifest {
122 const SCHEMA: &'static str = formatc!(
123 "https://aka.ms/winget-manifest.installer.{}.schema.json",
124 ManifestVersion::DEFAULT
125 );
126 const TYPE: ManifestType = ManifestType::Installer;
127}
128
129impl InstallerManifest {
130 pub fn reorder_keys(
131 &mut self,
132 package_identifier: &PackageIdentifier,
133 package_version: &PackageVersion,
134 ) {
135 fn reorder_key<T>(
136 installers: &mut [Installer],
137 get_installer_key: impl Fn(&mut Installer) -> &mut Option<T>,
138 root_key: &mut Option<T>,
139 ) where
140 T: PartialEq,
141 {
142 if let Ok(value) = installers
143 .iter_mut()
144 .map(&get_installer_key)
145 .all_equal_value()
146 {
147 if value.is_some() {
148 *root_key = value.take();
149 installers
150 .iter_mut()
151 .for_each(|installer| *get_installer_key(installer) = None);
152 }
153 }
154 }
155
156 fn reorder_struct_key<T, R>(
157 installers: &mut [Installer],
158 get_installer_struct: impl Fn(&mut Installer) -> &mut Option<T>,
159 get_struct_key: impl Fn(&mut T) -> &mut Option<R>,
160 root_struct: &mut Option<T>,
161 ) where
162 T: Default,
163 R: PartialEq,
164 {
165 if let Ok(Some(common_value)) = installers
166 .iter_mut()
167 .map(&get_installer_struct)
168 .map(|r#struct| r#struct.as_mut().map(&get_struct_key))
169 .all_equal_value()
170 {
171 if common_value.is_some() {
172 *get_struct_key(root_struct.get_or_insert_default()) = common_value.take();
173
174 installers
175 .iter_mut()
176 .filter_map(|installer| get_installer_struct(installer).as_mut())
177 .for_each(|r#struct| *get_struct_key(r#struct) = None);
178 }
179 }
180 }
181
182 macro_rules! reorder_root_keys {
183 ($($field:ident),*) => {
184 $(
185 reorder_key(&mut self.installers, |installer| &mut installer.$field, &mut self.$field);
186 )*
187 };
188 }
189
190 macro_rules! reorder_struct_key {
191 ($struct:ident, $( $field:ident ),*) => {
192 $(
193 reorder_struct_key(&mut self.installers, |installer| &mut installer.$struct, |s| &mut s.$field, &mut self.$struct);
194 )*
195 self.installers.iter_mut().for_each(|installer| {
196 if let Some(r#struct) = &mut installer.$struct {
197 if !r#struct.is_any_some() {
198 installer.$struct = None;
199 }
200 }
201 });
202 };
203 }
204
205 self.package_identifier.clone_from(package_identifier);
206 self.package_version.clone_from(package_version);
207 reorder_root_keys!(
208 locale,
209 platform,
210 minimum_os_version,
211 r#type,
212 nested_installer_type,
213 nested_installer_files,
214 scope,
215 install_modes,
216 success_codes,
217 expected_return_codes,
218 upgrade_behavior,
219 commands,
220 protocols,
221 file_extensions,
222 dependencies,
223 package_family_name,
224 product_code,
225 capabilities,
226 restricted_capabilities,
227 markets,
228 aborts_terminal,
229 release_date,
230 install_location_required,
231 require_explicit_upgrade,
232 display_install_warnings,
233 unsupported_os_architectures,
234 unsupported_arguments,
235 apps_and_features_entries,
236 elevation_requirement,
237 installation_metadata,
238 download_command_prohibited,
239 repair_behavior,
240 archive_binaries_depend_on_path
241 );
242 reorder_struct_key!(
243 switches,
244 silent,
245 silent_with_progress,
246 interactive,
247 install_location,
248 log,
249 silent_with_progress,
250 upgrade,
251 custom,
252 repair
253 );
254
255 if self
256 .apps_and_features_entries
257 .as_ref()
258 .is_some_and(|entries| !entries.iter().any(AppsAndFeaturesEntry::is_any_some))
259 {
260 self.apps_and_features_entries = None;
261 }
262
263 self.manifest_version = ManifestVersion::default();
264
265 self.installers.sort_unstable();
266 self.installers.dedup();
267 }
268}
269
270#[skip_serializing_none]
271#[derive(Serialize, Deserialize, Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
272#[serde(rename_all = "PascalCase")]
273pub struct Installer {
274 #[serde(rename = "InstallerLocale")]
275 pub locale: Option<LanguageTag>,
276 pub platform: Option<Platform>,
277 #[serde(rename = "MinimumOSVersion")]
278 pub minimum_os_version: Option<MinimumOSVersion>,
279 pub architecture: Architecture,
280 #[serde(rename = "InstallerType")]
281 pub r#type: Option<InstallerType>,
282 pub nested_installer_type: Option<NestedInstallerType>,
283 pub nested_installer_files: Option<BTreeSet<NestedInstallerFiles>>,
284 pub scope: Option<Scope>,
285 #[serde(rename = "InstallerUrl")]
286 pub url: DecodedUrl,
287 #[serde(rename = "InstallerSha256")]
288 pub sha_256: Sha256String,
289 pub signature_sha_256: Option<Sha256String>,
290 pub install_modes: Option<InstallModes>,
291 #[serde(rename = "InstallerSwitches")]
292 pub switches: Option<InstallerSwitches>,
293 #[serde(rename = "InstallerSuccessCodes")]
294 pub success_codes: Option<BTreeSet<InstallerSuccessCode>>,
295 pub expected_return_codes: Option<BTreeSet<ExpectedReturnCodes>>,
296 pub upgrade_behavior: Option<UpgradeBehavior>,
297 pub commands: Option<BTreeSet<Command>>,
298 pub protocols: Option<BTreeSet<Protocol>>,
299 pub file_extensions: Option<BTreeSet<FileExtension>>,
300 pub dependencies: Option<Dependencies>,
301 pub package_family_name: Option<PackageFamilyName>,
302 pub product_code: Option<String>,
303 pub capabilities: Option<BTreeSet<String>>,
304 pub restricted_capabilities: Option<BTreeSet<String>>,
305 pub markets: Option<Markets>,
306 #[serde(rename = "InstallerAbortsTerminal")]
307 pub aborts_terminal: Option<bool>,
308 pub release_date: Option<NaiveDate>,
309 pub install_location_required: Option<bool>,
310 pub require_explicit_upgrade: Option<bool>,
311 pub display_install_warnings: Option<bool>,
312 #[serde(rename = "UnsupportedOSArchitectures")]
313 pub unsupported_os_architectures: Option<UnsupportedOSArchitecture>,
314 pub unsupported_arguments: Option<UnsupportedArguments>,
315 pub apps_and_features_entries: Option<Vec<AppsAndFeaturesEntry>>,
316 pub elevation_requirement: Option<ElevationRequirement>,
317 pub installation_metadata: Option<InstallationMetadata>,
318 pub download_command_prohibited: Option<bool>,
319 pub repair_behavior: Option<RepairBehavior>,
320 pub archive_binaries_depend_on_path: Option<bool>,
321}
322
323impl Installer {
324 #[must_use]
325 pub fn merge_with(mut self, other: Self) -> Self {
326 macro_rules! merge_fields {
327 ($self:ident, $other:ident, $( $field:ident ),* ) => {
328 $(
329 if $self.$field.is_none() {
330 $self.$field = $other.$field;
331 }
332 )*
333 }
334 }
335
336 if let (Some(custom), Some(other_custom)) = (
337 self.switches
338 .as_mut()
339 .and_then(|switches| switches.custom.as_mut()),
340 other
341 .switches
342 .as_ref()
343 .and_then(|switches| switches.custom.as_ref()),
344 ) {
345 for part in other_custom {
346 if !custom.contains(part) {
347 custom.push(part.clone());
348 }
349 }
350 }
351
352 merge_fields!(
353 self,
354 other,
355 locale,
356 platform,
357 minimum_os_version,
358 r#type,
359 nested_installer_type,
360 nested_installer_files,
361 scope,
362 install_modes,
363 switches,
364 success_codes,
365 expected_return_codes,
366 upgrade_behavior,
367 commands,
368 protocols,
369 file_extensions,
370 dependencies,
371 package_family_name,
372 product_code,
373 capabilities,
374 restricted_capabilities,
375 markets,
376 aborts_terminal,
377 require_explicit_upgrade,
378 display_install_warnings,
379 unsupported_os_architectures,
380 unsupported_arguments,
381 elevation_requirement,
382 installation_metadata,
383 download_command_prohibited,
384 repair_behavior,
385 archive_binaries_depend_on_path
386 );
387
388 self
389 }
390}