winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

YamlWriter.cpp (33709B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "AppInstallerStrings.h"
      5 #include "AppInstallerSHA256.h"
      6 #include "winget/Yaml.h"
      7 #include "winget/ManifestYamlWriter.h"
      8 
      9 namespace AppInstaller::Manifest::YamlWriter
     10 {
     11     using namespace Utility::literals;
     12 
     13     namespace
     14     {
     15         constexpr std::string_view PackageIdentifier = "PackageIdentifier"sv;
     16         constexpr std::string_view PackageFamilyName = "PackageFamilyName"sv;
     17         constexpr std::string_view ProductCode = "ProductCode"sv;
     18         constexpr std::string_view Versions = "Versions"sv;
     19         constexpr std::string_view PackageVersion = "PackageVersion"sv;
     20         constexpr std::string_view Channel = "Channel"sv;
     21         constexpr std::string_view ManifestVersion = "ManifestVersion"sv;
     22         constexpr std::string_view ManifestType = "ManifestType"sv;
     23 
     24         // Installer
     25         constexpr std::string_view Installers = "Installers"sv;
     26         constexpr std::string_view InstallerIdentifier = "InstallerIdentifier"sv;
     27         constexpr std::string_view InstallerSha256 = "InstallerSha256"sv;
     28         constexpr std::string_view InstallerUrl = "InstallerUrl"sv;
     29         constexpr std::string_view Architecture = "Architecture"sv;
     30         constexpr std::string_view InstallerLocale = "InstallerLocale"sv;
     31         constexpr std::string_view Platform = "Platform"sv;
     32         constexpr std::string_view InstallerType = "InstallerType"sv;
     33         constexpr std::string_view Scope = "Scope"sv;
     34         constexpr std::string_view SignatureSha256 = "SignatureSha256"sv;
     35         constexpr std::string_view InstallModes = "InstallModes"sv;
     36         constexpr std::string_view MSStoreProductIdentifier = "MSStoreProductIdentifier"sv;
     37         constexpr std::string_view ReleaseDate = "ReleaseDate"sv;
     38         constexpr std::string_view InstallerAbortsTerminal = "InstallerAbortsTerminal"sv;
     39         constexpr std::string_view InstallLocationRequired = "InstallLocationRequired"sv;
     40         constexpr std::string_view RequireExplicitUpgrade = "RequireExplicitUpgrade"sv;
     41         constexpr std::string_view UnsupportedOSArchitectures = "UnsupportedOSArchitectures"sv;
     42         constexpr std::string_view AppsAndFeaturesEntries = "AppsAndFeaturesEntries"sv;
     43         constexpr std::string_view DisplayVersion = "DisplayVersion"sv;
     44         constexpr std::string_view UpgradeCode = "UpgradeCode"sv;
     45         constexpr std::string_view Markets = "Markets"sv;
     46         constexpr std::string_view AllowedMarkets = "AllowedMarkets"sv;
     47         constexpr std::string_view ExcludedMarkets = "ExcludedMarkets"sv;
     48         constexpr std::string_view ElevationRequirement = "ElevationRequirement"sv;
     49         constexpr std::string_view ExpectedReturnCodes = "ExpectedReturnCodes"sv;
     50         constexpr std::string_view InstallerReturnCode = "InstallerReturnCode"sv;
     51         constexpr std::string_view ReturnResponse = "ReturnResponse"sv;
     52         constexpr std::string_view ReturnResponseUrl = "ReturnResponseUrl"sv;
     53         constexpr std::string_view NestedInstallerType = "NestedInstallerType"sv;
     54         constexpr std::string_view DisplayInstallWarnings = "DisplayInstallWarnings"sv;
     55         constexpr std::string_view UnsupportedArguments = "UnsupportedArguments"sv;
     56         constexpr std::string_view NestedInstallerFiles = "NestedInstallerFiles"sv;
     57         constexpr std::string_view NestedInstallerFileRelativeFilePath = "RelativeFilePath"sv;
     58         constexpr std::string_view PortableCommandAlias = "PortableCommandAlias"sv;
     59         constexpr std::string_view InstallationMetadata = "InstallationMetadata"sv;
     60         constexpr std::string_view DefaultInstallLocation = "DefaultInstallLocation"sv;
     61         constexpr std::string_view InstallationMetadataFiles = "Files"sv;
     62         constexpr std::string_view InstallationMetadataRelativeFilePath = "RelativeFilePath"sv;
     63         constexpr std::string_view FileSha256 = "FileSha256"sv;
     64         constexpr std::string_view FileType = "FileType"sv;
     65         constexpr std::string_view InvocationParameter = "InvocationParameter"sv;
     66         constexpr std::string_view DisplayName = "DisplayName"sv;
     67         constexpr std::string_view MinimumOSVersion = "MinimumOSVersion"sv;
     68         constexpr std::string_view DownloadCommandProhibited = "DownloadCommandProhibited"sv;
     69         constexpr std::string_view RepairBehavior = "RepairBehavior"sv;
     70         constexpr std::string_view ArchiveBinariesDependOnPath = "ArchiveBinariesDependOnPath"sv;
     71         constexpr std::string_view Authentication = "Authentication"sv;
     72         constexpr std::string_view AuthenticationType = "AuthenticationType"sv;
     73         constexpr std::string_view MicrosoftEntraIdAuthenticationInfo = "MicrosoftEntraIdAuthenticationInfo"sv;
     74         constexpr std::string_view MicrosoftEntraIdResource = "Resource"sv;
     75         constexpr std::string_view MicrosoftEntraIdScope = "Scope"sv;
     76 
     77         // Installer switches
     78         constexpr std::string_view InstallerSwitches = "InstallerSwitches"sv;
     79         constexpr std::string_view Silent = "Silent"sv;
     80         constexpr std::string_view SilentWithProgress = "SilentWithProgress"sv;
     81         constexpr std::string_view Interactive = "Interactive"sv;
     82         constexpr std::string_view InstallLocation = "InstallLocation"sv;
     83         constexpr std::string_view Log = "Log"sv;
     84         constexpr std::string_view Upgrade = "Upgrade"sv;
     85         constexpr std::string_view Custom = "Custom"sv;
     86         constexpr std::string_view Repair = "Repair"sv;
     87 
     88         constexpr std::string_view InstallerSuccessCodes = "InstallerSuccessCodes"sv;
     89         constexpr std::string_view UpgradeBehavior = "UpgradeBehavior"sv;
     90         constexpr std::string_view Commands = "Commands"sv;
     91         constexpr std::string_view Protocols = "Protocols"sv;
     92         constexpr std::string_view FileExtensions = "FileExtensions"sv;
     93 
     94         // Dependencies
     95         constexpr std::string_view Dependencies = "Dependencies"sv;
     96         constexpr std::string_view WindowsFeatures = "WindowsFeatures"sv;
     97         constexpr std::string_view WindowsLibraries = "WindowsLibraries"sv;
     98         constexpr std::string_view PackageDependencies = "PackageDependencies"sv;
     99         constexpr std::string_view MinimumVersion = "MinimumVersion"sv;
    100         constexpr std::string_view ExternalDependencies = "ExternalDependencies"sv;
    101         constexpr std::string_view Capabilities = "Capabilities"sv;
    102         constexpr std::string_view RestrictedCapabilities = "RestrictedCapabilities"sv;
    103 
    104         // Locale
    105         constexpr std::string_view Localization = "Localization"sv;
    106         constexpr std::string_view InstallationNotes = "InstallationNotes"sv;
    107         constexpr std::string_view PurchaseUrl = "PurchaseUrl"sv;
    108         constexpr std::string_view Documentations = "Documentations"sv;
    109         constexpr std::string_view DocumentLabel = "DocumentLabel"sv;
    110         constexpr std::string_view DocumentUrl = "DocumentUrl"sv;
    111         constexpr std::string_view Icons = "Icons"sv;
    112         constexpr std::string_view IconUrl = "IconUrl"sv;
    113         constexpr std::string_view IconFileType = "IconFileType"sv;
    114         constexpr std::string_view IconResolution = "IconResolution"sv;
    115         constexpr std::string_view IconTheme = "IconTheme"sv;
    116         constexpr std::string_view IconSha256 = "IconSha256"sv;
    117         constexpr std::string_view ReleaseNotes = "ReleaseNotes"sv;
    118         constexpr std::string_view ReleaseNotesUrl = "ReleaseNotesUrl"sv;
    119         constexpr std::string_view Agreements = "Agreements"sv;
    120         constexpr std::string_view AgreementLabel = "AgreementLabel"sv;
    121         constexpr std::string_view Agreement = "Agreement"sv;
    122         constexpr std::string_view AgreementUrl = "AgreementUrl"sv;
    123         constexpr std::string_view DefaultLocale = "DefaultLocale"sv;
    124         constexpr std::string_view Locales = "Locales"sv;
    125         constexpr std::string_view PackageLocale = "PackageLocale"sv;
    126         constexpr std::string_view Publisher = "Publisher"sv;
    127         constexpr std::string_view PublisherUrl = "PublisherUrl"sv;
    128         constexpr std::string_view PublisherSupportUrl = "PublisherSupportUrl"sv;
    129         constexpr std::string_view PrivacyUrl = "PrivacyUrl"sv;
    130         constexpr std::string_view Author = "Author"sv;
    131         constexpr std::string_view PackageName = "PackageName"sv;
    132         constexpr std::string_view PackageUrl = "PackageUrl"sv;
    133         constexpr std::string_view License = "License"sv;
    134         constexpr std::string_view LicenseUrl = "LicenseUrl"sv;
    135         constexpr std::string_view Copyright = "Copyright"sv;
    136         constexpr std::string_view CopyrightUrl = "CopyrightUrl"sv;
    137         constexpr std::string_view ShortDescription = "ShortDescription"sv;
    138         constexpr std::string_view Description = "Description"sv;
    139         constexpr std::string_view Tags = "Tags"sv;
    140         constexpr std::string_view Moniker = "Moniker"sv;
    141 
    142 #define WRITE_PROPERTY(emitter, key, value) \
    143         { \
    144             emitter << YAML::Key << key << YAML::Value << value; \
    145         }
    146 
    147 #define WRITE_BOOL_PROPERTY(emitter, key, value) \
    148         { \
    149             emitter << YAML::Key << key << YAML::Value << Utility::ConvertBoolToString(value); \
    150         }
    151 
    152 #define WRITE_PROPERTY_IF_EXISTS(emitter, key, value) \
    153         { \
    154             if (!value.empty()) \
    155             { \
    156                 WRITE_PROPERTY(emitter, key, value) \
    157             } \
    158         }
    159 
    160 #define WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(emitter, key, field) \
    161         { \
    162             if (!field.empty()) \
    163             { \
    164                 WRITE_PROPERTY(emitter, key, Utility::SHA256::ConvertToString(field)) \
    165             } \
    166         }
    167 
    168 #define WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(emitter, key, value, enumField, enumType) \
    169         { \
    170             if (enumField != enumType::Unknown) \
    171             { \
    172                 WRITE_PROPERTY(emitter, key, value) \
    173             } \
    174         }
    175 
    176         void ProcessAgreements(YAML::Emitter& out, const std::vector<AppInstaller::Manifest::Agreement>& agreements)
    177         {
    178             if (agreements.empty())
    179             {
    180                 return;
    181             }
    182 
    183             out << YAML::Key << Agreements;
    184             out << YAML::BeginSeq;
    185             for (const auto& agreement : agreements)
    186             {
    187                 out << YAML::BeginMap;
    188                 WRITE_PROPERTY_IF_EXISTS(out, AgreementLabel, agreement.Label);
    189                 WRITE_PROPERTY_IF_EXISTS(out, AgreementUrl, agreement.AgreementUrl);
    190                 WRITE_PROPERTY_IF_EXISTS(out, Agreement, agreement.AgreementText);
    191                 out << YAML::EndMap;
    192             }
    193             out << YAML::EndSeq;
    194         }
    195 
    196         void ProcessDocumentations(YAML::Emitter& out, const std::vector<AppInstaller::Manifest::Documentation>& documentations)
    197         {
    198             if (documentations.empty())
    199             {
    200                 return;
    201             }
    202 
    203             out << YAML::Key << Documentations;
    204             out << YAML::BeginSeq;
    205             for (const auto& document : documentations)
    206             {
    207                 out << YAML::BeginMap;
    208                 WRITE_PROPERTY_IF_EXISTS(out, DocumentLabel, document.DocumentLabel);
    209                 WRITE_PROPERTY_IF_EXISTS(out, DocumentUrl, document.DocumentUrl);
    210                 out << YAML::EndMap;
    211             }
    212             out << YAML::EndSeq;
    213         }
    214 
    215         void ProcessIcons(YAML::Emitter& out, std::vector<AppInstaller::Manifest::Icon> icons)
    216         {
    217             if (icons.empty())
    218             {
    219                 return;
    220             }
    221 
    222             out << YAML::Key << Icons;
    223             out << YAML::BeginSeq;
    224             for (const auto& icon : icons)
    225             {
    226                 out << YAML::BeginMap;
    227                 WRITE_PROPERTY(out, IconUrl, icon.Url);
    228                 WRITE_PROPERTY(out, IconFileType, IconFileTypeToString(icon.FileType));
    229                 WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, IconResolution, IconResolutionToString(icon.Resolution), icon.Resolution, IconResolutionEnum);
    230                 WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, IconTheme, IconThemeToString(icon.Theme), icon.Theme, IconThemeEnum);
    231                 WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, IconSha256, icon.Sha256);
    232                 out << YAML::EndMap;
    233             }
    234             out << YAML::EndSeq;
    235         }
    236 
    237         // Generic method for handling a list of strings (i.e. tags)
    238         void ProcessStringSequence(YAML::Emitter& out, std::string_view name, std::vector<AppInstaller::Manifest::string_t> items)
    239         {
    240             if (items.empty())
    241             {
    242                 return;
    243             }
    244 
    245             out << YAML::Key << name;
    246             out << YAML::BeginSeq;
    247             for (const auto& item : items)
    248             {
    249                 if (!item.empty())
    250                 {
    251                     out << item;
    252                 }
    253             }
    254             out << YAML::EndSeq;
    255         }
    256 
    257         void ProcessLocaleFields(YAML::Emitter& out, const ManifestLocalization& manifest)
    258         {
    259             ProcessAgreements(out, manifest.Get<Localization::Agreements>());
    260             ProcessDocumentations(out, manifest.Get<Localization::Documentations>());
    261             ProcessIcons(out, manifest.Get<Localization::Icons>());
    262             ProcessStringSequence(out, Tags, manifest.Get<Localization::Tags>());
    263 
    264             WRITE_PROPERTY(out, PackageLocale, manifest.Locale);
    265             WRITE_PROPERTY_IF_EXISTS(out, Author, manifest.Get<Localization::Author>());
    266             WRITE_PROPERTY_IF_EXISTS(out, Copyright, manifest.Get<Localization::Copyright>());
    267             WRITE_PROPERTY_IF_EXISTS(out, CopyrightUrl, manifest.Get<Localization::CopyrightUrl>());
    268             WRITE_PROPERTY_IF_EXISTS(out, Description, manifest.Get<Localization::Description>());
    269             WRITE_PROPERTY_IF_EXISTS(out, ShortDescription, manifest.Get<Localization::ShortDescription>());
    270             WRITE_PROPERTY_IF_EXISTS(out, License, manifest.Get<Localization::License>());
    271             WRITE_PROPERTY_IF_EXISTS(out, LicenseUrl, manifest.Get<Localization::LicenseUrl>());
    272             WRITE_PROPERTY_IF_EXISTS(out, PackageName, manifest.Get<Localization::PackageName>());
    273             WRITE_PROPERTY_IF_EXISTS(out, PackageUrl, manifest.Get<Localization::PackageUrl>());
    274             WRITE_PROPERTY_IF_EXISTS(out, PrivacyUrl, manifest.Get<Localization::PrivacyUrl>());
    275             WRITE_PROPERTY_IF_EXISTS(out, Publisher, manifest.Get<Localization::Publisher>());
    276             WRITE_PROPERTY_IF_EXISTS(out, PublisherSupportUrl, manifest.Get<Localization::PublisherSupportUrl>());
    277             WRITE_PROPERTY_IF_EXISTS(out, PublisherUrl, manifest.Get<Localization::PublisherUrl>());
    278             WRITE_PROPERTY_IF_EXISTS(out, PurchaseUrl, manifest.Get<Localization::PurchaseUrl>());
    279             WRITE_PROPERTY_IF_EXISTS(out, ReleaseNotes, manifest.Get<Localization::ReleaseNotes>());
    280             WRITE_PROPERTY_IF_EXISTS(out, ReleaseNotesUrl, manifest.Get<Localization::ReleaseNotesUrl>());
    281             WRITE_PROPERTY_IF_EXISTS(out, InstallationNotes, manifest.Get<Localization::InstallationNotes>());
    282         }
    283 
    284         void ProcessAppsAndFeaturesEntries(YAML::Emitter& out, const std::vector<AppsAndFeaturesEntry>& appsAndFeaturesEntries)
    285         {
    286             if (appsAndFeaturesEntries.empty())
    287             {
    288                 return;
    289             }
    290 
    291             out << YAML::Key << AppsAndFeaturesEntries;
    292             out << YAML::BeginSeq;
    293             for (const auto& appsAndFeatureEntry : appsAndFeaturesEntries)
    294             {
    295                 out << YAML::BeginMap;
    296                 WRITE_PROPERTY_IF_EXISTS(out, DisplayName, appsAndFeatureEntry.DisplayName);
    297                 WRITE_PROPERTY_IF_EXISTS(out, DisplayVersion, appsAndFeatureEntry.DisplayVersion);
    298                 WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, InstallerType, InstallerTypeToString(appsAndFeatureEntry.InstallerType), appsAndFeatureEntry.InstallerType, InstallerTypeEnum);
    299                 WRITE_PROPERTY_IF_EXISTS(out, ProductCode, appsAndFeatureEntry.ProductCode);
    300                 WRITE_PROPERTY_IF_EXISTS(out, Publisher, appsAndFeatureEntry.Publisher);
    301                 WRITE_PROPERTY_IF_EXISTS(out, UpgradeCode, appsAndFeatureEntry.UpgradeCode);
    302                 out << YAML::EndMap;
    303             }
    304             out << YAML::EndSeq;
    305         }
    306 
    307         void ProcessInstallerSwitches(YAML::Emitter& out, const std::map<InstallerSwitchType, string_t>& installerSwitches)
    308         {
    309             if (installerSwitches.empty())
    310             {
    311                 return;
    312             }
    313 
    314             out << YAML::Key << InstallerSwitches;
    315             out << YAML::BeginMap;
    316             for (auto const& [type, value] : installerSwitches)
    317             {
    318                 WRITE_PROPERTY_IF_EXISTS(out, InstallerSwitchTypeToString(type), value);
    319             }
    320             out << YAML::EndMap;
    321         }
    322 
    323         void ProcessExpectedReturnCodes(YAML::Emitter& out, const std::map<DWORD, ManifestInstaller::ExpectedReturnCodeInfo>& expectedReturnCodes)
    324         {
    325             if (expectedReturnCodes.empty())
    326             {
    327                 return;
    328             }
    329 
    330             out << YAML::Key << ExpectedReturnCodes;
    331             out << YAML::BeginSeq;
    332             for (const auto& expectedReturnCode : expectedReturnCodes)
    333             {
    334                 out << YAML::BeginMap;
    335                 WRITE_PROPERTY(out, InstallerReturnCode, std::to_string(expectedReturnCode.first));
    336                 WRITE_PROPERTY(out, ReturnResponse, ExpectedReturnCodeToString(expectedReturnCode.second.ReturnResponseEnum));
    337                 WRITE_PROPERTY_IF_EXISTS(out, ReturnResponseUrl, expectedReturnCode.second.ReturnResponseUrl);
    338                 out << YAML::EndMap;
    339             }
    340             out << YAML::EndSeq;
    341         }
    342 
    343         void ProcessUnsupportedArguments(YAML::Emitter& out, const std::vector<UnsupportedArgumentEnum>& unsupportedArguments)
    344         {
    345             if (unsupportedArguments.empty())
    346             {
    347                 return;
    348             }
    349 
    350             out << YAML::Key << UnsupportedArguments;
    351             out << YAML::BeginSeq;
    352             for (auto const& unsupportedArg : unsupportedArguments)
    353             {
    354                 if (unsupportedArg != UnsupportedArgumentEnum::Unknown)
    355                 {
    356                     out << UnsupportedArgumentToString(unsupportedArg);
    357                 }
    358             }
    359             out << YAML::EndSeq;
    360         }
    361 
    362         void ProcessUnsupportedOSArchitecture(YAML::Emitter& out, const std::vector<AppInstaller::Utility::Architecture>& architectures)
    363         {
    364             if (architectures.empty())
    365             {
    366                 return;
    367             }
    368 
    369             out << YAML::Key << UnsupportedOSArchitectures;
    370             out << YAML::BeginSeq;
    371             for (auto const& architecture : architectures)
    372             {
    373                 if (architecture != AppInstaller::Utility::Architecture::Unknown)
    374                 {
    375                     out << Utility::ToLower(ToString(architecture));
    376                 }
    377             }
    378             out << YAML::EndSeq;
    379         }
    380 
    381         void ProcessInstallModes(YAML::Emitter& out, const std::vector<InstallModeEnum>& installModes)
    382         {
    383             if (installModes.empty())
    384             {
    385                 return;
    386             }
    387 
    388             out << YAML::Key << InstallModes;
    389             out << YAML::BeginSeq;
    390             for (auto const& installMode : installModes)
    391             {
    392                 if (installMode != InstallModeEnum::Unknown)
    393                 {
    394                     out << InstallModeToString(installMode);
    395                 }
    396             }
    397             out << YAML::EndSeq;
    398         }
    399 
    400         void ProcessPlatforms(YAML::Emitter& out, const std::vector<PlatformEnum>& platforms)
    401         {
    402             if (platforms.empty())
    403             {
    404                 return;
    405             }
    406             out << YAML::Key << Platform;
    407             out << YAML::BeginSeq;
    408             for (auto const& platform : platforms)
    409             {
    410                 if (platform != PlatformEnum::Unknown)
    411                 {
    412                     out << PlatformToString(platform);
    413                 }
    414             }
    415             out << YAML::EndSeq;
    416         }
    417 
    418         void ProcessInstallerSuccessCodes(YAML::Emitter& out, const std::vector<DWORD>& installerSuccessCodes)
    419         {
    420             if (installerSuccessCodes.empty())
    421             {
    422                 return;
    423             }
    424 
    425             out << YAML::Key << InstallerSuccessCodes;
    426             out << YAML::BeginSeq;
    427             for (auto const& installerSuccessCode : installerSuccessCodes)
    428             {
    429                 out << std::to_string(installerSuccessCode);
    430             }
    431             out << YAML::EndSeq;
    432         }
    433 
    434         void ProcessMarkets(YAML::Emitter& out, const MarketsInfo& marketsInfo)
    435         {
    436             if (marketsInfo.AllowedMarkets.empty() && marketsInfo.ExcludedMarkets.empty())
    437             {
    438                 return;
    439             }
    440 
    441             out << YAML::Key << Markets;
    442             out << YAML::BeginMap;
    443             ProcessStringSequence(out, AllowedMarkets, marketsInfo.AllowedMarkets);
    444             ProcessStringSequence(out, ExcludedMarkets, marketsInfo.ExcludedMarkets);
    445             out << YAML::EndMap;
    446         }
    447 
    448         void ProcessNestedInstallerFiles(YAML::Emitter& out, const std::vector<NestedInstallerFile>& nestedInstallerFiles)
    449         {
    450             if (nestedInstallerFiles.empty())
    451             {
    452                 return;
    453             }
    454 
    455             out << YAML::Key << NestedInstallerFiles;
    456             out << YAML::BeginSeq;
    457             for (const auto& nestedInstallerFile : nestedInstallerFiles)
    458             {
    459                 out << YAML::BeginMap;
    460                 WRITE_PROPERTY(out, NestedInstallerFileRelativeFilePath, nestedInstallerFile.RelativeFilePath);
    461                 WRITE_PROPERTY_IF_EXISTS(out, PortableCommandAlias, nestedInstallerFile.PortableCommandAlias);
    462                 out << YAML::EndMap;
    463             }
    464             out << YAML::EndSeq;
    465         }
    466 
    467         void ProcessInstallationMetadataInstalledFiles(YAML::Emitter& out, const std::vector<InstalledFile>& installedFiles)
    468         {
    469             if (installedFiles.empty())
    470             {
    471                 return;
    472             }
    473 
    474             out << YAML::Key << InstallationMetadataFiles;
    475             out << YAML::BeginSeq;
    476             for (const auto& installedFile : installedFiles)
    477             {
    478                 out << YAML::BeginMap;
    479                 WRITE_PROPERTY(out, InstallationMetadataRelativeFilePath, installedFile.RelativeFilePath);
    480                 WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, FileSha256, installedFile.FileSha256);
    481                 WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, FileType, InstalledFileTypeToString(installedFile.FileType), installedFile.FileType, InstalledFileTypeEnum);
    482                 WRITE_PROPERTY_IF_EXISTS(out, InvocationParameter, installedFile.InvocationParameter);
    483                 WRITE_PROPERTY_IF_EXISTS(out, DisplayName, installedFile.DisplayName);
    484 
    485                 out << YAML::EndMap;
    486             }
    487             out << YAML::EndSeq;
    488         }
    489 
    490         void ProcessInstallationMetadata(YAML::Emitter& out, const InstallationMetadataInfo& installationMetadata)
    491         {
    492             if (installationMetadata.DefaultInstallLocation.empty() && installationMetadata.Files.empty())
    493             {
    494                 return;
    495             }
    496 
    497             out << YAML::Key << InstallationMetadata;
    498             out << YAML::BeginMap;
    499             WRITE_PROPERTY_IF_EXISTS(out, DefaultInstallLocation, installationMetadata.DefaultInstallLocation);
    500             ProcessInstallationMetadataInstalledFiles(out, installationMetadata.Files);
    501             out << YAML::EndMap;
    502         }
    503 
    504         void ProcessAuthentication(YAML::Emitter& out, const Authentication::AuthenticationInfo& authInfo)
    505         {
    506             if (authInfo.Type == Authentication::AuthenticationType::None)
    507             {
    508                 return;
    509             }
    510 
    511             out << YAML::Key << Authentication;
    512             out << YAML::BeginMap;
    513             WRITE_PROPERTY(out, AuthenticationType, Authentication::AuthenticationTypeToString(authInfo.Type));
    514             if (authInfo.MicrosoftEntraIdInfo)
    515             {
    516                 out << YAML::Key << MicrosoftEntraIdAuthenticationInfo;
    517                 out << YAML::BeginMap;
    518                 WRITE_PROPERTY_IF_EXISTS(out, MicrosoftEntraIdResource, authInfo.MicrosoftEntraIdInfo->Resource);
    519                 WRITE_PROPERTY_IF_EXISTS(out, MicrosoftEntraIdScope, authInfo.MicrosoftEntraIdInfo->Scope);
    520                 out << YAML::EndMap;
    521             }
    522             out << YAML::EndMap;
    523         }
    524 
    525         void ProcessDependencies(YAML::Emitter& out, const DependencyList& dependencies)
    526         {
    527             if (!dependencies.HasAny())
    528             {
    529                 return;
    530             }
    531 
    532             out << YAML::Key << Dependencies;
    533             out << YAML::BeginMap;
    534 
    535             if (dependencies.HasAnyOf(DependencyType::WindowsFeature))
    536             {
    537                 out << YAML::Key << WindowsFeatures;
    538                 out << YAML::BeginSeq;
    539                 dependencies.ApplyToType(DependencyType::WindowsFeature, [&out](Dependency dependency)
    540                     {
    541                         out << dependency.Id();
    542                     });
    543                 out << YAML::EndSeq;
    544             }
    545 
    546             if (dependencies.HasAnyOf(DependencyType::WindowsLibrary))
    547             {
    548                 out << YAML::Key << WindowsLibraries;
    549                 out << YAML::BeginSeq;
    550                 dependencies.ApplyToType(DependencyType::WindowsLibrary, [&out](Dependency dependency)
    551                     {
    552                         out << dependency.Id();
    553                     });
    554                 out << YAML::EndSeq;
    555             }
    556 
    557             if (dependencies.HasAnyOf(DependencyType::Package))
    558             {
    559                 out << YAML::Key << PackageDependencies;
    560                 out << YAML::BeginSeq;
    561                 dependencies.ApplyToType(DependencyType::Package, [&out](Dependency dependency)
    562                     {
    563                         out << YAML::BeginMap;
    564                         WRITE_PROPERTY(out, PackageIdentifier, dependency.Id());
    565 
    566                         if (dependency.MinVersion.has_value())
    567                         {
    568                             WRITE_PROPERTY_IF_EXISTS(out, MinimumVersion, dependency.MinVersion.value().ToString());
    569                         }
    570 
    571                         out << YAML::EndMap;
    572                     });
    573                 out << YAML::EndSeq;
    574             }
    575 
    576             if (dependencies.HasAnyOf(DependencyType::External))
    577             {
    578                 out << YAML::Key << ExternalDependencies;
    579                 out << YAML::BeginSeq;
    580                 dependencies.ApplyToType(DependencyType::External, [&out](Dependency dependency)
    581                     {
    582                         out << dependency.Id();
    583                     });
    584                 out << YAML::EndSeq;
    585             }
    586 
    587             out << YAML::EndMap;
    588         }
    589 
    590         void ProcessInstallerFields(YAML::Emitter& out, const ManifestInstaller& installer)
    591         {
    592             WRITE_PROPERTY(out, Architecture, Utility::ToLower(ToString(installer.Arch)));
    593             WRITE_PROPERTY(out, InstallerType, InstallerTypeToString(installer.BaseInstallerType));
    594             WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, NestedInstallerType, InstallerTypeToString(installer.NestedInstallerType), installer.NestedInstallerType, InstallerTypeEnum);
    595             WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, InstallerSha256, installer.Sha256);
    596             WRITE_SHA256_PROPERTY_IF_NOT_EMPTY(out, SignatureSha256, installer.SignatureSha256);
    597             WRITE_PROPERTY_IF_EXISTS(out, InstallerUrl, installer.Url);
    598             WRITE_PROPERTY_IF_EXISTS(out, MSStoreProductIdentifier, installer.ProductId);
    599 
    600             WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, Scope, Utility::ToLower(ScopeToString(installer.Scope)), installer.Scope, ScopeEnum);
    601             WRITE_PROPERTY_IF_EXISTS(out, InstallerLocale, installer.Locale);
    602             WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, ElevationRequirement, ElevationRequirementToString(installer.ElevationRequirement), installer.ElevationRequirement, ElevationRequirementEnum);
    603             WRITE_PROPERTY_IF_EXISTS(out, PackageFamilyName, installer.PackageFamilyName);
    604             WRITE_PROPERTY_IF_EXISTS(out, ReleaseDate, installer.ReleaseDate);
    605             WRITE_BOOL_PROPERTY(out, InstallerAbortsTerminal, installer.InstallerAbortsTerminal);
    606             WRITE_BOOL_PROPERTY(out, InstallLocationRequired, installer.InstallLocationRequired);
    607             WRITE_BOOL_PROPERTY(out, RequireExplicitUpgrade, installer.RequireExplicitUpgrade);
    608             WRITE_BOOL_PROPERTY(out, DisplayInstallWarnings, installer.DisplayInstallWarnings);
    609             WRITE_BOOL_PROPERTY(out, DownloadCommandProhibited, installer.DownloadCommandProhibited);
    610             WRITE_BOOL_PROPERTY(out, ArchiveBinariesDependOnPath, installer.ArchiveBinariesDependOnPath);
    611             WRITE_PROPERTY_IF_EXISTS(out, MinimumOSVersion, installer.MinOSVersion);
    612             WRITE_PROPERTY_IF_EXISTS(out, ProductCode, installer.ProductCode);
    613             WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, UpgradeBehavior, UpdateBehaviorToString(installer.UpdateBehavior), installer.UpdateBehavior, UpdateBehaviorEnum);
    614             WRITE_ENUM_PROPERTY_IF_NOT_UNKNOWN(out, RepairBehavior, RepairBehaviorToString(installer.RepairBehavior), installer.RepairBehavior, RepairBehaviorEnum);
    615 
    616             ProcessStringSequence(out, Capabilities, installer.Capabilities);
    617             ProcessStringSequence(out, Commands, installer.Commands);
    618             ProcessStringSequence(out, FileExtensions, installer.FileExtensions);
    619             ProcessStringSequence(out, Protocols, installer.Protocols);
    620             ProcessStringSequence(out, RestrictedCapabilities, installer.RestrictedCapabilities);
    621 
    622             ProcessAppsAndFeaturesEntries(out, installer.AppsAndFeaturesEntries);
    623             ProcessDependencies(out, installer.Dependencies);
    624             ProcessExpectedReturnCodes(out, installer.ExpectedReturnCodes);
    625             ProcessInstallerSwitches(out, installer.Switches);
    626             ProcessInstallationMetadata(out, installer.InstallationMetadata);
    627             ProcessInstallerSuccessCodes(out, installer.InstallerSuccessCodes);
    628             ProcessInstallModes(out, installer.InstallModes);
    629             ProcessMarkets(out, installer.Markets);
    630             ProcessNestedInstallerFiles(out, installer.NestedInstallerFiles);
    631             ProcessPlatforms(out, installer.Platform);
    632             ProcessUnsupportedArguments(out, installer.UnsupportedArguments);
    633             ProcessUnsupportedOSArchitecture(out, installer.UnsupportedOSArchitectures);
    634             ProcessAuthentication(out, installer.AuthInfo);
    635         }
    636 
    637         void ProcessInstaller(YAML::Emitter& out, const ManifestInstaller& installer)
    638         {
    639             out << YAML::Key << Installers;
    640             out << YAML::BeginSeq;
    641             out << YAML::BeginMap;
    642             ProcessInstallerFields(out, installer);
    643             out << YAML::EndMap;
    644             out << YAML::EndSeq;
    645         }
    646 
    647         void ProcessLocalizations(YAML::Emitter& out, const std::vector<ManifestLocalization>& localizations)
    648         {
    649             if (!localizations.empty())
    650             {
    651                 out << YAML::Key << Localization;
    652                 out << YAML::BeginSeq;
    653 
    654                 for (const auto& localization : localizations)
    655                 {
    656                     out << YAML::BeginMap;
    657                     ProcessLocaleFields(out, localization);
    658                     out << YAML::EndMap;
    659                 }
    660 
    661                 out << YAML::EndSeq;
    662             }
    663         }
    664 
    665         void PopulateManifestYamlEmitter(YAML::Emitter& out, const Manifest& manifest, const ManifestInstaller& installer)
    666         {
    667             // Currently, exporting the yaml only supports outputting a single installer.
    668             // TODO: If no single installer is provided, output all installers.
    669             out << YAML::BeginMap;
    670             WRITE_PROPERTY(out, PackageIdentifier, manifest.Id);
    671             WRITE_PROPERTY(out, PackageVersion, manifest.Version);
    672             WRITE_PROPERTY_IF_EXISTS(out, Channel, manifest.Channel);
    673             WRITE_PROPERTY_IF_EXISTS(out, Moniker, manifest.Moniker);
    674             ProcessLocaleFields(out, manifest.DefaultLocalization);
    675             ProcessLocalizations(out, manifest.Localizations);
    676             ProcessInstaller(out, installer);
    677             WRITE_PROPERTY(out, ManifestVersion, manifest.ManifestVersion.ToString());
    678 
    679             ManifestTypeEnum manifestType = ManifestTypeEnum::Merged;
    680             WRITE_PROPERTY(out, ManifestType, ManifestTypeToString(manifestType));
    681             out << YAML::EndMap;
    682         }
    683     }
    684 
    685     std::string YamlWriter::ManifestToYamlString(const Manifest& manifest, const ManifestInstaller& installer)
    686     {
    687         YAML::Emitter out;
    688         PopulateManifestYamlEmitter(out, manifest, installer);
    689         return out.str();
    690     }
    691 
    692     void YamlWriter::OutputYamlFile(const Manifest& manifest, const ManifestInstaller& installer, const std::filesystem::path& out)
    693     {
    694         const std::filesystem::path& parentDirectory = out.parent_path();
    695 
    696         if (!std::filesystem::exists(parentDirectory))
    697         {
    698             std::filesystem::create_directories(parentDirectory);
    699         }
    700         else
    701         {
    702             THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_CANNOT_MAKE), !std::filesystem::is_directory(parentDirectory));
    703         }
    704 
    705         YAML::Emitter emitter;
    706         PopulateManifestYamlEmitter(emitter, manifest, installer);
    707 
    708         std::ofstream outFileStream(out);
    709         emitter.Emit(outFileStream);
    710         outFileStream.close();
    711     }
    712 }