winget-cli

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

commit 0a11b104b0840b0f8c015a7b59d69cefda381ac7
parent 379e9b5125d2294451f5ae8bc24eec432c81be9f
Author: KEINOS <github+fork-qiita-news@keinos.com>
Date:   Sat, 30 Aug 2025 01:54:30 +0000

Merge remote-tracking branch 'upstream/master'

Diffstat:
Msrc/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp | 6++----
Msrc/AppInstallerCLITests/CompositeSource.cpp | 25+++++++++++++++++++++----
Msrc/AppInstallerCLITests/DateTime.cpp | 9+++++++++
Msrc/AppInstallerCLITests/SQLiteIndexSource.cpp | 26++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/TestSource.cpp | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLITests/TestSource.h | 14++++++++++++++
Msrc/AppInstallerRepositoryCore/CompositeSource.cpp | 95+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.cpp | 22++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV1.h | 2++
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.cpp | 13+++++++++++++
Msrc/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSourceV2.h | 2++
Msrc/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h | 25+++++++++++++++++++++++++
Msrc/AppInstallerRepositoryCore/RepositorySearch.cpp | 16++++++++++++++++
Msrc/AppInstallerRepositoryCore/Rest/RestSource.cpp | 160+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/AppInstallerSharedLib/DateTime.cpp | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/AppInstallerSharedLib/Public/AppInstallerDateTime.h | 29++++++++++++++++++++++++++++-
Msrc/AppInstallerSharedLib/Public/winget/LocIndependent.h | 1+
Msrc/WindowsPackageManager/ConfigurationStaticFunctions.cpp | 2+-
18 files changed, 504 insertions(+), 137 deletions(-)

diff --git a/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/ShellExecuteInstallerHandler.cpp @@ -173,11 +173,9 @@ namespace AppInstaller::CLI::Workflow const auto& manifest = context.Get<Execution::Data::Manifest>(); auto path = Runtime::GetPathTo(Runtime::PathName::DefaultLogLocation); - path /= Logging::FileLogger::DefaultPrefix(); + path /= Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); path += '-'; - path += Utility::ConvertToUTF16(manifest.Id + '.' + manifest.Version); - path += '-'; - path += Utility::GetCurrentTimeForFilename(); + path += Utility::GetCurrentTimeForFilename(true); path += Logging::FileLogger::DefaultExt(); logPath = path.u8string(); diff --git 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<ISource> 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/DateTime.cpp b/src/AppInstallerCLITests/DateTime.cpp @@ -82,3 +82,12 @@ TEST_CASE("GetTimePointFromVersion", "[datetime]") system_clock::time_point now = system_clock::now(); REQUIRE(GetTimePointFromVersion(UInt64Version{ StringFromTimePoint(now) }) == time_point_cast<minutes>(now)); } + +TEST_CASE("ShortFileTime", "[datetime]") +{ + auto shortTime = GetCurrentTimeForFilename(true); + auto longTime = GetCurrentTimeForFilename(false); + INFO(shortTime); + INFO(longTime); + REQUIRE(shortTime.length() < longTime.length()); +} diff --git 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<SQLiteIndexSource> 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 @@ -16,6 +16,19 @@ namespace TestCommon static std::atomic_size_t packageId(0); return ++packageId; } + + TestSource* GetTestSourceFromWeakPtr(const std::weak_ptr<const ISource>& weakSource) + { + if (auto source = weakSource.lock()) + { + if (auto testSource = const_cast<ISource*>(source.get())->CastTo(TestSource::SourceType)) + { + return reinterpret_cast<TestSource*>(testSource); + } + } + + return nullptr; + } } TestPackageVersion::TestPackageVersion(const Manifest& manifest, MetadataMap installationMetadata, std::weak_ptr<const ISource> 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<Manifest>& available, std::weak_ptr<const ISource> source, bool hideSystemReferenceStringsOnVersion) : Source(source) { @@ -168,8 +191,34 @@ namespace TestCommon } } + std::vector<TestPackage::LocIndString> TestPackage::GetMultiProperty(PackageMultiProperty property) const + { + std::vector<LocIndString> 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<PackageVersionKey> TestPackage::GetVersionKeys() const { + if (auto source = GetTestSource()) + { + source->IncrementCountOfCallsRequiringVersionData(); + } + std::vector<PackageVersionKey> result; for (const auto& version : Versions) { @@ -190,6 +239,14 @@ namespace TestCommon std::shared_ptr<IPackageVersion> 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<Manifest>& available, std::weak_ptr<const ISource> 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 @@ -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<LocIndString>& 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<AppInstaller::Utility::LocIndString> GetMultiProperty(AppInstaller::Repository::PackageMultiProperty property) const override; std::vector<AppInstaller::Repository::PackageVersionKey> GetVersionKeys() const override; std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetLatestVersion() const override; std::shared_ptr<AppInstaller::Repository::IPackageVersion> GetVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; @@ -76,6 +80,9 @@ namespace TestCommon std::weak_ptr<const ISource> Source; size_t DefaultIsSameIdentity = 0; std::function<bool(const IPackage*, const IPackage*)> 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 @@ -369,6 +369,26 @@ namespace AppInstaller::Repository return m_packages[m_versionKeyData[0].PackageIndex]->GetProperty(property); } + std::vector<Utility::LocIndString> GetMultiProperty(PackageMultiProperty property) const override + { + std::vector<Utility::LocIndString> 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<PackageVersionKey> 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<IPackage> result; if (trackingResult.Matches.size() == 1) { - std::shared_ptr<IPackage> 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 @@ -115,6 +115,28 @@ namespace AppInstaller::Repository::Microsoft::details::V1 return result; } + std::vector<Utility::LocIndString> SQLitePackage::GetMultiProperty(PackageMultiProperty property) const + { + std::shared_ptr<SQLiteIndexSource> source = GetReferenceSource(); + std::vector<Utility::LocIndString> 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<PackageVersionKey> SQLitePackage::GetVersionKeys() const { std::shared_ptr<SQLiteIndexSource> source = GetReferenceSource(); diff --git 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<Utility::LocIndString> GetMultiProperty(PackageMultiProperty property) const override; + std::vector<PackageVersionKey> GetVersionKeys() const override; std::shared_ptr<IPackageVersion> GetLatestVersion() const override; diff --git 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<Utility::LocIndString> SQLitePackage::GetMultiProperty(PackageMultiProperty property) const + { + std::vector<LocIndString> 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<PackageVersionKey> SQLitePackage::GetVersionKeys() const { std::shared_ptr<SQLiteIndexSource> source = GetReferenceSource(); diff --git 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<Utility::LocIndString> GetMultiProperty(PackageMultiProperty property) const override; + std::vector<PackageVersionKey> GetVersionKeys() const override; std::shared_ptr<IPackageVersion> GetLatestVersion() const override; diff --git 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<Utility::LocIndString> 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 @@ -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 @@ -54,6 +54,8 @@ namespace AppInstaller::Repository::Rest } } + std::vector<Utility::LocIndString> GetMultiProperty(PackageMultiProperty property) const override; + std::vector<PackageVersionKey> GetVersionKeys() const override { std::shared_ptr<const RestSource> 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<Utility::LocIndString>& result, + void (*Action)(std::vector<Utility::LocIndString>&, 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<Utility::LocIndString> RestPackage::GetMultiProperty(PackageMultiProperty property) const + { + std::scoped_lock versionsLock{ m_packageVersionsLock }; + std::vector<Utility::LocIndString> result; + PackageVersionMultiProperty mappedProperty = PackageMultiPropertyToPackageVersionMultiProperty(property); + + for (const auto& versionInfo : m_package.Versions) + { + GetMultiPropertyValues( + this, + versionInfo, + mappedProperty, + result, + [](std::vector<Utility::LocIndString>& 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<Utility::LocIndString> GetMultiProperty(PackageVersionMultiProperty property) const override { std::vector<Utility::LocIndString> 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<Utility::LocIndString>& 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/DateTime.cpp b/src/AppInstallerSharedLib/DateTime.cpp @@ -3,37 +3,93 @@ #include "pch.h" #include "Public/AppInstallerDateTime.h" +using namespace std::chrono; + namespace AppInstaller::Utility { - // If moved to C++20, this can be replaced with standard library implementations. - void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, bool useRFC3339) + namespace { - using namespace std::chrono; - - tm localTime{}; - auto tt = system_clock::to_time_t(time); - _localtime64_s(&localTime, &tt); - - stream - << std::setw(4) << (1900 + localTime.tm_year) << '-' - << std::setw(2) << std::setfill('0') << (1 + localTime.tm_mon) << '-' - << std::setw(2) << std::setfill('0') << localTime.tm_mday << (useRFC3339 ? 'T' : ' ') - << std::setw(2) << std::setfill('0') << localTime.tm_hour << ':' - << std::setw(2) << std::setfill('0') << localTime.tm_min << ':' - << std::setw(2) << std::setfill('0') << localTime.tm_sec << '.'; - - // Get partial seconds - auto sinceEpoch = time.time_since_epoch(); - auto leftoverMillis = duration_cast<milliseconds>(sinceEpoch) - duration_cast<seconds>(sinceEpoch); + struct OutputTimePointContext + { + OutputTimePointContext(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet) : + Stream(stream), Time(time), Facet(facet) + { + auto tt = system_clock::to_time_t(time); + _localtime64_s(&LocalTime, &tt); + } + + std::ostream& Stream; + const std::chrono::system_clock::time_point& Time; + tm LocalTime{}; + TimeFacet Facet; + }; + + struct OutputTimePointFacetInfo + { + TimeFacet Facet; + char FollowingSeparator; + void (*Action)(const OutputTimePointContext&); + }; + } - stream << std::setw(3) << std::setfill('0') << leftoverMillis.count(); + void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, bool useRFC3339) + { + OutputTimePoint(stream, time, TimeFacet::Default | (useRFC3339 ? TimeFacet::RFC3339 : TimeFacet::None)); + } - if (useRFC3339) + // If moved to C++20, this can be replaced with standard library implementations. + void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet) + { + OutputTimePointContext context{ stream, time, facet }; + using Ctx = const OutputTimePointContext&; + + bool useRFC3339 = WI_IsFlagSet(facet, TimeFacet::RFC3339); + bool filename = WI_IsFlagSet(facet, TimeFacet::Filename); + char day_time_separator = useRFC3339 ? 'T' : (filename ? '-' : ' '); + char time_field_separator = filename ? '-' : ':'; + + bool needsSeparator = false; + char currentSeparator = '-'; + + for (const auto& info : { + OutputTimePointFacetInfo{ TimeFacet::ShortYear, '-', [](Ctx ctx) { ctx.Stream << (ctx.LocalTime.tm_year - 100); }}, + OutputTimePointFacetInfo{ TimeFacet::Year, '-', [](Ctx ctx) { ctx.Stream << (1900 + ctx.LocalTime.tm_year); }}, + OutputTimePointFacetInfo{ TimeFacet::Month, '-', [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << (1 + ctx.LocalTime.tm_mon); }}, + OutputTimePointFacetInfo{ TimeFacet::Day, day_time_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_mday; }}, + OutputTimePointFacetInfo{ TimeFacet::Hour, time_field_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_hour; }}, + OutputTimePointFacetInfo{ TimeFacet::Minute, time_field_separator, [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_min; }}, + OutputTimePointFacetInfo{ TimeFacet::Second, '.', [](Ctx ctx) { ctx.Stream << std::setw(2) << std::setfill('0') << ctx.LocalTime.tm_sec; }}, + OutputTimePointFacetInfo{ TimeFacet::Millisecond, '-', [](Ctx ctx) + { + // Get partial seconds + auto sinceEpoch = ctx.Time.time_since_epoch(); + auto leftoverMillis = duration_cast<milliseconds>(sinceEpoch) - duration_cast<seconds>(sinceEpoch); + + ctx.Stream << std::setw(3) << std::setfill('0') << leftoverMillis.count(); + }}, + OutputTimePointFacetInfo{ TimeFacet::RFC3339, '\0', [](Ctx ctx) + { + // RFC 3339 requires adding time zone info. + // No need to bother getting the actual time zone as we don't need it. + // -00:00 represents an unspecified time zone, not UTC. + ctx.Stream << "00:00"; + }}, + }) { - // RFC 3339 requires adding time zone info. - // No need to bother getting the actual time zone as we don't need it. - // -00:00 represents an unspecified time zone, not UTC. - stream << "-00:00"; + if (WI_AreAllFlagsSet(facet, info.Facet)) + { + if (needsSeparator) + { + stream << currentSeparator; + } + + info.Action(context); + needsSeparator = true; + } + + // Getting this right for every mix of facets is probably not possible. + // Future needs can dictate changes here. + currentSeparator = info.FollowingSeparator; } } @@ -44,16 +100,16 @@ namespace AppInstaller::Utility return std::move(stream).str(); } - std::string GetCurrentTimeForFilename() + std::string TimePointToString(const std::chrono::system_clock::time_point& time, TimeFacet facet) { - std::stringstream stream; - OutputTimePoint(stream, std::chrono::system_clock::now()); - - auto result = stream.str(); - std::replace(result.begin(), result.end(), ':', '-'); - std::replace(result.begin(), result.end(), ' ', '-'); + std::ostringstream stream; + OutputTimePoint(stream, time, facet); + return std::move(stream).str(); + } - return result; + std::string GetCurrentTimeForFilename(bool shortTime) + { + return TimePointToString(std::chrono::system_clock::now(), (shortTime ? TimeFacet::ShortYearSecondPrecision : TimeFacet::Default) | TimeFacet::Filename); } std::string GetCurrentDateForARP() diff --git a/src/AppInstallerSharedLib/Public/AppInstallerDateTime.h b/src/AppInstallerSharedLib/Public/AppInstallerDateTime.h @@ -7,16 +7,43 @@ namespace AppInstaller::Utility { + // The individual aspects of a time point. + enum class TimeFacet + { + None = 0x000, + Millisecond = 0x001, + Second = 0x002, + Minute = 0x004, + Hour = 0x008, + Day = 0x010, + Month = 0x020, + Year = 0x040, + // `Year - 2000` [2 digits for 75 more years] + ShortYear = 0x080, + // Includes unspecified time zone + RFC3339 = 0x100, + // Limits special character use + Filename = 0x200, + + Default = Year | Month | Day | Hour | Minute | Second | Millisecond, + ShortYearSecondPrecision = ShortYear | Month | Day | Hour | Minute | Second, + }; + + DEFINE_ENUM_FLAG_OPERATORS(TimeFacet); + // Writes the given time to the given stream. // Assumes that system_clock uses Linux epoch (as required by C++20 standard). // Time is also assumed to be after the epoch. void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, bool useRFC3339 = false); + void OutputTimePoint(std::ostream& stream, const std::chrono::system_clock::time_point& time, TimeFacet facet); // Converts the time point to a string using OutputTimePoint. std::string TimePointToString(const std::chrono::system_clock::time_point& time, bool useRFC3339 = false); + std::string TimePointToString(const std::chrono::system_clock::time_point& time, TimeFacet facet); // Gets the current time as a string. Can be used as a file name. - std::string GetCurrentTimeForFilename(); + // Tries to make things a little bit shorter when shortTime == true. + std::string GetCurrentTimeForFilename(bool shortTime = false); // Gets the current date as a string to be used in the ARP registry. std::string GetCurrentDateForARP(); diff --git 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) { diff --git a/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp b/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp @@ -90,7 +90,7 @@ namespace ConfigurationShim auto& diagnosticsLogger = m_threadGlobals.GetDiagnosticLogger(); diagnosticsLogger.SetEnabledChannels(AppInstaller::Logging::Channel::All); diagnosticsLogger.SetLevel(AppInstaller::Logging::Level::Verbose); - diagnosticsLogger.AddLogger(std::make_unique<AppInstaller::Logging::FileLogger>("ConfigStatics"sv)); + diagnosticsLogger.AddLogger(std::make_unique<AppInstaller::Logging::FileLogger>("WinGetCFG"sv)); if (IsConfigurationAvailable()) {