diff --git a/src/AppInstallerCLITests/CompositeSource.cpp b/src/AppInstallerCLITests/CompositeSource.cpp index 28c98ba198..24659ea796 100644 --- a/src/AppInstallerCLITests/CompositeSource.cpp +++ b/src/AppInstallerCLITests/CompositeSource.cpp @@ -166,11 +166,26 @@ struct CompositeTestSetup Composite.AddAvailableSource(Source{ Available }); } - SearchResult Search() + SearchResult Search(bool disableDataChecks = false) { + size_t initialCountOfCallsRequiringVersionData = Available->CountOfCallsRequiringVersionData; + size_t initialCountOfCallsRequiringManifestData = Available->CountOfCallsRequiringManifestData; + SearchRequest request; request.Query = RequestMatch(MatchType::Exact, s_Everything_Query); - return Composite.Search(request); + auto result = Composite.Search(request); + + // We want to prevent calls to these functions for Search as they can require network or I/O activity. + size_t countOfCallsRequiringVersionData = Available->CountOfCallsRequiringVersionData - initialCountOfCallsRequiringVersionData; + size_t countOfCallsRequiringManifestData = Available->CountOfCallsRequiringManifestData - initialCountOfCallsRequiringManifestData; + if (!disableDataChecks && (countOfCallsRequiringVersionData || countOfCallsRequiringManifestData)) + { + std::ostringstream stream; + stream << "Version data calls [" << countOfCallsRequiringVersionData << "] : Manifest data calls [" << countOfCallsRequiringManifestData << "]"; + FAIL(stream.str()); + } + + return result; } TestPackageHelper MakeInstalled(std::shared_ptr source) @@ -572,7 +587,8 @@ TEST_CASE("CompositePackage_AvailableVersions_ChannelFilteredOut", "[CompositeSo return result; }; - SearchResult result = setup.Search(); + // Disable data checks as we call one of the data methods as validation in the search + SearchResult result = setup.Search(true); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); @@ -614,7 +630,8 @@ TEST_CASE("CompositePackage_AvailableVersions_NoChannelFilteredOut", "[Composite return result; }; - SearchResult result = setup.Search(); + // Disable data checks as we call one of the data methods as validation in the search + SearchResult result = setup.Search(true); REQUIRE(result.Matches.size() == 1); REQUIRE(result.Matches[0].Package->GetAvailable().size() == 1); diff --git a/src/AppInstallerCLITests/SQLiteIndexSource.cpp b/src/AppInstallerCLITests/SQLiteIndexSource.cpp index d02d7f5019..b3be2698e2 100644 --- a/src/AppInstallerCLITests/SQLiteIndexSource.cpp +++ b/src/AppInstallerCLITests/SQLiteIndexSource.cpp @@ -233,3 +233,29 @@ TEST_CASE("SQLiteIndexSource_IsSame", "[sqliteindexsource]") REQUIRE(result1.Matches[0].Package->GetAvailable()[0]->IsSame(result2.Matches[0].Package->GetAvailable()[0].get())); } + +TEST_CASE("SQLiteIndexSource_Package_ProductCodes", "[sqliteindexsource]") +{ + TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; + INFO("Using temporary file named: " << tempFile.GetPath()); + + SourceDetails details; + Manifest manifest; + std::string relativePath; + std::shared_ptr source = SimpleTestSetup(tempFile, details, manifest, relativePath); + + SearchRequest request; + request.Query = RequestMatch(MatchType::Exact, manifest.Id); + + auto results = source->Search(request); + REQUIRE(results.Matches.size() == 1); + REQUIRE(results.Matches[0].Package); + + auto package = results.Matches[0].Package->GetAvailable()[0]; + + auto manifestPCs = manifest.GetProductCodes(); + auto propertyPCs = package->GetMultiProperty(PackageMultiProperty::ProductCode); + REQUIRE(manifestPCs.size() == 1); + REQUIRE(propertyPCs.size() == 1); + REQUIRE(manifestPCs[0] == propertyPCs[0].get()); +} diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index cb8ad0c75f..6544f99395 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -16,6 +16,19 @@ namespace TestCommon static std::atomic_size_t packageId(0); return ++packageId; } + + TestSource* GetTestSourceFromWeakPtr(const std::weak_ptr& weakSource) + { + if (auto source = weakSource.lock()) + { + if (auto testSource = const_cast(source.get())->CastTo(TestSource::SourceType)) + { + return reinterpret_cast(testSource); + } + } + + return nullptr; + } } TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr source) : @@ -100,6 +113,11 @@ namespace TestCommon TestPackageVersion::Manifest TestPackageVersion::GetManifest() { + if (auto source = GetTestSource()) + { + source->IncrementCountOfCallsRequiringManifestData(); + } + return VersionManifest; } @@ -126,6 +144,11 @@ namespace TestCommon } } + TestSource* TestPackageVersion::GetTestSource() const + { + return GetTestSourceFromWeakPtr(Source); + } + TestPackage::TestPackage(const std::vector& available, std::weak_ptr source, bool hideSystemReferenceStringsOnVersion) : Source(source) { @@ -168,8 +191,34 @@ namespace TestCommon } } + std::vector TestPackage::GetMultiProperty(PackageMultiProperty property) const + { + std::vector result; + PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property); + + for (const auto& version : Versions) + { + for (auto&& string : version->GetMultiProperty(mappedProperty)) + { + auto itr = std::lower_bound(result.begin(), result.end(), string); + + if (itr == result.end() || *itr != string) + { + result.emplace(itr, std::move(string)); + } + } + } + + return result; + } + std::vector TestPackage::GetVersionKeys() const { + if (auto source = GetTestSource()) + { + source->IncrementCountOfCallsRequiringVersionData(); + } + std::vector result; for (const auto& version : Versions) { @@ -190,6 +239,14 @@ namespace TestCommon std::shared_ptr TestPackage::GetVersion(const PackageVersionKey& versionKey) const { + if (!versionKey.IsDefaultLatest()) + { + if (auto source = GetTestSource()) + { + source->IncrementCountOfCallsRequiringVersionData(); + } + } + for (const auto& version : Versions) { if ((versionKey.Version.empty() || versionKey.Version == version->GetProperty(PackageVersionProperty::Version).get()) && @@ -234,6 +291,11 @@ namespace TestCommon return nullptr; } + TestSource* TestPackage::GetTestSource() const + { + return GetTestSourceFromWeakPtr(Source); + } + TestCompositePackage::TestCompositePackage(const std::vector& available, std::weak_ptr source, bool hideSystemReferenceStringsOnVersion) { if (!available.empty()) @@ -332,6 +394,16 @@ namespace TestCommon return nullptr; } + void TestSource::IncrementCountOfCallsRequiringVersionData() + { + ++CountOfCallsRequiringVersionData; + } + + void TestSource::IncrementCountOfCallsRequiringManifestData() + { + ++CountOfCallsRequiringManifestData; + } + std::string_view TestSourceFactory::TypeName() const { return "*TestSource"sv; diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h index 27033c45bf..bc5aca74f7 100644 --- a/src/AppInstallerCLITests/TestSource.h +++ b/src/AppInstallerCLITests/TestSource.h @@ -10,6 +10,8 @@ namespace TestCommon { + struct TestSource; + // IPackageVersion for TestSource struct TestPackageVersion : public AppInstaller::Repository::IPackageVersion { @@ -40,6 +42,7 @@ namespace TestCommon protected: static void AddIfHasValueAndNotPresent(const AppInstaller::Utility::NormalizedString& value, std::vector& target, bool folded = false); + TestSource* GetTestSource() const; }; // IPackage for TestSource @@ -65,6 +68,7 @@ namespace TestCommon } AppInstaller::Utility::LocIndString GetProperty(AppInstaller::Repository::PackageProperty property) const override; + std::vector GetMultiProperty(AppInstaller::Repository::PackageMultiProperty property) const override; std::vector GetVersionKeys() const override; std::shared_ptr GetLatestVersion() const override; std::shared_ptr GetVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; @@ -76,6 +80,9 @@ namespace TestCommon std::weak_ptr Source; size_t DefaultIsSameIdentity = 0; std::function IsSameOverride; + + protected: + TestSource* GetTestSource() const; }; // ICompositePackage for TestSource @@ -127,6 +134,13 @@ namespace TestCommon TestSource() = default; TestSource(const AppInstaller::Repository::SourceDetails& details) : Details(details) {} + + // Tracking for potential network impacts + void IncrementCountOfCallsRequiringVersionData(); + size_t CountOfCallsRequiringVersionData = 0; + + void IncrementCountOfCallsRequiringManifestData(); + size_t CountOfCallsRequiringManifestData = 0; }; struct TestSourceReference : public AppInstaller::Repository::ISourceReference diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 7bbbe2c8a7..d8f81eb923 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -369,6 +369,26 @@ namespace AppInstaller::Repository return m_packages[m_versionKeyData[0].PackageIndex]->GetProperty(property); } + std::vector GetMultiProperty(PackageMultiProperty property) const override + { + std::vector result; + + for (const auto& package : m_packages) + { + for (auto&& string : package->GetMultiProperty(property)) + { + auto itr = std::lower_bound(result.begin(), result.end(), string); + + if (itr == result.end() || *itr != string) + { + result.emplace(itr, std::move(string)); + } + } + } + + return result; + } + std::vector GetVersionKeys() const override { return { m_versionKeyData.begin(), m_versionKeyData.end() }; @@ -897,48 +917,40 @@ namespace AppInstaller::Repository trackingRequest.Filters.emplace_back(PackageMatchField::Id, MatchType::CaseInsensitive, identifier.get()); SearchResult trackingResult = trackingCatalog.Search(trackingRequest); + std::shared_ptr result; if (trackingResult.Matches.size() == 1) { - std::shared_ptr result = OnlyAvailable(trackingResult.Matches[0].Package); + result = OnlyAvailable(trackingResult.Matches[0].Package); AddSystemReferenceStrings(result.get()); - return result; } - else + else if (trackingResult.Matches.size() > 1) { - AICLI_LOG(Repo, Warning, << "Found multiple results for Id [" << identifier << "] in tracking catalog for: " << sourceIdentifier); - return {}; + AICLI_LOG(Repo, Warning, << "Found " << trackingResult.Matches.size() << " results for Id [" << identifier << "] in tracking catalog for: " << sourceIdentifier); } - } - void AddSystemReferenceStrings(IPackage* package) - { - for (auto const& versionKey : package->GetVersionKeys()) - { - auto version = package->GetVersion(versionKey); - AddSystemReferenceStrings(version.get()); - } + return result; } - void AddSystemReferenceStrings(IPackageVersion* version) + void AddSystemReferenceStrings(IPackage* package) { GetSystemReferenceStrings( - version, - PackageVersionMultiProperty::PackageFamilyName, + package, + PackageMultiProperty::PackageFamilyName, PackageMatchField::PackageFamilyName); GetSystemReferenceStrings( - version, - PackageVersionMultiProperty::ProductCode, + package, + PackageMultiProperty::ProductCode, PackageMatchField::ProductCode); GetSystemReferenceStrings( - version, - PackageVersionMultiProperty::UpgradeCode, + package, + PackageMultiProperty::UpgradeCode, PackageMatchField::UpgradeCode); GetNameAndPublisher( - version); + package); } void AddSystemReferenceStringsFromManifest(const Manifest::Manifest& manifest) @@ -969,33 +981,33 @@ namespace AppInstaller::Repository private: void GetSystemReferenceStrings( - IPackageVersion* installedVersion, - PackageVersionMultiProperty prop, + IPackage* package, + PackageMultiProperty prop, PackageMatchField field) { - for (auto&& string : installedVersion->GetMultiProperty(prop)) + for (auto&& string : package->GetMultiProperty(prop)) { AddIfNotPresent(SystemReferenceString{ field, std::move(string) }); } } void GetNameAndPublisher( - IPackageVersion* installedVersion) + IPackage* package) { // Unfortunately the names and publishers are unique and not tied to each other strictly, so we need // to go broad on the matches. Future work can hopefully make name and publisher operate more as a unit, // but for now we have to search for the cartesian of these... - auto names = installedVersion->GetMultiProperty(PackageVersionMultiProperty::Name); - auto publishers = installedVersion->GetMultiProperty(PackageVersionMultiProperty::Publisher); + auto names = package->GetMultiProperty(PackageMultiProperty::NormalizedName); + auto publishers = package->GetMultiProperty(PackageMultiProperty::NormalizedPublisher); - for (size_t i = 0; i < names.size(); ++i) + for (const auto& name : names) { - for (size_t j = 0; j < publishers.size(); ++j) + for (const auto& publisher : publishers) { AddIfNotPresent(SystemReferenceString{ PackageMatchField::NormalizedNameAndPublisher, - names[i], - publishers[j] }); + name, + publisher }); } } } @@ -1031,20 +1043,27 @@ namespace AppInstaller::Repository } PackageData result; - constexpr int c_downloadManifestsLimit = 3; - int manifestsDownloaded = 0; - for (auto const& versionKey : availablePackage->GetVersionKeys()) - { - auto packageVersion = availablePackage->GetVersion(versionKey); - result.AddSystemReferenceStrings(packageVersion.get()); + result.AddSystemReferenceStrings(availablePackage.get()); - if (downloadManifests && manifestsDownloaded < c_downloadManifestsLimit) + if (downloadManifests) + { + constexpr int c_downloadManifestsLimit = 3; + int manifestsDownloaded = 0; + for (auto const& versionKey : availablePackage->GetVersionKeys()) { + auto packageVersion = availablePackage->GetVersion(versionKey); + auto manifest = packageVersion->GetManifest(); result.AddSystemReferenceStringsFromManifest(manifest); manifestsDownloaded++; + + if (manifestsDownloaded >= c_downloadManifestsLimit) + { + break; + } } } + return result; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.cpp index 8362e78deb..651f2b014b 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.cpp @@ -115,6 +115,28 @@ namespace AppInstaller::Repository::Microsoft::details::V1 return result; } + std::vector SQLitePackage::GetMultiProperty(PackageMultiProperty property) const + { + std::shared_ptr source = GetReferenceSource(); + std::vector result; + PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property); + + for (const auto& version : source->GetIndex().GetVersionKeysById(m_idId)) + { + for (auto&& string : source->GetIndex().GetMultiPropertyByPrimaryId(version.ManifestId, mappedProperty)) + { + auto itr = std::lower_bound(result.begin(), result.end(), string); + + if (itr == result.end() || itr->get() != string) + { + result.emplace(itr, std::move(string)); + } + } + } + + return result; + } + std::vector SQLitePackage::GetVersionKeys() const { std::shared_ptr source = GetReferenceSource(); diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.h index 70d0ff3f4e..164daa6832 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.h +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.h @@ -16,6 +16,8 @@ namespace AppInstaller::Repository::Microsoft::details::V1 // Inherited via IPackage Utility::LocIndString GetProperty(PackageProperty property) const; + std::vector GetMultiProperty(PackageMultiProperty property) const override; + std::vector GetVersionKeys() const override; std::shared_ptr GetLatestVersion() const override; diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.cpp index ebd98eee2b..05e2d025ae 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.cpp @@ -445,6 +445,19 @@ namespace AppInstaller::Repository::Microsoft::details::V2 return LocIndString{ result ? std::move(result).value() : std::string{} }; } + std::vector SQLitePackage::GetMultiProperty(PackageMultiProperty property) const + { + std::vector result; + + for (auto&& value : GetReferenceSource()->GetIndex().GetMultiPropertyByPrimaryId(m_packageRowId, PackageMultiPropertyToPackageVersionMultiProperty(property))) + { + // Values coming from the index will always be localized/independent. + result.emplace_back(std::move(value)); + } + + return result; + } + std::vector SQLitePackage::GetVersionKeys() const { std::shared_ptr source = GetReferenceSource(); diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.h b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.h index d8476142d4..2eb04a5c1e 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.h +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.h @@ -22,6 +22,8 @@ namespace AppInstaller::Repository::Microsoft::details::V2 // Inherited via IPackage Utility::LocIndString GetProperty(PackageProperty property) const; + std::vector GetMultiProperty(PackageMultiProperty property) const override; + std::vector GetVersionKeys() const override; std::shared_ptr GetLatestVersion() const override; diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index 7491521b24..3ad4fff9cf 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -281,6 +281,28 @@ namespace AppInstaller::Repository Name, }; + // A property of a package that can have multiple values. + enum class PackageMultiProperty + { + // The package family names (PFN) associated with the package. + PackageFamilyName, + // The product codes associated with the package. + ProductCode, + // The upgrade codes associated with the package. + UpgradeCode, + // The normalized names for the package. + NormalizedName, + // The normalized publisher names for the package. + NormalizedPublisher, + // The tags associated with the package. + Tag, + // The commands associated with the package. + Command, + }; + + // Maps the package multi-property value to its package version multi-property value for internal use. + PackageVersionMultiProperty PackageMultiPropertyToPackageVersionMultiProperty(PackageMultiProperty property); + // To allow for runtime casting from IPackage to the specific types, this enum contains all of the IPackage implementations. enum class IPackageType { @@ -317,6 +339,9 @@ namespace AppInstaller::Repository // Gets a property of this package. virtual Utility::LocIndString GetProperty(PackageProperty property) const = 0; + // Gets a property of this package that can have multiple values. + virtual std::vector GetMultiProperty(PackageMultiProperty property) const = 0; + // Gets the source that this package is from. virtual Source GetSource() const = 0; diff --git a/src/AppInstallerRepositoryCore/RepositorySearch.cpp b/src/AppInstallerRepositoryCore/RepositorySearch.cpp index eb988fafb3..5b8b93978c 100644 --- a/src/AppInstallerRepositoryCore/RepositorySearch.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySearch.cpp @@ -105,6 +105,22 @@ namespace AppInstaller::Repository return Version.empty() && Channel.empty(); } + PackageVersionMultiProperty PackageMultiPropertyToPackageVersionMultiProperty(PackageMultiProperty property) + { + switch (property) + { + case PackageMultiProperty::PackageFamilyName: return PackageVersionMultiProperty::PackageFamilyName; + case PackageMultiProperty::ProductCode: return PackageVersionMultiProperty::ProductCode; + case PackageMultiProperty::UpgradeCode: return PackageVersionMultiProperty::UpgradeCode; + case PackageMultiProperty::NormalizedName: return PackageVersionMultiProperty::Name; + case PackageMultiProperty::NormalizedPublisher: return PackageVersionMultiProperty::Publisher; + case PackageMultiProperty::Tag: return PackageVersionMultiProperty::Tag; + case PackageMultiProperty::Command: return PackageVersionMultiProperty::Command; + default: + THROW_HR_MSG(E_UNEXPECTED, "PackageMultiProperty must map to a PackageVersionMultiProperty"); + } + } + const char* UnsupportedRequestException::what() const noexcept { if (m_whatMessage.empty()) diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index 0bba3c84dc..198c301905 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -54,6 +54,8 @@ namespace AppInstaller::Repository::Rest } } + std::vector GetMultiProperty(PackageMultiProperty property) const override; + std::vector GetVersionKeys() const override { std::shared_ptr source = GetReferenceSource(); @@ -195,6 +197,99 @@ namespace AppInstaller::Repository::Rest mutable std::mutex m_packageVersionsLock; }; + void GetMultiPropertyValues( + const RestPackage* package, + const IRestClient::VersionInfo& versionInfo, + PackageVersionMultiProperty property, + std::vector& result, + void (*Action)(std::vector&, Utility::LocIndString&&)) + { + switch (property) + { + case PackageVersionMultiProperty::PackageFamilyName: + for (const std::string& pfn : versionInfo.PackageFamilyNames) + { + Action(result, Utility::LocIndString{ pfn }); + } + break; + case PackageVersionMultiProperty::ProductCode: + for (const std::string& productCode : versionInfo.ProductCodes) + { + Action(result, Utility::LocIndString{ productCode }); + } + break; + case PackageVersionMultiProperty::UpgradeCode: + for (const std::string& upgradeCode : versionInfo.UpgradeCodes) + { + Action(result, Utility::LocIndString{ upgradeCode }); + } + break; + case PackageVersionMultiProperty::Name: + if (versionInfo.Manifest) + { + for (auto&& name : versionInfo.Manifest->GetPackageNames()) + { + Action(result, Utility::LocIndString{ std::move(name) }); + } + } + else + { + Action(result, Utility::LocIndString{ package->PackageInfo().PackageName }); + } + break; + case PackageVersionMultiProperty::Publisher: + if (versionInfo.Manifest) + { + for (auto&& publisher : versionInfo.Manifest->GetPublishers()) + { + Action(result, Utility::LocIndString{ std::move(publisher) }); + } + } + else + { + Action(result, Utility::LocIndString{ package->PackageInfo().Publisher }); + } + break; + case PackageVersionMultiProperty::Locale: + if (versionInfo.Manifest) + { + Action(result, Utility::LocIndString{ versionInfo.Manifest->DefaultLocalization.Locale }); + for (const auto& loc : versionInfo.Manifest->Localizations) + { + Action(result, Utility::LocIndString{ loc.Locale }); + } + } + break; + } + } + + std::vector RestPackage::GetMultiProperty(PackageMultiProperty property) const + { + std::scoped_lock versionsLock{ m_packageVersionsLock }; + std::vector result; + PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property); + + for (const auto& versionInfo : m_package.Versions) + { + GetMultiPropertyValues( + this, + versionInfo, + mappedProperty, + result, + [](std::vector& result, Utility::LocIndString&& string) + { + auto itr = std::lower_bound(result.begin(), result.end(), string); + + if (itr == result.end() || *itr != string) + { + result.emplace(itr, std::move(string)); + } + }); + } + + return result; + } + // The IPackageVersion impl for RestSource. struct PackageVersion : public SourceReference, public IPackageVersion { @@ -257,63 +352,16 @@ namespace AppInstaller::Repository::Rest std::vector GetMultiProperty(PackageVersionMultiProperty property) const override { std::vector result; - switch (property) - { - case PackageVersionMultiProperty::PackageFamilyName: - for (std::string pfn : m_versionInfo.PackageFamilyNames) - { - result.emplace_back(Utility::LocIndString{ pfn }); - } - break; - case PackageVersionMultiProperty::ProductCode: - for (std::string productCode : m_versionInfo.ProductCodes) - { - result.emplace_back(Utility::LocIndString{ productCode }); - } - break; - case PackageVersionMultiProperty::UpgradeCode: - for (std::string upgradeCode : m_versionInfo.UpgradeCodes) - { - result.emplace_back(Utility::LocIndString{ upgradeCode }); - } - break; - case PackageVersionMultiProperty::Name: - if (m_versionInfo.Manifest) - { - for (auto name : m_versionInfo.Manifest->GetPackageNames()) - { - result.emplace_back(std::move(name)); - } - } - else - { - result.emplace_back(m_package->PackageInfo().PackageName); - } - break; - case PackageVersionMultiProperty::Publisher: - if (m_versionInfo.Manifest) - { - for (auto publisher : m_versionInfo.Manifest->GetPublishers()) - { - result.emplace_back(std::move(publisher)); - } - } - else - { - result.emplace_back(m_package->PackageInfo().Publisher); - } - break; - case PackageVersionMultiProperty::Locale: - if (m_versionInfo.Manifest) + + GetMultiPropertyValues( + m_package.get(), + m_versionInfo, + property, + result, + [](std::vector& result, Utility::LocIndString&& string) { - result.emplace_back(m_versionInfo.Manifest->DefaultLocalization.Locale); - for (const auto& loc : m_versionInfo.Manifest->Localizations) - { - result.emplace_back(loc.Locale); - } - } - break; - } + result.emplace_back(std::move(string)); + }); return result; } diff --git a/src/AppInstallerSharedLib/Public/winget/LocIndependent.h b/src/AppInstallerSharedLib/Public/winget/LocIndependent.h index 35a6220c20..abdec52e21 100644 --- a/src/AppInstallerSharedLib/Public/winget/LocIndependent.h +++ b/src/AppInstallerSharedLib/Public/winget/LocIndependent.h @@ -47,6 +47,7 @@ namespace AppInstaller::Utility bool operator!=(const LocIndString& other) const { return m_value != other.m_value; } bool operator<(const LocIndString& other) const { return m_value < other.m_value; } + bool operator<(const std::string& other) const { return m_value < other; } friend std::ostream& operator<<(std::ostream& out, const AppInstaller::Utility::LocIndString& lis) {