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