winget_types/installer/mod.rs
1#![expect(clippy::struct_excessive_bools)]
2
3mod apps_and_features_entry;
4mod architecture;
5pub mod authentication;
6mod capability;
7mod channel;
8mod command;
9mod dependencies;
10mod elevation_requirement;
11mod expected_return_codes;
12mod file_extension;
13mod install_modes;
14mod installation_metadata;
15mod installer_return_code;
16mod installer_type;
17mod market;
18mod minimum_os_version;
19mod nested;
20mod platform;
21mod protocol;
22mod repair_behavior;
23mod return_response;
24mod scope;
25pub mod switches;
26mod unsupported_arguments;
27mod unsupported_os_architectures;
28mod upgrade_behavior;
29
30use alloc::{collections::BTreeSet, string::String, vec::Vec};
31
32pub use apps_and_features_entry::AppsAndFeaturesEntry;
33pub use architecture::{Architecture, ParseArchitectureError};
34pub use authentication::Authentication;
35pub use capability::{Capability, CapabilityError, RestrictedCapability};
36pub use channel::{Channel, ChannelError};
37use chrono::NaiveDate;
38pub use command::{Command, CommandError};
39pub use dependencies::{Dependencies, PackageDependencies};
40pub use elevation_requirement::ElevationRequirement;
41pub use expected_return_codes::ExpectedReturnCodes;
42pub use file_extension::{FileExtension, FileExtensionError};
43pub use install_modes::InstallModes;
44pub use installation_metadata::InstallationMetadata;
45pub use installer_return_code::{InstallerReturnCode, InstallerSuccessCode};
46pub use installer_type::InstallerType;
47use itertools::Itertools;
48pub use market::{Market, MarketError, Markets, MarketsError};
49pub use minimum_os_version::{MinimumOSVersion, MinimumOSVersionError};
50use nested::installer_type::NestedInstallerType;
51pub use nested::{
52 PortableCommandAlias, PortableCommandAliasError, installer_files::NestedInstallerFiles,
53};
54pub use package_family_name::PackageFamilyName;
55pub use platform::{Platform, PlatformParseError};
56pub use protocol::{Protocol, ProtocolError};
57pub use repair_behavior::RepairBehavior;
58pub use scope::{Scope, ScopeParseError};
59pub use switches::InstallerSwitches;
60pub use unsupported_arguments::UnsupportedArguments;
61pub use unsupported_os_architectures::UnsupportedOSArchitecture;
62pub use upgrade_behavior::{UpgradeBehavior, UpgradeBehaviorParseError};
63
64use super::{
65 LanguageTag, Manifest, ManifestType, ManifestVersion, PackageIdentifier, PackageVersion,
66 Sha256String, url::DecodedUrl,
67};
68
69pub const VALID_FILE_EXTENSIONS: [&str; 7] = [
70 "msix",
71 "msi",
72 "appx",
73 "exe",
74 "zip",
75 "msixbundle",
76 "appxbundle",
77];
78
79#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
80#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
81#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
82pub struct InstallerManifest {
83 /// The unique identifier for a given package.
84 ///
85 /// This value is generally in the form of `Publisher.Package`. It is case-sensitive, and this
86 /// value must match the folder structure under the partition directory in GitHub.
87 pub package_identifier: PackageIdentifier,
88
89 /// The version of the package.
90 ///
91 /// It is related to the specific release this manifests targets. In some cases you will see a
92 /// perfectly formed [semantic version] number, and in other cases you might see something
93 /// different. These may be date driven, or they might have other characters with some package
94 /// specific meaning for example.
95 ///
96 /// The Windows Package Manager client uses this version to determine if an upgrade for a
97 /// package is available. In some cases, packages may be released with a marketing driven
98 /// version, and that causes trouble with the [`winget upgrade`] command.
99 ///
100 /// The current best practice is to use the value reported in Add / Remove Programs when this
101 /// version of the package is installed. In some cases, packages do not report a version
102 /// resulting in an upgrade loop or other unwanted behavior.
103 ///
104 /// [semantic version]: https://semver.org/
105 /// [`winget upgrade`]: https://docs.microsoft.com/windows/package-manager/winget/upgrade
106 pub package_version: PackageVersion,
107
108 /// The distribution channel for a package.
109 ///
110 /// Examples may include "stable" or "beta".
111 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
112 pub channel: Option<Channel>,
113
114 /// The locale for an installer not the package meta-data.
115 ///
116 /// Some installers are compiled with locale or language specific properties. If this key is
117 /// present, it is used to represent the package locale for an installer.
118 #[cfg_attr(
119 feature = "serde",
120 serde(rename = "InstallerLocale", skip_serializing_if = "Option::is_none")
121 )]
122 pub locale: Option<LanguageTag>,
123
124 /// The Windows platform targeted by the installer.
125 ///
126 /// The Windows Package Manager currently supports "Windows.Desktop" and "Windows.Universal".
127 #[cfg_attr(
128 feature = "serde",
129 serde(skip_serializing_if = "Platform::is_empty", default)
130 )]
131 pub platform: Platform,
132
133 /// The minimum version of the Windows operating system supported by the package.
134 #[cfg_attr(
135 feature = "serde",
136 serde(rename = "MinimumOSVersion", skip_serializing_if = "Option::is_none")
137 )]
138 pub minimum_os_version: Option<MinimumOSVersion>,
139
140 /// The installer type for the package.
141 ///
142 /// The Windows Package Manager supports [MSIX], [MSI], and executable installers. Some well
143 /// known formats ([Inno], [Nullsoft], [WiX], and [Burn]) provide standard sets of installer
144 /// switches to provide different installer experiences. Portable packages are supported as of
145 /// Windows Package Manager 1.3. Zip packages are supported as of Windows Package Manager 1.5.
146 ///
147 /// [MSIX]: https://docs.microsoft.com/windows/msix/overview
148 /// [MSI]: https://docs.microsoft.com/windows/win32/msi/windows-installer-portal
149 /// [Inno]: https://jrsoftware.org/isinfo.php
150 /// [Nullsoft]: https://sourceforge.net/projects/nsis
151 /// [WiX]: https://wixtoolset.org/
152 /// [Burn]: https://wixtoolset.org/docs/v3/bundle/
153 #[cfg_attr(
154 feature = "serde",
155 serde(rename = "InstallerType", skip_serializing_if = "Option::is_none")
156 )]
157 pub r#type: Option<InstallerType>,
158
159 /// The installer type of the file within the archive which will be used as the installer.
160 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
161 pub nested_installer_type: Option<NestedInstallerType>,
162
163 /// A list of all the installers to be executed within an archive.
164 #[cfg_attr(
165 feature = "serde",
166 serde(skip_serializing_if = "BTreeSet::is_empty", default)
167 )]
168 pub nested_installer_files: BTreeSet<NestedInstallerFiles>,
169
170 /// The scope the package is installed under.
171 ///
172 /// The two configurations are [`user`] and [`machine`]. Some installers support only one of
173 /// these scopes while others support both via arguments passed to the installer using
174 /// [`InstallerSwitches`].
175 ///
176 /// [`user`]: Scope::User
177 /// [`machine`]: Scope::Machine
178 /// [`InstallerSwitches`]: InstallerSwitches
179 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
180 pub scope: Option<Scope>,
181
182 /// The install modes supported by the installer.
183 ///
184 /// The Microsoft community package repository requires a package support "silent" and
185 /// "silent with progress". The Windows Package Manager also supports "interactive" installers.
186 #[cfg_attr(
187 feature = "serde",
188 serde(skip_serializing_if = "InstallModes::is_empty", default)
189 )]
190 pub install_modes: InstallModes,
191
192 /// The set of switches passed to installers.
193 #[cfg_attr(
194 feature = "serde",
195 serde(
196 rename = "InstallerSwitches",
197 skip_serializing_if = "InstallerSwitches::is_empty",
198 default
199 )
200 )]
201 pub switches: InstallerSwitches,
202
203 /// Any status codes returned by the installer representing a success condition other than zero.
204 #[cfg_attr(
205 feature = "serde",
206 serde(
207 rename = "InstallerSuccessCodes",
208 skip_serializing_if = "BTreeSet::is_empty",
209 default
210 )
211 )]
212 pub success_codes: BTreeSet<InstallerSuccessCode>,
213
214 /// Any status codes returned by the installer representing a condition other than zero.
215 #[cfg_attr(
216 feature = "serde",
217 serde(skip_serializing_if = "BTreeSet::is_empty", default)
218 )]
219 pub expected_return_codes: BTreeSet<ExpectedReturnCodes>,
220
221 /// What the Windows Package Manager should do regarding the currently installed package during
222 /// a package upgrade.
223 ///
224 /// If the package should be uninstalled first, the [`uninstallPrevious`] value should be
225 /// specified. If the package should not be upgraded through `WinGet`, the [`deny`] value should
226 /// be specified.
227 ///
228 /// [`uninstallPrevious`]: UpgradeBehavior::UninstallPrevious
229 /// [`deny`]: UpgradeBehavior::Deny
230 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
231 pub upgrade_behavior: Option<UpgradeBehavior>,
232
233 /// Any commands or aliases used to execute the package after it has been installed.
234 #[cfg_attr(
235 feature = "serde",
236 serde(skip_serializing_if = "BTreeSet::is_empty", default)
237 )]
238 pub commands: BTreeSet<Command>,
239
240 /// Any protocols (i.e. URI schemes) supported by the package. For example: `["ftp", "ldap"]`.
241 /// Entries shouldn't have trailing colons. The Windows Package Manager does not support any
242 /// behavior related to protocols handled by a package.
243 #[cfg_attr(
244 feature = "serde",
245 serde(skip_serializing_if = "BTreeSet::is_empty", default)
246 )]
247 pub protocols: BTreeSet<Protocol>,
248
249 /// Any file extensions supported by the package.
250 ///
251 /// For example: `["html", "jpg"]`. Entries shouldn't have leading dots. The Windows Package
252 /// Manager does not support any behavior related to the file extensions supported by the
253 /// package.
254 #[cfg_attr(
255 feature = "serde",
256 serde(skip_serializing_if = "BTreeSet::is_empty", default)
257 )]
258 pub file_extensions: BTreeSet<FileExtension>,
259
260 /// Any dependencies required to install or run the package.
261 #[cfg_attr(
262 feature = "serde",
263 serde(skip_serializing_if = "Dependencies::is_empty", default)
264 )]
265 pub dependencies: Dependencies,
266
267 /// The [package family name] specified in an MSIX installer.
268 ///
269 /// This value is used to assist with matching packages from a source to the program installed
270 /// in Windows via Add / Remove Programs for list, and upgrade behavior.
271 ///
272 /// [package family name]: https://learn.microsoft.com/windows/apps/desktop/modernize/package-identity-overview#package-family-name
273 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
274 pub package_family_name: Option<PackageFamilyName>,
275
276 /// The [product code].
277 ///
278 /// This value is used to assist with matching packages from a source to the program installed
279 /// in Windows via Add / Remove Programs for list, and upgrade behavior.
280 ///
281 /// [product code]: https://learn.microsoft.com/windows/win32/msi/product-codes
282 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
283 pub product_code: Option<String>,
284
285 /// The capabilities provided by an MSIX package.
286 ///
287 /// More information is available for [App capability declarations].
288 ///
289 /// [App capability declarations]: https://docs.microsoft.com/windows/uwp/packaging/app-capability-declarations
290 #[cfg_attr(
291 feature = "serde",
292 serde(skip_serializing_if = "BTreeSet::is_empty", default)
293 )]
294 pub capabilities: BTreeSet<Capability>,
295
296 /// The restricted capabilities provided by an MSIX package.
297 ///
298 /// More information is available for [App capability declarations].
299 ///
300 /// [App capability declarations]: https://docs.microsoft.com/windows/uwp/packaging/app-capability-declarations
301 #[cfg_attr(
302 feature = "serde",
303 serde(skip_serializing_if = "BTreeSet::is_empty", default)
304 )]
305 pub restricted_capabilities: BTreeSet<RestrictedCapability>,
306
307 /// Any markets a package may or may not be installed in.
308 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
309 pub markets: Option<Markets>,
310
311 /// The behavior associated with installers that abort the terminal.
312 ///
313 /// This most often occurs when a user is performing an upgrade of the running terminal.
314 #[cfg_attr(
315 feature = "serde",
316 serde(
317 rename = "InstallerAbortsTerminal",
318 skip_serializing_if = "core::ops::Not::not",
319 default
320 )
321 )]
322 pub aborts_terminal: bool,
323
324 /// The release date for a package, in RFC 3339 / ISO 8601 format, i.e. "YYYY-MM-DD".
325 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
326 pub release_date: Option<NaiveDate>,
327
328 /// The requirement to have an install location specified.
329 ///
330 /// These installers are known to deploy files to the location the installer is executed in.
331 #[cfg_attr(
332 feature = "serde",
333 serde(skip_serializing_if = "core::ops::Not::not", default)
334 )]
335 pub install_location_required: bool,
336
337 /// Identifies packages that upgrade themselves.
338 ///
339 /// By default, they are excluded from `winget upgrade --all`.
340 #[cfg_attr(
341 feature = "serde",
342 serde(skip_serializing_if = "core::ops::Not::not", default)
343 )]
344 pub require_explicit_upgrade: bool,
345
346 /// Whether a warning message is displayed to the user prior to install or upgrade if the
347 /// package is known to interfere with any running applications.
348 #[cfg_attr(
349 feature = "serde",
350 serde(skip_serializing_if = "core::ops::Not::not", default)
351 )]
352 pub display_install_warnings: bool,
353
354 /// Any architectures a package is known not to be compatible with.
355 ///
356 /// Generally, this is associated with emulation modes.
357 #[cfg_attr(
358 feature = "serde",
359 serde(
360 rename = "UnsupportedOSArchitectures",
361 skip_serializing_if = "UnsupportedOSArchitecture::is_empty",
362 default
363 )
364 )]
365 pub unsupported_os_architectures: UnsupportedOSArchitecture,
366
367 /// The list of Windows Package Manager Client arguments the installer does not support.
368 ///
369 /// Only the `--log` and `--location` arguments can be specified as unsupported arguments for an
370 /// installer.
371 #[cfg_attr(
372 feature = "serde",
373 serde(skip_serializing_if = "UnsupportedArguments::is_empty", default)
374 )]
375 pub unsupported_arguments: UnsupportedArguments,
376
377 /// The values reported by Windows Apps & Features.
378 ///
379 /// When a package is installed, entries are made into the Windows Registry.
380 #[cfg_attr(
381 feature = "serde",
382 serde(skip_serializing_if = "Vec::is_empty", default)
383 )]
384 pub apps_and_features_entries: Vec<AppsAndFeaturesEntry>,
385
386 /// The scope in which scope a package is required to be executed under.
387 ///
388 /// Some packages require user level execution while others require administrative level
389 /// execution.
390 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
391 pub elevation_requirement: Option<ElevationRequirement>,
392
393 /// Allows for additional metadata to be used for deeper installation detection.
394 #[cfg_attr(
395 feature = "serde",
396 serde(skip_serializing_if = "InstallationMetadata::is_empty", default)
397 )]
398 pub installation_metadata: InstallationMetadata,
399
400 /// When true, this flag will prohibit the manifest from being downloaded for offline
401 /// installation with the winget download command.
402 #[cfg_attr(
403 feature = "serde",
404 serde(skip_serializing_if = "core::ops::Not::not", default)
405 )]
406 pub download_command_prohibited: bool,
407
408 /// This field controls what method is used to repair existing installations of packages.
409 ///
410 /// Specifying `modify` will use the `ModifyPath` string from the package's ARP data,
411 /// `uninstaller` will use the Uninstall string from the package's ARP data, and `installer`
412 /// will download and run the installer. In each case, the `Repair` value from
413 /// `InstallerSwitches` will be added as an argument when invoking the command to repair the
414 /// package.
415 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
416 pub repair_behavior: Option<RepairBehavior>,
417
418 /// This field controls the behavior of environment variables when installing portable packages
419 /// from an archive (i.e. `zip`).
420 ///
421 /// Specifying `true` will add the install location directly to the `PATH` environment variable.
422 /// Specifying `false` will use the default behavior of adding a symlink to the `links` folder,
423 /// if supported, or adding the install location directly to `PATH` if symlinks are not
424 /// supported.
425 #[cfg_attr(
426 feature = "serde",
427 serde(skip_serializing_if = "core::ops::Not::not", default)
428 )]
429 pub archive_binaries_depend_on_path: bool,
430
431 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
432 pub authentication: Option<Authentication>,
433
434 pub installers: Vec<Installer>,
435
436 /// The manifest type.
437 ///
438 /// Must have the value [`installer`]. The Microsoft community package repository validation
439 /// pipelines also use this value to determine appropriate validation rules when evaluating this
440 /// file.
441 ///
442 /// [`installer`]: ManifestType::Installer
443 #[cfg_attr(feature = "serde", serde(default = "ManifestType::installer"))]
444 pub manifest_type: ManifestType,
445
446 /// The manifest syntax version.
447 ///
448 /// Must have the value `1.10.0`. The Microsoft community package repository validation
449 /// pipelines also use this value to determine appropriate validation rules when evaluating this
450 /// file.
451 #[cfg_attr(feature = "serde", serde(default))]
452 pub manifest_version: ManifestVersion,
453}
454
455impl Manifest for InstallerManifest {
456 const SCHEMA: &'static str = "https://aka.ms/winget-manifest.installer.1.10.0.schema.json";
457 const TYPE: ManifestType = ManifestType::Installer;
458}
459
460impl InstallerManifest {
461 #[expect(
462 clippy::cognitive_complexity,
463 reason = "The resulting complexity is generated by a macro"
464 )]
465 pub fn optimize(&mut self) {
466 macro_rules! optimize_keys {
467 ($($($field:ident).+),* $(,)?) => {
468 #[inline]
469 fn default<T: Default>(_: &T) -> T {
470 T::default()
471 }
472
473 $(
474 if let Ok(field) = self
475 .installers
476 .iter_mut()
477 .map(|installer| &mut installer.$($field).+)
478 .all_equal_value()
479 {
480 self.$($field).+ = core::mem::take(r#field);
481 for installer in &mut self.installers {
482 installer.$($field).+ = default(&installer.$($field).+);
483 }
484 } else {
485 self.$($field).+ = default(&self.$($field).+);
486 }
487 )*
488 };
489 }
490
491 optimize_keys!(
492 locale,
493 platform,
494 minimum_os_version,
495 r#type,
496 nested_installer_type,
497 nested_installer_files,
498 scope,
499 install_modes,
500 switches.silent,
501 switches.silent_with_progress,
502 switches.interactive,
503 switches.install_location,
504 switches.log,
505 switches.upgrade,
506 switches.repair,
507 success_codes,
508 expected_return_codes,
509 upgrade_behavior,
510 commands,
511 protocols,
512 file_extensions,
513 dependencies.windows_features,
514 dependencies.windows_libraries,
515 dependencies.package,
516 dependencies.external,
517 package_family_name,
518 product_code,
519 capabilities,
520 restricted_capabilities,
521 markets,
522 aborts_terminal,
523 release_date,
524 install_location_required,
525 require_explicit_upgrade,
526 display_install_warnings,
527 unsupported_os_architectures,
528 unsupported_arguments,
529 apps_and_features_entries,
530 elevation_requirement,
531 installation_metadata,
532 download_command_prohibited,
533 repair_behavior,
534 archive_binaries_depend_on_path,
535 );
536
537 self.manifest_version = ManifestVersion::default();
538
539 self.installers.sort_unstable();
540 self.installers.dedup();
541 }
542}
543
544#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
545#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
546#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
547pub struct Installer {
548 /// The locale for an installer *not* the package meta-data.
549 ///
550 /// Some installers are compiled with locale or language specific properties. If this key is
551 /// present, it is used to represent the package locale for an installer.
552 #[cfg_attr(
553 feature = "serde",
554 serde(rename = "InstallerLocale", skip_serializing_if = "Option::is_none")
555 )]
556 pub locale: Option<LanguageTag>,
557
558 /// The Windows platform targeted by the installer.
559 ///
560 /// The Windows Package Manager currently supports "Windows.Desktop" and "Windows.Universal".
561 #[cfg_attr(
562 feature = "serde",
563 serde(skip_serializing_if = "Platform::is_empty", default)
564 )]
565 pub platform: Platform,
566
567 /// The minimum version of the Windows operating system supported by the package.
568 #[cfg_attr(
569 feature = "serde",
570 serde(rename = "MinimumOSVersion", skip_serializing_if = "Option::is_none")
571 )]
572 pub minimum_os_version: Option<MinimumOSVersion>,
573
574 /// The hardware architecture targeted by the installer.
575 ///
576 /// The Windows Package Manager will attempt to determine the best architecture to use. If
577 /// emulation is available and the native hardware architecture does not have a supported
578 /// installer, the emulated architecture may be used.
579 pub architecture: Architecture,
580
581 /// The installer type for the package.
582 ///
583 /// The Windows Package Manager supports [MSIX], [MSI], and executable installers. Some well
584 /// known formats ([Inno], [Nullsoft], [WiX], and [Burn]) provide standard sets of installer
585 /// switches to provide different installer experiences. Portable packages are supported as of
586 /// Windows Package Manager 1.3. Zip packages are supported as of Windows Package Manager 1.5.
587 ///
588 /// [MSIX]: https://docs.microsoft.com/windows/msix/overview
589 /// [MSI]: https://docs.microsoft.com/windows/win32/msi/windows-installer-portal
590 /// [Inno]: https://jrsoftware.org/isinfo.php
591 /// [Nullsoft]: https://sourceforge.net/projects/nsis
592 /// [WiX]: https://wixtoolset.org/
593 /// [Burn]: https://wixtoolset.org/docs/v3/bundle/
594 #[cfg_attr(
595 feature = "serde",
596 serde(rename = "InstallerType", skip_serializing_if = "Option::is_none")
597 )]
598 pub r#type: Option<InstallerType>,
599
600 /// The installer type of the file within the archive which will be used as the installer.
601 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
602 pub nested_installer_type: Option<NestedInstallerType>,
603
604 /// A list of all the installers to be executed within an archive.
605 #[cfg_attr(
606 feature = "serde",
607 serde(skip_serializing_if = "BTreeSet::is_empty", default)
608 )]
609 pub nested_installer_files: BTreeSet<NestedInstallerFiles>,
610
611 /// The scope the package is installed under.
612 ///
613 /// The two configurations are [`user`] and [`machine`]. Some installers support only one of
614 /// these scopes while others support both via arguments passed to the installer using
615 /// [`InstallerSwitches`].
616 ///
617 /// [`user`]: Scope::User
618 /// [`machine`]: Scope::Machine
619 /// [`InstallerSwitches`]: InstallerSwitches
620 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
621 pub scope: Option<Scope>,
622
623 /// The URL to download the installer.
624 #[cfg_attr(feature = "serde", serde(rename = "InstallerUrl"))]
625 pub url: DecodedUrl,
626
627 /// The SHA 256 hash for the installer. It is used to confirm the installer has not been
628 /// modified. The Windows Package Manager will compare the hash in the manifest with the
629 /// calculated hash of the installer after it has been downloaded.
630 #[cfg_attr(feature = "serde", serde(rename = "InstallerSha256"))]
631 pub sha_256: Sha256String,
632
633 /// The signature file (AppxSignature.p7x) inside an MSIX installer. It is used to provide
634 /// streaming install for MSIX packages.
635 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
636 pub signature_sha_256: Option<Sha256String>,
637
638 /// The install modes supported by the installer.
639 ///
640 /// The Microsoft community package repository requires a package support "silent" and
641 /// "silent with progress". The Windows Package Manager also supports "interactive" installers.
642 #[cfg_attr(
643 feature = "serde",
644 serde(skip_serializing_if = "InstallModes::is_empty", default)
645 )]
646 pub install_modes: InstallModes,
647
648 /// The set of switches passed to installers.
649 #[cfg_attr(
650 feature = "serde",
651 serde(
652 rename = "InstallerSwitches",
653 skip_serializing_if = "InstallerSwitches::is_empty",
654 default
655 )
656 )]
657 pub switches: InstallerSwitches,
658
659 /// Any status codes returned by the installer representing a success condition other than zero.
660 #[cfg_attr(
661 feature = "serde",
662 serde(
663 rename = "InstallerSuccessCodes",
664 skip_serializing_if = "BTreeSet::is_empty",
665 default
666 )
667 )]
668 pub success_codes: BTreeSet<InstallerSuccessCode>,
669
670 /// Any status codes returned by the installer representing a condition other than zero.
671 #[cfg_attr(
672 feature = "serde",
673 serde(skip_serializing_if = "BTreeSet::is_empty", default)
674 )]
675 pub expected_return_codes: BTreeSet<ExpectedReturnCodes>,
676
677 /// What the Windows Package Manager should do regarding the currently installed package during
678 /// a package upgrade.
679 ///
680 /// If the package should be uninstalled first, the [`uninstallPrevious`] value should be
681 /// specified. If the package should not be upgraded through `WinGet`, the [`deny`] value should
682 /// be specified.
683 ///
684 /// [`uninstallPrevious`]: UpgradeBehavior::UninstallPrevious
685 /// [`deny`]: UpgradeBehavior::Deny
686 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
687 pub upgrade_behavior: Option<UpgradeBehavior>,
688
689 /// Any commands or aliases used to execute the package after it has been installed.
690 #[cfg_attr(
691 feature = "serde",
692 serde(skip_serializing_if = "BTreeSet::is_empty", default)
693 )]
694 pub commands: BTreeSet<Command>,
695
696 /// Any protocols (i.e. URI schemes) supported by the package. For example: `["ftp", "ldap"]`.
697 /// Entries shouldn't have trailing colons. The Windows Package Manager does not support any
698 /// behavior related to protocols handled by a package.
699 #[cfg_attr(
700 feature = "serde",
701 serde(skip_serializing_if = "BTreeSet::is_empty", default)
702 )]
703 pub protocols: BTreeSet<Protocol>,
704
705 /// Any file extensions supported by the package.
706 ///
707 /// For example: `["html", "jpg"]`. Entries shouldn't have leading dots. The Windows Package
708 /// Manager does not support any behavior related to the file extensions supported by the
709 /// package.
710 #[cfg_attr(
711 feature = "serde",
712 serde(skip_serializing_if = "BTreeSet::is_empty", default)
713 )]
714 pub file_extensions: BTreeSet<FileExtension>,
715
716 /// Any dependencies required to install or run the package.
717 #[cfg_attr(
718 feature = "serde",
719 serde(skip_serializing_if = "Dependencies::is_empty", default)
720 )]
721 pub dependencies: Dependencies,
722
723 /// The [package family name] specified in an MSIX installer.
724 ///
725 /// This value is used to assist with matching packages from a source to the program installed
726 /// in Windows via Add / Remove Programs for list, and upgrade behavior.
727 ///
728 /// [package family name]: https://learn.microsoft.com/windows/apps/desktop/modernize/package-identity-overview#package-family-name
729 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
730 pub package_family_name: Option<PackageFamilyName>,
731
732 /// The [product code].
733 ///
734 /// This value is used to assist with matching packages from a source to the program installed
735 /// in Windows via Add / Remove Programs for list, and upgrade behavior.
736 ///
737 /// [product code]: https://learn.microsoft.com/windows/win32/msi/product-codes
738 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
739 pub product_code: Option<String>,
740
741 /// The capabilities provided by an MSIX package.
742 ///
743 /// More information is available for [App capability declarations].
744 ///
745 /// [App capability declarations]: https://docs.microsoft.com/windows/uwp/packaging/app-capability-declarations
746 #[cfg_attr(
747 feature = "serde",
748 serde(skip_serializing_if = "BTreeSet::is_empty", default)
749 )]
750 pub capabilities: BTreeSet<Capability>,
751
752 /// The restricted capabilities provided by an MSIX package.
753 ///
754 /// More information is available for [App capability declarations].
755 ///
756 /// [App capability declarations]: https://docs.microsoft.com/windows/uwp/packaging/app-capability-declarations
757 #[cfg_attr(
758 feature = "serde",
759 serde(skip_serializing_if = "BTreeSet::is_empty", default)
760 )]
761 pub restricted_capabilities: BTreeSet<RestrictedCapability>,
762
763 /// Any markets a package may or may not be installed in.
764 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
765 pub markets: Option<Markets>,
766
767 /// The behavior associated with installers that abort the terminal.
768 ///
769 /// This most often occurs when a user is performing an upgrade of the running terminal.
770 #[cfg_attr(
771 feature = "serde",
772 serde(
773 rename = "InstallerAbortsTerminal",
774 skip_serializing_if = "core::ops::Not::not",
775 default
776 )
777 )]
778 pub aborts_terminal: bool,
779
780 /// The release date for a package, in RFC 3339 / ISO 8601 format, i.e. "YYYY-MM-DD".
781 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
782 pub release_date: Option<NaiveDate>,
783
784 /// The requirement to have an install location specified.
785 ///
786 /// These installers are known to deploy files to the location the installer is executed in.
787 #[cfg_attr(
788 feature = "serde",
789 serde(skip_serializing_if = "core::ops::Not::not", default)
790 )]
791 pub install_location_required: bool,
792
793 /// Identifies packages that upgrade themselves.
794 ///
795 /// By default, they are excluded from `winget upgrade --all`.
796 #[cfg_attr(
797 feature = "serde",
798 serde(skip_serializing_if = "core::ops::Not::not", default)
799 )]
800 pub require_explicit_upgrade: bool,
801
802 /// Whether a warning message is displayed to the user prior to install or upgrade if the
803 /// package is known to interfere with any running applications.
804 #[cfg_attr(
805 feature = "serde",
806 serde(skip_serializing_if = "core::ops::Not::not", default)
807 )]
808 pub display_install_warnings: bool,
809
810 /// Any architectures a package is known not to be compatible with.
811 ///
812 /// Generally, this is associated with emulation modes.
813 #[cfg_attr(
814 feature = "serde",
815 serde(
816 rename = "UnsupportedOSArchitectures",
817 skip_serializing_if = "UnsupportedOSArchitecture::is_empty",
818 default
819 )
820 )]
821 pub unsupported_os_architectures: UnsupportedOSArchitecture,
822
823 /// The list of Windows Package Manager Client arguments the installer does not support.
824 ///
825 /// Only the `--log` and `--location` arguments can be specified as unsupported arguments for an
826 /// installer.
827 #[cfg_attr(
828 feature = "serde",
829 serde(skip_serializing_if = "UnsupportedArguments::is_empty", default)
830 )]
831 pub unsupported_arguments: UnsupportedArguments,
832
833 /// The values reported by Windows Apps & Features.
834 ///
835 /// When a package is installed, entries are made into the Windows Registry.
836 #[cfg_attr(
837 feature = "serde",
838 serde(skip_serializing_if = "Vec::is_empty", default)
839 )]
840 pub apps_and_features_entries: Vec<AppsAndFeaturesEntry>,
841
842 /// The scope in which scope a package is required to be executed under.
843 ///
844 /// Some packages require user level execution while others require administrative level
845 /// execution.
846 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
847 pub elevation_requirement: Option<ElevationRequirement>,
848
849 /// Allows for additional metadata to be used for deeper installation detection.
850 #[cfg_attr(
851 feature = "serde",
852 serde(skip_serializing_if = "InstallationMetadata::is_empty", default)
853 )]
854 pub installation_metadata: InstallationMetadata,
855
856 /// When true, this flag will prohibit the manifest from being downloaded for offline
857 /// installation with the winget download command.
858 #[cfg_attr(
859 feature = "serde",
860 serde(skip_serializing_if = "core::ops::Not::not", default)
861 )]
862 pub download_command_prohibited: bool,
863
864 /// This field controls what method is used to repair existing installations of packages.
865 ///
866 /// Specifying `modify` will use the `ModifyPath` string from the package's ARP data,
867 /// `uninstaller` will use the Uninstall string from the package's ARP data, and `installer`
868 /// will download and run the installer. In each case, the `Repair` value from
869 /// `InstallerSwitches` will be added as an argument when invoking the command to repair the
870 /// package.
871 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
872 pub repair_behavior: Option<RepairBehavior>,
873
874 /// This field controls the behavior of environment variables when installing portable packages
875 /// from an archive (i.e. `zip`).
876 ///
877 /// Specifying `true` will add the install location directly to the `PATH` environment variable.
878 /// Specifying `false` will use the default behavior of adding a symlink to the `links` folder,
879 /// if supported, or adding the install location directly to `PATH` if symlinks are not
880 /// supported.
881 #[cfg_attr(
882 feature = "serde",
883 serde(skip_serializing_if = "core::ops::Not::not", default)
884 )]
885 pub archive_binaries_depend_on_path: bool,
886
887 /// This field controls the authentication for Entra ID secured private sources.
888 ///
889 /// Resource and scope information can be included if a specific resource is needed to download
890 /// or install the package.
891 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
892 pub authentication: Option<Authentication>,
893}
894
895impl Installer {
896 /// Merges two installers.
897 ///
898 /// If a key of `self` is equal to its default, it will take the value from `other`. If the key
899 /// of `self` is not equal to its default, it will retain that value and the equivalent key in
900 /// `other` is ignored.
901 #[expect(
902 clippy::cognitive_complexity,
903 reason = "The resulting complexity is generated by a macro"
904 )]
905 #[must_use]
906 pub fn merge_with(mut self, other: Self) -> Self {
907 macro_rules! merge_keys {
908 (
909 $($($field:ident).+),*,
910 [$($switch:ident),* $(,)?]$(,)?
911 ) => {
912 #[inline]
913 fn default<T: Default>(_: &T) -> T {
914 T::default()
915 }
916
917 $(
918 if self.$($field).+ == default(&self.$($field).+) {
919 self.$($field).+ = other.$($field).+;
920 }
921 )*
922
923 $(
924 if let (Some(switch), Some(other_switch)) = (
925 self.switches.$switch.as_mut(),
926 other.switches.$switch.as_ref(),
927 ) {
928 for part in other_switch {
929 if !switch.contains(part) {
930 switch.push(part.clone());
931 }
932 }
933 }
934 )*
935 };
936 }
937
938 merge_keys!(
939 locale,
940 platform,
941 minimum_os_version,
942 r#type,
943 nested_installer_type,
944 nested_installer_files,
945 scope,
946 install_modes,
947 success_codes,
948 expected_return_codes,
949 upgrade_behavior,
950 commands,
951 protocols,
952 file_extensions,
953 dependencies,
954 package_family_name,
955 product_code,
956 capabilities,
957 restricted_capabilities,
958 markets,
959 aborts_terminal,
960 release_date,
961 install_location_required,
962 require_explicit_upgrade,
963 display_install_warnings,
964 unsupported_os_architectures,
965 unsupported_arguments,
966 apps_and_features_entries,
967 elevation_requirement,
968 installation_metadata,
969 download_command_prohibited,
970 repair_behavior,
971 archive_binaries_depend_on_path,
972 [
973 silent,
974 silent_with_progress,
975 interactive,
976 install_location,
977 log,
978 upgrade,
979 custom,
980 repair
981 ],
982 );
983
984 self
985 }
986}
987
988#[cfg(test)]
989mod tests {
990 use alloc::vec;
991
992 use crate::{
993 installer::{Architecture, Installer, InstallerManifest, InstallerSwitches},
994 shared::LanguageTag,
995 };
996
997 #[test]
998 fn optimize_duplicate_locale() {
999 let mut manifest = InstallerManifest {
1000 installers: vec![
1001 Installer {
1002 locale: Some("en-US".parse::<LanguageTag>().unwrap()),
1003 architecture: Architecture::X86,
1004 ..Installer::default()
1005 },
1006 Installer {
1007 locale: Some("en-US".parse::<LanguageTag>().unwrap()),
1008 architecture: Architecture::X64,
1009 ..Installer::default()
1010 },
1011 ],
1012 ..InstallerManifest::default()
1013 };
1014
1015 manifest.optimize();
1016
1017 assert_eq!(
1018 manifest,
1019 InstallerManifest {
1020 locale: Some("en-US".parse::<LanguageTag>().unwrap()),
1021 installers: vec![
1022 Installer {
1023 architecture: Architecture::X86,
1024 ..Installer::default()
1025 },
1026 Installer {
1027 architecture: Architecture::X64,
1028 ..Installer::default()
1029 },
1030 ],
1031 ..InstallerManifest::default()
1032 }
1033 )
1034 }
1035
1036 #[test]
1037 fn optimize_duplicate_switch() {
1038 let mut manifest = InstallerManifest {
1039 installers: vec![
1040 Installer {
1041 architecture: Architecture::X86,
1042 switches: InstallerSwitches::new()
1043 .silent("--silent")
1044 .custom("--custom"),
1045 ..Installer::default()
1046 },
1047 Installer {
1048 architecture: Architecture::X64,
1049 switches: InstallerSwitches::new().silent("--silent"),
1050 ..Installer::default()
1051 },
1052 ],
1053 ..InstallerManifest::default()
1054 };
1055
1056 manifest.optimize();
1057
1058 assert_eq!(
1059 manifest,
1060 InstallerManifest {
1061 switches: InstallerSwitches::new().silent("--silent"),
1062 installers: vec![
1063 Installer {
1064 architecture: Architecture::X86,
1065 switches: InstallerSwitches::new().custom("--custom"),
1066 ..Installer::default()
1067 },
1068 Installer {
1069 architecture: Architecture::X64,
1070 ..Installer::default()
1071 },
1072 ],
1073 ..InstallerManifest::default()
1074 }
1075 )
1076 }
1077}