winget_types/installer/
mod.rs

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}