From 3ddffad25c5034db12241ea17ddc775e1c8094d8 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 10:39:16 -0700 Subject: [PATCH 01/52] Added join, toString and format --- .../Commands/InstallCommand.cpp | 2 +- .../Commands/RootCommand.cpp | 2 +- .../Workflows/ManifestComparator.cpp | 8 ++--- src/AppInstallerCommonCore/Architecture.cpp | 2 +- .../Manifest/ManifestValidation.cpp | 2 +- .../Public/AppInstallerArchitecture.h | 2 +- .../Public/AppInstallerStrings.h | 34 +++++++++++++++++++ .../Microsoft/ARPHelper.cpp | 2 +- 8 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index adedca2792..3238d3a41d 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -124,7 +124,7 @@ namespace AppInstaller::CLI std::vector applicableArchitectures; for (Utility::Architecture i : Utility::GetApplicableArchitectures()) { - applicableArchitectures.emplace_back(Utility::ToString(i)); + applicableArchitectures.emplace_back(Utility::ConvertFromArchitectureEnum(i)); } throw CommandException(Resource::String::InvalidArgumentValueError, s_ArgumentName_Architecture, std::forward>((applicableArchitectures))); } diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index e5b19ab15c..9f4d97b4f5 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -178,7 +178,7 @@ namespace AppInstaller::CLI info << std::endl << "Windows: "_liv << Runtime::GetOSVersion() << std::endl; - info << Resource::String::SystemArchitecture << ": "_liv << Utility::ToString(Utility::GetSystemArchitecture()) << std::endl; + info << Resource::String::SystemArchitecture << ": "_liv << Utility::ConvertFromArchitectureEnum(Utility::GetSystemArchitecture()) << std::endl; if (Runtime::IsRunningInPackagedContext()) { diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp index 470edb9ef9..0ca333bcd3 100644 --- a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp +++ b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp @@ -12,7 +12,7 @@ using namespace AppInstaller::Manifest; std::ostream& operator<<(std::ostream& out, const AppInstaller::Manifest::ManifestInstaller& installer) { return out << '[' << - AppInstaller::Utility::ToString(installer.Arch) << ',' << + AppInstaller::Utility::ConvertFromArchitectureEnum(installer.Arch) << ',' << AppInstaller::Manifest::InstallerTypeToString(installer.InstallerType) << ',' << AppInstaller::Manifest::ScopeToString(installer.Scope) << ',' << installer.Locale << ']'; @@ -74,7 +74,7 @@ namespace AppInstaller::CLI::Workflow MachineArchitectureComparator(std::vector allowedArchitectures) : details::ComparisonField("Machine Architecture"), m_allowedArchitectures(std::move(allowedArchitectures)) { - AICLI_LOG(CLI, Verbose, << "Architecture Comparator created with allowed architectures: " << Utility::ConvertContainerToString(m_allowedArchitectures, Utility::ToString)); + AICLI_LOG(CLI, Verbose, << "Architecture Comparator created with allowed architectures: " << Utility::ConvertContainerToString(m_allowedArchitectures, Utility::ConvertFromArchitectureEnum)); } // TODO: At some point we can do better about matching the currently installed architecture @@ -142,7 +142,7 @@ namespace AppInstaller::CLI::Workflow if (Utility::IsApplicableArchitecture(installer.Arch) == Utility::InapplicableArchitecture) { result = "Machine is not compatible with "; - result += Utility::ToString(installer.Arch); + result += Utility::ConvertFromArchitectureEnum(installer.Arch); } else if (IsSystemArchitectureUnsupportedByInstaller(installer)) { @@ -151,7 +151,7 @@ namespace AppInstaller::CLI::Workflow else { result = "Architecture was excluded by caller : "; - result += Utility::ToString(installer.Arch); + result += Utility::ConvertFromArchitectureEnum(installer.Arch); } return result; diff --git a/src/AppInstallerCommonCore/Architecture.cpp b/src/AppInstallerCommonCore/Architecture.cpp index 93d1fe8a34..7069b99385 100644 --- a/src/AppInstallerCommonCore/Architecture.cpp +++ b/src/AppInstallerCommonCore/Architecture.cpp @@ -187,7 +187,7 @@ namespace AppInstaller::Utility return Architecture::Unknown; } - LocIndView ToString(Architecture architecture) + LocIndView ConvertFromArchitectureEnum(Architecture architecture) { switch (architecture) { diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp index 1abd30fe4e..5401dc6d19 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -82,7 +82,7 @@ namespace AppInstaller::Manifest if (!duplicateInstallerFound && !installerSet.insert(installer).second) { AICLI_LOG(Core, Error, << "Duplicate installer: Type[" << InstallerTypeToString(installer.InstallerType) << - "] Architecture[" << Utility::ToString(installer.Arch) << "] Locale[" << installer.Locale << + "] Architecture[" << Utility::ConvertFromArchitectureEnum(installer.Arch) << "] Locale[" << installer.Locale << "] Scope[" << ScopeToString(installer.Scope) << "]"); resultErrors.emplace_back(ManifestError::DuplicateInstallerEntry); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h b/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h index 5e20cf14e2..bd372bb75d 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h @@ -23,7 +23,7 @@ namespace AppInstaller::Utility Architecture ConvertToArchitectureEnum(const std::string& archStr); // Converts an Architecture to a string_view - LocIndView ToString(Architecture architecture); + LocIndView ConvertFromArchitectureEnum(Architecture architecture); // Gets the system's architecture as Architecture enum AppInstaller::Utility::Architecture GetSystemArchitecture(); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index c9cb20fe3f..2fbae3f822 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -230,4 +230,38 @@ namespace AppInstaller::Utility // Converts the given hexadecimal string into bytes. std::vector ParseFromHexString(const std::string& value, size_t byteCount = 0); + + // Join an arbitrary number of values using the provided separator string. + template + std::string Join(std::string_view separator, T value, TArgs ... optValues) + { + std::ostringstream ssJoin; + ssJoin << value; + ((ssJoin << separator << optValues), ...); + return ssJoin.str(); + } + + // Superset of std::to_string supporting string convertibles as input. + template + std::string ToString(T value) + { + if constexpr (std::is_convertible_v || std::is_convertible_v) + { + return value; + } + else + { + return std::to_string(value); + } + } + + // Format an input string by replacing placeholders {index} with provided values at corresponding indices. + // Note: After upgrading to C++20, this function should be deprecated in favor of std::format. + template + std::string Format(std::string inputStr, T ... args) + { + int index = 0; + (FindAndReplace(inputStr, "{" + std::to_string(index++) + "}", ToString(args)), ...); + return inputStr; + } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp index 41ec717a49..1baa0850a5 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp @@ -214,7 +214,7 @@ namespace AppInstaller::Repository::Microsoft if (arpRootKey) { - PopulateIndexFromKey(index, arpRootKey, Manifest::ScopeToString(scope), Utility::ToString(architecture)); + PopulateIndexFromKey(index, arpRootKey, Manifest::ScopeToString(scope), Utility::ConvertFromArchitectureEnum(architecture)); } } } From b1af99306f103d8859091a3d3818d5ac8fd0b796 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 10:59:15 -0700 Subject: [PATCH 02/52] Moved Loc to common resources --- src/AppInstallerCLICore/Resources.cpp | 40 ----------------- src/AppInstallerCLICore/Resources.h | 44 ++----------------- .../Public/winget/Resources.h | 41 +++++++++++++++++ src/AppInstallerCommonCore/Resources.cpp | 41 +++++++++++++++++ 4 files changed, 85 insertions(+), 81 deletions(-) diff --git a/src/AppInstallerCLICore/Resources.cpp b/src/AppInstallerCLICore/Resources.cpp index 1105dedbf9..6a08ec05b3 100644 --- a/src/AppInstallerCLICore/Resources.cpp +++ b/src/AppInstallerCLICore/Resources.cpp @@ -7,41 +7,6 @@ using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Resource { - LocString::LocString(StringId id) : Utility::LocIndString(Loader::Instance().ResolveString(id)) {} - - const Loader& Loader::Instance() - { - static Loader instance; - return instance; - } - - Loader::Loader() : m_wingetLoader(nullptr) - { - try - { - // The default constructor of ResourceLoader throws a winrt::hresult_error exception - // when resource.pri is not found. ResourceLoader::GetForViewIndependentUse also throws - // a winrt::hresult_error but for reasons unknown it only gets catch when running on the - // debugger. Running without a debugger will result in a crash that not even adding a - // catch all fix. To provide a good error message we call the default constructor - // before calling GetForViewIndependentUse. - m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader(); - m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse(L"winget"); - } - catch (const winrt::hresult_error& hre) - { - // This message cannot be localized. - AICLI_LOG(CLI, Error, << "Failure loading resource file with error: " << hre.code()); - throw ResourceOpenException(hre); - } - } - - std::string Loader::ResolveString( - std::wstring_view resKey) const - { - return Utility::ConvertToUTF8(m_wingetLoader.GetString(resKey)); - } - Utility::LocIndView GetFixedString(FixedString fs) { switch (fs) @@ -51,9 +16,4 @@ namespace AppInstaller::CLI::Resource THROW_HR(E_UNEXPECTED); } - - ResourceOpenException::ResourceOpenException(const winrt::hresult_error& hre) - { - m_message = "Could not open the resource file: " + GetUserPresentableMessage(hre); - } } diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index a445a33960..51232cc6bf 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include #include #include @@ -11,6 +10,9 @@ namespace AppInstaller::CLI::Resource { using AppInstaller::StringResource::StringId; + using AppInstaller::Resource::LocString; + using AppInstaller::Resource::Loader; + using AppInstaller::Resource::ResourceOpenException; // Resource string identifiers. // This list can mostly be generated by the following PowerShell: @@ -403,36 +405,6 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(WordArgumentDescription); }; - // A localized string - struct LocString : public Utility::LocIndString - { - LocString() = default; - - LocString(StringId id); - - LocString(const LocString&) = default; - LocString& operator=(const LocString&) = default; - - LocString(LocString&&) = default; - LocString& operator=(LocString&&) = default; - }; - - // Utility class to load resources - class Loader - { - public: - // Gets the singleton instance of the resource loader. - static const Loader& Instance(); - - // Gets the the string resource value. - std::string ResolveString(std::wstring_view resKey) const; - - private: - winrt::Windows::ApplicationModel::Resources::ResourceLoader m_wingetLoader; - - Loader(); - }; - // Fixed strings are not localized, but we use a similar system to prevent duplication enum class FixedString { @@ -440,16 +412,6 @@ namespace AppInstaller::CLI::Resource }; Utility::LocIndView GetFixedString(FixedString fs); - - struct ResourceOpenException : std::exception - { - ResourceOpenException(const winrt::hresult_error& hre); - - const char* what() const noexcept override { return m_message.c_str(); } - - private: - std::string m_message; - }; } inline std::ostream& operator<<(std::ostream& out, AppInstaller::CLI::Resource::StringId si) diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index ec9e181e35..443a351c6e 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -5,6 +5,7 @@ #include #include +#include "LocIndependent.h" using namespace std::string_view_literals; @@ -55,5 +56,45 @@ namespace AppInstaller // Resource data is valid as long as the binary is loaded. std::pair GetResourceAsBytes(int resourceName, int resourceType); std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType); + + // A localized string + struct LocString : public Utility::LocIndString + { + LocString() = default; + + LocString(StringResource::StringId id); + + LocString(const LocString&) = default; + LocString& operator=(const LocString&) = default; + + LocString(LocString&&) = default; + LocString& operator=(LocString&&) = default; + }; + + // Utility class to load resources + class Loader + { + public: + // Gets the singleton instance of the resource loader. + static const Loader& Instance(); + + // Gets the the string resource value. + std::string ResolveString(std::wstring_view resKey) const; + + private: + winrt::Windows::ApplicationModel::Resources::ResourceLoader m_wingetLoader; + + Loader(); + }; + + struct ResourceOpenException : std::exception + { + ResourceOpenException(const winrt::hresult_error& hre); + + const char* what() const noexcept override { return m_message.c_str(); } + + private: + std::string m_message; + }; } } diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 8aab316026..6fb5715f1f 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -4,6 +4,7 @@ #include "winget/Resources.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" +#include "Public//AppInstallerErrors.h" namespace AppInstaller::Resource { @@ -55,4 +56,44 @@ namespace AppInstaller::Resource auto resourceData = GetResourceData(resourceName, resourceType); return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); } + + LocString::LocString(StringResource::StringId id) : Utility::LocIndString(Loader::Instance().ResolveString(id)) {} + + const Loader& Loader::Instance() + { + static Loader instance; + return instance; + } + + Loader::Loader() : m_wingetLoader(nullptr) + { + try + { + // The default constructor of ResourceLoader throws a winrt::hresult_error exception + // when resource.pri is not found. ResourceLoader::GetForViewIndependentUse also throws + // a winrt::hresult_error but for reasons unknown it only gets catch when running on the + // debugger. Running without a debugger will result in a crash that not even adding a + // catch all fix. To provide a good error message we call the default constructor + // before calling GetForViewIndependentUse. + m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader(); + m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse(L"winget"); + } + catch (const winrt::hresult_error& hre) + { + // This message cannot be localized. + AICLI_LOG(CLI, Error, << "Failure loading resource file with error: " << hre.code()); + throw ResourceOpenException(hre); + } + } + + std::string Loader::ResolveString( + std::wstring_view resKey) const + { + return Utility::ConvertToUTF8(m_wingetLoader.GetString(resKey)); + } + + ResourceOpenException::ResourceOpenException(const winrt::hresult_error& hre) + { + m_message = "Could not open the resource file: " + GetUserPresentableMessage(hre); + } } From 6a6f5aec1887fdf800aac85cb25acf5c608b5595 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 11:00:08 -0700 Subject: [PATCH 03/52] Added /MP --- src/AppInstallerCLICore/AppInstallerCLICore.vcxproj | 1 + src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj | 1 + .../AppInstallerRepositoryCore.vcxproj | 1 + 3 files changed, 3 insertions(+) diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index f09cd3c3ab..8ee677f401 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -174,6 +174,7 @@ true true true + true false diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index ddcdb11413..56fb145d8b 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -190,6 +190,7 @@ true true true + true false diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 6259cbcfa0..bf28f0e43f 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -173,6 +173,7 @@ true true true + true false From 47a99830b9d5597d8698a51a9780c8d66975fdc1 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 12:26:35 -0700 Subject: [PATCH 04/52] Added operator() for StringId --- .../Public/winget/Resources.h | 77 +++++++++++-------- src/AppInstallerCommonCore/Resources.cpp | 2 +- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 443a351c6e..f03124190d 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -6,45 +6,13 @@ #include #include #include "LocIndependent.h" +#include "AppInstallerStrings.h" using namespace std::string_view_literals; namespace AppInstaller { - namespace StringResource - { -#define WINGET_WIDE_STRINGIFY_HELP(_id_) L ## _id_ -#define WINGET_WIDE_STRINGIFY(_id_) WINGET_WIDE_STRINGIFY_HELP(_id_) -#define WINGET_DEFINE_RESOURCE_STRINGID(_id_) static constexpr AppInstaller::StringResource::StringId _id_ { WINGET_WIDE_STRINGIFY(#_id_) ## sv } - - // A resource identifier - struct StringId : public std::wstring_view - { - explicit constexpr StringId(std::wstring_view id) : std::wstring_view(id) {} - }; - - // Resource string identifiers. - struct String - { - WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWinGet); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWingetSettings); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableExperimentalFeatures); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableLocalManifests); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableHashOverride); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableDefaultSource); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableMSStoreSource); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyAdditionalSources); - WINGET_DEFINE_RESOURCE_STRINGID(PolicyAllowedSources); - WINGET_DEFINE_RESOURCE_STRINGID(PolicySourceAutoUpdateInterval); - - WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidFieldFormat); - WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidFieldValue); - WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidValueFromPolicy); - WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningLoadedBackupSettings); - WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningParseError); - }; - } - + namespace StringResource { struct StringId; } namespace Resource { // Get an embedded resource from the binary and return as std::string_view. @@ -97,4 +65,45 @@ namespace AppInstaller std::string m_message; }; } + + namespace StringResource + { +#define WINGET_WIDE_STRINGIFY_HELP(_id_) L ## _id_ +#define WINGET_WIDE_STRINGIFY(_id_) WINGET_WIDE_STRINGIFY_HELP(_id_) +#define WINGET_DEFINE_RESOURCE_STRINGID(_id_) static constexpr AppInstaller::StringResource::StringId _id_ { WINGET_WIDE_STRINGIFY(#_id_) ## sv } + + // A resource identifier + struct StringId : public std::wstring_view + { + explicit constexpr StringId(std::wstring_view id) : std::wstring_view(id) {} + + template + Utility::LocIndString operator()(T ... args) const + { + auto resolvedId = AppInstaller::Resource::Loader::Instance().ResolveString(*this); + return Utility::LocIndString(Utility::Format(resolvedId, args...)); + } + }; + + // Resource string identifiers. + struct String + { + WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWinGet); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableWingetSettings); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableExperimentalFeatures); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableLocalManifests); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableHashOverride); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableDefaultSource); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyEnableMSStoreSource); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyAdditionalSources); + WINGET_DEFINE_RESOURCE_STRINGID(PolicyAllowedSources); + WINGET_DEFINE_RESOURCE_STRINGID(PolicySourceAutoUpdateInterval); + + WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidFieldFormat); + WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidFieldValue); + WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningInvalidValueFromPolicy); + WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningLoadedBackupSettings); + WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningParseError); + }; + } } diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 6fb5715f1f..ee057576d0 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -57,7 +57,7 @@ namespace AppInstaller::Resource return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); } - LocString::LocString(StringResource::StringId id) : Utility::LocIndString(Loader::Instance().ResolveString(id)) {} + LocString::LocString(StringResource::StringId id) : Utility::LocIndString(id()) {} const Loader& Loader::Instance() { From 5a16227582e80a8ab194c1f3b6048ea2697c74fa Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 15:06:40 -0700 Subject: [PATCH 05/52] Removed CommandException cstr with param --- src/AppInstallerCLICore/Command.cpp | 32 +++++++++---------- src/AppInstallerCLICore/Command.h | 6 ---- .../Commands/SettingsCommand.cpp | 2 +- .../Commands/UninstallCommand.cpp | 4 +-- .../Public/AppInstallerStrings.h | 2 +- .../Public/winget/Resources.h | 1 + 6 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 5f52898c81..35cd7842ac 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -313,7 +313,7 @@ namespace AppInstaller::CLI { auto feature = ExperimentalFeature::GetFeature(command->Feature()); AICLI_LOG(CLI, Error, << "Trying to use command: " << *itr << " without enabling feature " << feature.JsonName()); - throw CommandException(Resource::String::FeatureDisabledMessage, feature.JsonName()); + throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); } if (!Settings::GroupPolicies().IsEnabled(command->GroupPolicy())) @@ -330,7 +330,7 @@ namespace AppInstaller::CLI } // TODO: If we get to a large number of commands, do a fuzzy search much like git - throw CommandException(Resource::String::UnrecognizedCommand, *itr); + throw CommandException(Resource::String::UnrecognizedCommand(*itr)); } // The argument parsing state machine. @@ -431,7 +431,7 @@ namespace AppInstaller::CLI // If the next argument was to be a value, but none was provided, convert it to an exception. else if (m_state.Type() && m_invocationItr == m_invocation.end()) { - throw CommandException(Resource::String::MissingArgumentError, m_state.Arg()); + throw CommandException(Resource::String::MissingArgumentError(m_state.Arg())); } } @@ -487,7 +487,7 @@ namespace AppInstaller::CLI const CLI::Argument* nextPositional = NextPositional(); if (!nextPositional) { - return CommandException(Resource::String::ExtraPositionalError, currArg); + return CommandException(Resource::String::ExtraPositionalError(currArg)); } m_executionArgs.AddArg(nextPositional->ExecArgType(), currArg); @@ -495,7 +495,7 @@ namespace AppInstaller::CLI // The currentArg must not be empty, and starts with a - else if (currArg.length() == 1) { - return CommandException(Resource::String::InvalidArgumentSpecifierError, currArg); + return CommandException(Resource::String::InvalidArgumentSpecifierError(currArg)); } // Now it must be at least 2 chars else if (currArg[1] != APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR) @@ -506,7 +506,7 @@ namespace AppInstaller::CLI auto itr = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr == m_arguments.end()) { - return CommandException(Resource::String::InvalidAliasError, currArg); + return CommandException(Resource::String::InvalidAliasError(currArg)); } if (itr->Type() == ArgumentType::Flag) @@ -520,11 +520,11 @@ namespace AppInstaller::CLI auto itr2 = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr2 == m_arguments.end()) { - return CommandException(Resource::String::AdjoinedNotFoundError, currArg); + return CommandException(Resource::String::AdjoinedNotFoundError(currArg)); } else if (itr2->Type() != ArgumentType::Flag) { - return CommandException(Resource::String::AdjoinedNotFlagError, currArg); + return CommandException(Resource::String::AdjoinedNotFlagError(currArg)); } else { @@ -540,7 +540,7 @@ namespace AppInstaller::CLI } else { - return CommandException(Resource::String::SingleCharAfterDashError, currArg); + return CommandException(Resource::String::SingleCharAfterDashError(currArg)); } } else @@ -583,7 +583,7 @@ namespace AppInstaller::CLI { if (hasValue) { - return CommandException(Resource::String::FlagContainAdjoinedError, currArg); + return CommandException(Resource::String::FlagContainAdjoinedError(currArg)); } m_executionArgs.AddArg(arg.ExecArgType()); @@ -603,7 +603,7 @@ namespace AppInstaller::CLI if (!argFound) { - return CommandException(Resource::String::InvalidNameError, currArg); + return CommandException(Resource::String::InvalidNameError(currArg)); } } @@ -663,29 +663,29 @@ namespace AppInstaller::CLI { auto feature = ExperimentalFeature::GetFeature(arg.Feature()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " without enabling feature " << feature.JsonName()); - throw CommandException(Resource::String::FeatureDisabledMessage, feature.JsonName()); + throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); } if (arg.Required() && !execArgs.Contains(arg.ExecArgType())) { - throw CommandException(Resource::String::RequiredArgError, arg.Name()); + throw CommandException(Resource::String::RequiredArgError(arg.Name())); } if (arg.Limit() < execArgs.GetCount(arg.ExecArgType())) { - throw CommandException(Resource::String::TooManyArgError, arg.Name()); + throw CommandException(Resource::String::TooManyArgError(arg.Name())); } } if (execArgs.Contains(Execution::Args::Type::Silent) && execArgs.Contains(Execution::Args::Type::Interactive)) { - throw CommandException(Resource::String::TooManyBehaviorsError, s_Command_ArgName_SilentAndInteractive); + throw CommandException(Resource::String::TooManyBehaviorsError(s_Command_ArgName_SilentAndInteractive)); } if (execArgs.Contains(Execution::Args::Type::CustomHeader) && !execArgs.Contains(Execution::Args::Type::Source) && !execArgs.Contains(Execution::Args::Type::SourceName)) { - throw CommandException(Resource::String::HeaderArgumentNotApplicableWithoutSource, Argument::ForType(Execution::Args::Type::CustomHeader).Name()); + throw CommandException(Resource::String::HeaderArgumentNotApplicableWithoutSource(Argument::ForType(Execution::Args::Type::CustomHeader).Name())); } if (execArgs.Contains(Execution::Args::Type::Count)) diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 2fa7e54959..cc2de54a21 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -24,12 +24,6 @@ namespace AppInstaller::CLI { CommandException(Resource::LocString message) : m_message(std::move(message)) {} - // The message should be a localized string. - // The parameters can be either localized or not. - // We 'convert' the param to a localization independent view here if needed. - CommandException(Resource::LocString message, Resource::LocString param) : m_message(std::move(message)), m_params({ param }) {} - CommandException(Resource::LocString message, std::string_view param) : m_message(std::move(message)), m_params({ Utility::LocIndString{ param } }) {} - // The message should be a localized string, but the replacement and parameters are not. // This supports replacing %1 in the message with the replace value. CommandException(Resource::LocString message, Utility::LocIndView replace, std::vector&& params) : diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index 3b8e112d2d..9cd7963e92 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -45,7 +45,7 @@ namespace AppInstaller::CLI { if (execArgs.Contains(Execution::Args::Type::AdminSettingEnable) && execArgs.Contains(Execution::Args::Type::AdminSettingDisable)) { - throw CommandException(Resource::String::TooManyAdminSettingArgumentsError, s_ArgName_EnableAndDisable); + throw CommandException(Resource::String::TooManyAdminSettingArgumentsError(s_ArgName_EnableAndDisable)); } if (execArgs.Contains(Execution::Args::Type::AdminSettingEnable) && AdminSetting::Unknown == StringToAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingEnable))) diff --git a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp index bddfd0fa0e..45fc25da5c 100644 --- a/src/AppInstallerCLICore/Commands/UninstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/UninstallCommand.cpp @@ -101,12 +101,12 @@ namespace AppInstaller::CLI execArgs.Contains(Execution::Args::Type::Source) || execArgs.Contains(Execution::Args::Type::Exact))) { - throw CommandException(Resource::String::BothManifestAndSearchQueryProvided, ""); + throw CommandException(Resource::String::BothManifestAndSearchQueryProvided); } if (execArgs.Contains(Execution::Args::Type::Purge) && execArgs.Contains(Execution::Args::Type::Preserve)) { - throw CommandException(Resource::String::BothPurgeAndPreserveFlagsProvided, ""); + throw CommandException(Resource::String::BothPurgeAndPreserveFlagsProvided); } } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index 2fbae3f822..613801fafb 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -247,7 +247,7 @@ namespace AppInstaller::Utility { if constexpr (std::is_convertible_v || std::is_convertible_v) { - return value; + return std::string(value); } else { diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index f03124190d..9ad6ba993c 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -31,6 +31,7 @@ namespace AppInstaller LocString() = default; LocString(StringResource::StringId id); + LocString(const LocIndString& lis) : Utility::LocIndString(lis) {} LocString(const LocString&) = default; LocString& operator=(const LocString&) = default; From 6a066cf040e3d7c4eacb5271ed5074891de4dffc Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 15:59:49 -0700 Subject: [PATCH 06/52] Removed CommandException with placeholder --- src/AppInstallerCLICore/Command.cpp | 2 +- src/AppInstallerCLICore/Command.h | 5 ----- .../Commands/InstallCommand.cpp | 10 ++++++---- .../Commands/SettingsCommand.cpp | 4 ++-- .../Public/AppInstallerStrings.h | 19 ++++++++++++++----- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 35cd7842ac..60c800bddd 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -656,7 +656,7 @@ namespace AppInstaller::CLI { auto setting = Settings::AdminSettingToString(arg.AdminSetting()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " disabled by admin setting " << setting); - throw CommandException(Resource::String::FeatureDisabledByAdminSettingMessage, Utility::LocIndView{ setting }, {}); + throw CommandException(Resource::String::FeatureDisabledByAdminSettingMessage(Utility::LocIndView{ setting })); } if (!ExperimentalFeature::IsEnabled(arg.Feature()) && execArgs.Contains(arg.ExecArgType())) diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index cc2de54a21..1584c28a2f 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -24,11 +24,6 @@ namespace AppInstaller::CLI { CommandException(Resource::LocString message) : m_message(std::move(message)) {} - // The message should be a localized string, but the replacement and parameters are not. - // This supports replacing %1 in the message with the replace value. - CommandException(Resource::LocString message, Utility::LocIndView replace, std::vector&& params) : - m_message(std::move(message)), m_replace(replace), m_params(std::move(params)) {} - const Utility::LocIndString Message() const; const std::vector& Params() const { return m_params; } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 3238d3a41d..6ba58b0bf7 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -113,7 +113,8 @@ namespace AppInstaller::CLI { if (ConvertToScopeEnum(execArgs.GetArg(Args::Type::InstallScope)) == Manifest::ScopeEnum::Unknown) { - throw CommandException(Resource::String::InvalidArgumentValueError, s_ArgumentName_Scope, { "user"_lis, "machine"_lis }); + auto validOptions = Utility::Format("'{0}', '{1}'", "user"_lis, "machine"_lis); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Scope, validOptions)); } } if (execArgs.Contains(Args::Type::InstallArchitecture)) @@ -124,9 +125,10 @@ namespace AppInstaller::CLI std::vector applicableArchitectures; for (Utility::Architecture i : Utility::GetApplicableArchitectures()) { - applicableArchitectures.emplace_back(Utility::ConvertFromArchitectureEnum(i)); + applicableArchitectures.emplace_back(Utility::Format("'{0}'", Utility::ConvertFromArchitectureEnum(i))); } - throw CommandException(Resource::String::InvalidArgumentValueError, s_ArgumentName_Architecture, std::forward>((applicableArchitectures))); + auto validOptions = Utility::Join(", ", applicableArchitectures); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Architecture, validOptions)); } } @@ -134,7 +136,7 @@ namespace AppInstaller::CLI { if (!Locale::IsWellFormedBcp47Tag(execArgs.GetArg(Args::Type::Locale))) { - throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues, Argument::ForType(Args::Type::Locale).Name(), {}); + throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues(Argument::ForType(Args::Type::Locale).Name())); } } } diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index 9cd7963e92..81d35fc90f 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -50,12 +50,12 @@ namespace AppInstaller::CLI if (execArgs.Contains(Execution::Args::Type::AdminSettingEnable) && AdminSetting::Unknown == StringToAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingEnable))) { - throw CommandException(Resource::String::InvalidArgumentValueError, s_ArgumentName_Enable, { "LocalManifestFiles"_lis }); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Enable, "LocalManifestFiles"_lis)); } if (execArgs.Contains(Execution::Args::Type::AdminSettingDisable) && AdminSetting::Unknown == StringToAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingDisable))) { - throw CommandException(Resource::String::InvalidArgumentValueError, s_ArgumentName_Disable, { "LocalManifestFiles"_lis }); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Disable, "LocalManifestFiles"_lis)); } } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index 613801fafb..6ca3f2260d 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -231,13 +231,22 @@ namespace AppInstaller::Utility // Converts the given hexadecimal string into bytes. std::vector ParseFromHexString(const std::string& value, size_t byteCount = 0); - // Join an arbitrary number of values using the provided separator string. - template - std::string Join(std::string_view separator, T value, TArgs ... optValues) + // Join vector values using the provided separator. + template + std::string Join(std::string_view separator, const std::vector& vector) { + auto vectorSize = vector.size(); + if (vectorSize == 0) + { + return {}; + } + std::ostringstream ssJoin; - ssJoin << value; - ((ssJoin << separator << optValues), ...); + ssJoin << vector[0]; + for (int i = 1; i < vectorSize; ++i) + { + ssJoin << separator << vector[i]; + } return ssJoin.str(); } From 2153425fd3cc633c888a529e5e2cc35fe6d06ab0 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 16:30:59 -0700 Subject: [PATCH 07/52] Removed m_replace --- src/AppInstallerCLICore/Command.cpp | 6 ------ src/AppInstallerCLICore/Command.h | 1 - src/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 4 +--- src/AppInstallerCommonCore/AppInstallerStrings.cpp | 8 -------- src/AppInstallerCommonCore/Public/AppInstallerStrings.h | 3 --- 5 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 60c800bddd..f6e06ae7c2 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -15,12 +15,6 @@ namespace AppInstaller::CLI const Utility::LocIndString CommandException::Message() const { - if (m_replace) - { - return Utility::LocIndString{ Utility::FindAndReplaceMessageToken(m_message, m_replace.value()) }; - } - - // Fall back to just using the message. return Utility::LocIndString{ m_message.get() }; } diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 1584c28a2f..345786f9a3 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -29,7 +29,6 @@ namespace AppInstaller::CLI private: Resource::LocString m_message; - std::optional m_replace; std::vector m_params; }; diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 65dd2928fd..bcc280237a 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -166,9 +166,7 @@ namespace AppInstaller::CLI::Workflow } // Show source agreements - std::string agreementsTitleMessage = Resource::LocString{ Resource::String::SourceAgreementsTitle }; - context.Reporter.Info() << Execution::SourceInfoEmphasis << - Utility::LocIndString{ Utility::FindAndReplaceMessageToken(agreementsTitleMessage, details.Name) } << std::endl; + context.Reporter.Info() << Execution::SourceInfoEmphasis << Resource::String::SourceAgreementsTitle(details.Name) << std::endl; const auto& agreements = source.GetInformation().SourceAgreements; diff --git a/src/AppInstallerCommonCore/AppInstallerStrings.cpp b/src/AppInstallerCommonCore/AppInstallerStrings.cpp index 33b0df833c..c8b4193499 100644 --- a/src/AppInstallerCommonCore/AppInstallerStrings.cpp +++ b/src/AppInstallerCommonCore/AppInstallerStrings.cpp @@ -14,7 +14,6 @@ namespace AppInstaller::Utility using namespace std::string_view_literals; constexpr std::string_view s_SpaceChars = AICLI_SPACE_CHARS; constexpr std::wstring_view s_WideSpaceChars = L"" AICLI_SPACE_CHARS; - constexpr std::string_view s_MessageReplacementToken = "%1"sv; namespace { @@ -567,13 +566,6 @@ namespace AppInstaller::Utility return result; } - std::string FindAndReplaceMessageToken(std::string_view message, std::string_view value) - { - std::string result{ message }; - FindAndReplace(result, s_MessageReplacementToken, value); - return result; - } - // Follow the rules at https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file to replace // invalid characters in a candidate path part. // Additionally, based on https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index 6ca3f2260d..a56c3d6b37 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -168,9 +168,6 @@ namespace AppInstaller::Utility // Expands environment variables within the input. std::wstring ExpandEnvironmentVariables(const std::wstring& input); - // Replace message predefined token - std::string FindAndReplaceMessageToken(std::string_view message, std::string_view value); - // Converts the candidate path part into one suitable for the actual file system std::string MakeSuitablePathPart(std::string_view candidate); From 442ce148497bc0faef45982a17a8c4095c6ff78b Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 18:50:13 -0700 Subject: [PATCH 08/52] Use placeholders --- src/AppInstallerCLICore/Command.cpp | 9 ++--- .../Commands/FeaturesCommand.cpp | 2 +- .../Commands/RootCommand.cpp | 6 +-- src/AppInstallerCLICore/Core.cpp | 2 +- .../Workflows/ArchiveFlow.cpp | 2 +- .../Workflows/DependencyNodeProcessor.cpp | 10 ++--- .../Workflows/ImportExportFlow.cpp | 18 +++++---- .../Workflows/MSStoreInstallerHandler.cpp | 6 ++- .../Workflows/PortableFlow.cpp | 2 +- .../Workflows/ShowFlow.cpp | 38 +++++++++---------- .../Workflows/SourceFlow.cpp | 24 ++++++------ .../Workflows/UninstallFlow.cpp | 6 +-- .../Workflows/WorkflowBase.cpp | 19 +++++----- 13 files changed, 74 insertions(+), 70 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index f6e06ae7c2..dafc54e833 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -42,9 +42,8 @@ namespace AppInstaller::CLI void Command::OutputIntroHeader(Execution::Reporter& reporter) const { - reporter.Info() << - (Runtime::IsReleaseBuild() ? Resource::String::WindowsPackageManager : Resource::String::WindowsPackageManagerPreview) << " v"_liv << Runtime::GetClientVersion() << std::endl << - Resource::String::MainCopyrightNotice << std::endl; + auto headerStringId = Runtime::IsReleaseBuild() ? Resource::String::WindowsPackageManager : Resource::String::WindowsPackageManagerPreview; + reporter.Info() << headerStringId(Runtime::GetClientVersion()) << std::endl << Resource::String::MainCopyrightNotice << std::endl; } void Command::OutputHelp(Execution::Reporter& reporter, const CommandException* exception) const @@ -108,7 +107,7 @@ namespace AppInstaller::CLI } // Output the command preamble and command chain - infoOut << Resource::String::Usage << ": winget"_liv << Utility::LocIndView{ commandChain }; + infoOut << Resource::String::Usage("winget"_liv, Utility::LocIndView{ commandChain }); auto commandAliases = Aliases(); auto commands = GetVisibleCommands(); @@ -276,7 +275,7 @@ namespace AppInstaller::CLI std::string helpLink = HelpLink(); if (!helpLink.empty()) { - infoOut << std::endl << Resource::String::HelpLinkPreamble << ' ' << helpLink << std::endl; + infoOut << std::endl << Resource::String::HelpLinkPreamble(helpLink) << std::endl; } } diff --git a/src/AppInstallerCLICore/Commands/FeaturesCommand.cpp b/src/AppInstallerCLICore/Commands/FeaturesCommand.cpp index a273681f9e..a9484ca5bc 100644 --- a/src/AppInstallerCLICore/Commands/FeaturesCommand.cpp +++ b/src/AppInstallerCLICore/Commands/FeaturesCommand.cpp @@ -53,7 +53,7 @@ namespace AppInstaller::CLI { table.OutputLine({ std::string{ feature.Name() }, - Resource::Loader::Instance().ResolveString(ExperimentalFeature::IsEnabled(feature.GetFeature()) ? Resource::String::FeaturesEnabled : Resource::String::FeaturesDisabled), + Resource::LocString{ ExperimentalFeature::IsEnabled(feature.GetFeature()) ? Resource::String::FeaturesEnabled : Resource::String::FeaturesDisabled}, std::string { feature.JsonName() }, std::string{ feature.Link() } }); } diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index 9f4d97b4f5..a340bbd405 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -178,14 +178,14 @@ namespace AppInstaller::CLI info << std::endl << "Windows: "_liv << Runtime::GetOSVersion() << std::endl; - info << Resource::String::SystemArchitecture << ": "_liv << Utility::ConvertFromArchitectureEnum(Utility::GetSystemArchitecture()) << std::endl; + info << Resource::String::SystemArchitecture(Utility::ConvertFromArchitectureEnum(Utility::GetSystemArchitecture())) << std::endl; if (Runtime::IsRunningInPackagedContext()) { - info << Resource::String::Package << ": "_liv << Runtime::GetPackageVersion() << std::endl; + info << Resource::String::Package(Runtime::GetPackageVersion()) << std::endl; }; - info << std::endl << Resource::String::Logs << ": "_liv << Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() << std::endl; + info << std::endl << Resource::String::Logs(Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string()) << std::endl; info << std::endl; diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index e8955b8103..5a2e3f8086 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -136,7 +136,7 @@ namespace AppInstaller::CLI // Report any action blocked by Group Policy. auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); AICLI_LOG(CLI, Error, << "Operation blocked by Group Policy: " << policy.RegValueName()); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy << " : "_liv << policy.PolicyName() << std::endl; + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } diff --git a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp index 4c08c97fca..c9e0f53bf2 100644 --- a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp @@ -55,7 +55,7 @@ namespace AppInstaller::CLI::Workflow else if (!std::filesystem::exists(nestedInstallerPath)) { AICLI_LOG(CLI, Error, << "Unable to locate nested installer at: " << nestedInstallerPath); - context.Reporter.Error() << Resource::String::NestedInstallerNotFound << ' ' << nestedInstallerPath << std::endl; + context.Reporter.Error() << Resource::String::NestedInstallerNotFound(nestedInstallerPath) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND); } else diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp index 6592c9efc3..c920d96ab2 100644 --- a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp +++ b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp @@ -31,7 +31,7 @@ namespace AppInstaller::CLI::Workflow if (matches.size() > 1) { - error << Resource::String::DependenciesFlowSourceTooManyMatches << " " << Utility::Normalize(dependencyNode.Id); + error << Resource::String::DependenciesFlowSourceTooManyMatches(Utility::Normalize(dependencyNode.Id)); AICLI_LOG(CLI, Error, << "Too many matches for package " << dependencyNode.Id); return DependencyNodeProcessorResult::Error; } @@ -52,14 +52,14 @@ namespace AppInstaller::CLI::Workflow if (!m_nodePackageLatestVersion) { - error << Resource::String::DependenciesFlowPackageVersionNotFound << " " << Utility::Normalize(packageId); + error << Resource::String::DependenciesFlowPackageVersionNotFound(Utility::Normalize(packageId)); AICLI_LOG(CLI, Error, << "Latest available version not found for package " << packageId); return DependencyNodeProcessorResult::Error; } if (!dependencyNode.IsVersionOk(Utility::Version(m_nodePackageLatestVersion->GetProperty(PackageVersionProperty::Version)))) { - error << Resource::String::DependenciesFlowNoMinVersion << " " << Utility::Normalize(packageId); + error << Resource::String::DependenciesFlowNoMinVersion(Utility::Normalize(packageId)); AICLI_LOG(CLI, Error, << "No suitable min version found for package " << packageId); return DependencyNodeProcessorResult::Error; } @@ -69,7 +69,7 @@ namespace AppInstaller::CLI::Workflow if (m_nodeManifest.Installers.empty()) { - error << Resource::String::DependenciesFlowNoInstallerFound << " " << Utility::Normalize(m_nodeManifest.Id); + error << Resource::String::DependenciesFlowNoInstallerFound(Utility::Normalize(m_nodeManifest.Id)); AICLI_LOG(CLI, Error, << "Installer not found for manifest " << m_nodeManifest.Id << " with version" << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } @@ -85,7 +85,7 @@ namespace AppInstaller::CLI::Workflow if (!installer.has_value()) { - error << Resource::String::DependenciesFlowNoSuitableInstallerFound << " " << Utility::Normalize(m_nodeManifest.Id) << m_nodeManifest.Version; + error << Resource::String::DependenciesFlowNoSuitableInstallerFound(Utility::Normalize(m_nodeManifest.Id)) << m_nodeManifest.Version; AICLI_LOG(CLI, Error, << "No suitable installer found for manifest " << m_nodeManifest.Id << " with version " << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index 20975a5e66..c3933575b7 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -82,9 +82,11 @@ namespace AppInstaller::CLI::Workflow << " Package Id [" << availablePackageVersion->GetProperty(PackageVersionProperty::Id) << "], Version [" << version << "], Channel [" << channel << "]" << ". Found Version [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "], Channel [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "]"); context.Reporter.Warn() - << Resource::String::InstalledPackageVersionNotAvailable - << ' ' << availablePackageVersion->GetProperty(PackageVersionProperty::Id) - << ' ' << version << ' ' << channel << std::endl; + << Resource::String::InstalledPackageVersionNotAvailable( + availablePackageVersion->GetProperty(PackageVersionProperty::Id), + version, + channel) + << std::endl; } } @@ -111,7 +113,7 @@ namespace AppInstaller::CLI::Workflow { // Report package not found and move to next package. AICLI_LOG(CLI, Warning, << "No available version of package [" << installedPackageVersion->GetProperty(PackageVersionProperty::Name) << "] was found to export"); - context.Reporter.Warn() << Resource::String::InstalledPackageNotAvailable << ' ' << installedPackageVersion->GetProperty(PackageVersionProperty::Name) << std::endl; + context.Reporter.Warn() << Resource::String::InstalledPackageNotAvailable(installedPackageVersion->GetProperty(PackageVersionProperty::Name)) << std::endl; continue; } @@ -123,7 +125,7 @@ namespace AppInstaller::CLI::Workflow { // Report that the package requires accepting license terms AICLI_LOG(CLI, Warning, << "Package [" << installedPackageVersion->GetProperty(PackageVersionProperty::Name) << "] requires license agreement to install"); - context.Reporter.Warn() << Resource::String::ExportedPackageRequiresLicenseAgreement << ' ' << installedPackageVersion->GetProperty(PackageVersionProperty::Name) << std::endl; + context.Reporter.Warn() << Resource::String::ExportedPackageRequiresLicenseAgreement(installedPackageVersion->GetProperty(PackageVersionProperty::Name)) << std::endl; } // Find the exported source for this package @@ -231,7 +233,7 @@ namespace AppInstaller::CLI::Workflow else { AICLI_LOG(CLI, Error, << "Missing required source: " << requiredSource.Details.Name); - context.Reporter.Warn() << Resource::String::ImportSourceNotInstalled << ' ' << requiredSource.Details.Name << std::endl; + context.Reporter.Warn() << Resource::String::ImportSourceNotInstalled(requiredSource.Details.Name) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } @@ -307,13 +309,13 @@ namespace AppInstaller::CLI::Workflow else if (searchContext.GetTerminationHR() == APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE) { AICLI_LOG(CLI, Info, << "Package is already installed: [" << packageRequest.Id << "]"); - context.Reporter.Info() << Resource::String::ImportPackageAlreadyInstalled << ' ' << packageRequest.Id << std::endl; + context.Reporter.Info() << Resource::String::ImportPackageAlreadyInstalled(packageRequest.Id) << std::endl; continue; } else { AICLI_LOG(CLI, Info, << "Package not found for import: [" << packageRequest.Id << "], Version " << packageRequest.VersionAndChannel.ToString()); - context.Reporter.Info() << Resource::String::ImportSearchFailed << ' ' << packageRequest.Id << std::endl; + context.Reporter.Info() << Resource::String::ImportSearchFailed(packageRequest.Id) << std::endl; // Keep searching for the remaining packages and only fail at the end. foundAll = false; diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp index 33f067a209..4a0cbf97cc 100644 --- a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -180,7 +180,11 @@ namespace AppInstaller::CLI::Workflow } else { - context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed << ' ' << WINGET_OSTREAM_FORMAT_HRESULT(errorCode) << std::endl; + // TODO: Replace with GetUserPresentableMessage? + std::ostringstream ssError; + ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); + + context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(ssError.str()) << std::endl; AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << WINGET_OSTREAM_FORMAT_HRESULT(errorCode)); AICLI_TERMINATE_CONTEXT(errorCode); } diff --git a/src/AppInstallerCLICore/Workflows/PortableFlow.cpp b/src/AppInstallerCLICore/Workflows/PortableFlow.cpp index 23430d2156..d9c616c446 100644 --- a/src/AppInstallerCLICore/Workflows/PortableFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/PortableFlow.cpp @@ -432,7 +432,7 @@ namespace AppInstaller::CLI::Workflow else if (std::filesystem::remove(symlinkFullPath)) { AICLI_LOG(CLI, Info, << "Removed existing file at " << symlinkFullPath); - context.Reporter.Warn() << Resource::String::OverwritingExistingFileAtMessage << ' ' << symlinkFullPath.u8string() << std::endl; + context.Reporter.Warn() << Resource::String::OverwritingExistingFileAtMessage(symlinkFullPath.u8string()) << std::endl; } std::filesystem::create_symlink(targetFullPath, symlinkFullPath); diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 4127834a0f..91b8f36198 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -40,26 +40,26 @@ namespace AppInstaller::CLI::Workflow auto info = context.Reporter.Info(); // TODO: Come up with a prettier format - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelVersion << ' ' << manifest.Version << std::endl; - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisher << ' ' << manifest.CurrentLocalization.Get() << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelVersion(manifest.Version) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisher(manifest.CurrentLocalization.Get()) << std::endl; auto publisherUrl = manifest.CurrentLocalization.Get(); if (!publisherUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherUrl << ' ' << publisherUrl << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherUrl(publisherUrl) << std::endl; } auto publisherSupportUrl = manifest.CurrentLocalization.Get(); if (!publisherSupportUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherSupportUrl << ' ' << publisherSupportUrl << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherSupportUrl(publisherSupportUrl) << std::endl; } auto author = manifest.CurrentLocalization.Get(); if (!author.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelAuthor << ' ' << author << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelAuthor(author) << std::endl; } if (!manifest.Moniker.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelMoniker << ' ' << manifest.Moniker << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelMoniker(manifest.Moniker) << std::endl; } auto description = manifest.CurrentLocalization.Get(); if (description.empty()) @@ -74,28 +74,28 @@ namespace AppInstaller::CLI::Workflow auto homepage = manifest.CurrentLocalization.Get(); if (!homepage.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPackageUrl << ' ' << homepage << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPackageUrl(homepage) << std::endl; } - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicense << ' ' << manifest.CurrentLocalization.Get() << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicense(manifest.CurrentLocalization.Get()) << std::endl; auto licenseUrl = manifest.CurrentLocalization.Get(); if (!licenseUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicenseUrl << ' ' << licenseUrl << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicenseUrl(licenseUrl) << std::endl; } auto privacyUrl = manifest.CurrentLocalization.Get(); if (!privacyUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPrivacyUrl << ' ' << privacyUrl << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPrivacyUrl(privacyUrl) << std::endl; } auto copyright = manifest.CurrentLocalization.Get(); if (!copyright.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyright << ' ' << copyright << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyright(copyright) << std::endl; } auto copyrightUrl = manifest.CurrentLocalization.Get(); if (!copyrightUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyrightUrl << ' ' << copyrightUrl << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyrightUrl(copyrightUrl) << std::endl; } auto releaseNotes = manifest.CurrentLocalization.Get(); if (!releaseNotes.empty()) @@ -105,7 +105,7 @@ namespace AppInstaller::CLI::Workflow auto releaseNotesUrl = manifest.CurrentLocalization.Get(); if (!releaseNotesUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelReleaseNotesUrl << ' ' << releaseNotesUrl << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelReleaseNotesUrl(releaseNotesUrl) << std::endl; } auto installationNotes = manifest.CurrentLocalization.Get(); if (!installationNotes.empty()) @@ -173,26 +173,26 @@ namespace AppInstaller::CLI::Workflow info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstaller << std::endl; if (installer) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerType << ' ' << Manifest::InstallerTypeToString(installer->InstallerType) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerType(Manifest::InstallerTypeToString(installer->InstallerType)) << std::endl; if (!installer->Locale.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerLocale << ' ' << installer->Locale << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerLocale(installer->Locale) << std::endl; } if (!installer->Url.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerUrl << ' ' << installer->Url << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerUrl(installer->Url) << std::endl; } if (!installer->Sha256.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerSha256 << ' ' << Utility::SHA256::ConvertToString(installer->Sha256) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerSha256(Utility::SHA256::ConvertToString(installer->Sha256)) << std::endl; } if (!installer->ProductId.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerProductId << ' ' << installer->ProductId << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerProductId(installer->ProductId) << std::endl; } if (!installer->ReleaseDate.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerReleaseDate << ' ' << installer->ReleaseDate << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerReleaseDate(installer->ReleaseDate) << std::endl; } if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp index 9dbb3c2e91..ce6e15fbf9 100644 --- a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp @@ -35,7 +35,7 @@ namespace AppInstaller::CLI::Workflow } } - context.Reporter.Error() << Resource::String::SourceListNoneFound << ' ' << name << std::endl; + context.Reporter.Error() << Resource::String::SourceListNoneFound(name) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } else @@ -144,24 +144,24 @@ namespace AppInstaller::CLI::Workflow Execution::TableOutput<2> table(context.Reporter, { Resource::String::SourceListField, Resource::String::SourceListValue }); - table.OutputLine({ Resource::Loader::Instance().ResolveString(Resource::String::SourceListName), source.Name }); - table.OutputLine({ Resource::Loader::Instance().ResolveString(Resource::String::SourceListType), source.Type }); - table.OutputLine({ Resource::Loader::Instance().ResolveString(Resource::String::SourceListArg), source.Arg }); - table.OutputLine({ Resource::Loader::Instance().ResolveString(Resource::String::SourceListData), source.Data }); - table.OutputLine({ Resource::Loader::Instance().ResolveString(Resource::String::SourceListIdentifier), source.Identifier }); + table.OutputLine({ Resource::LocString(Resource::String::SourceListName), source.Name }); + table.OutputLine({ Resource::LocString(Resource::String::SourceListType), source.Type }); + table.OutputLine({ Resource::LocString(Resource::String::SourceListArg), source.Arg }); + table.OutputLine({ Resource::LocString(Resource::String::SourceListData), source.Data }); + table.OutputLine({ Resource::LocString(Resource::String::SourceListIdentifier), source.Identifier }); if (source.LastUpdateTime == Utility::ConvertUnixEpochToSystemClock(0)) { table.OutputLine({ - Resource::Loader::Instance().ResolveString(Resource::String::SourceListUpdated), - Resource::Loader::Instance().ResolveString(Resource::String::SourceListUpdatedNever) + Resource::LocString(Resource::String::SourceListUpdated), + Resource::LocString(Resource::String::SourceListUpdatedNever) }); } else { std::ostringstream strstr; strstr << source.LastUpdateTime; - table.OutputLine({ Resource::Loader::Instance().ResolveString(Resource::String::SourceListUpdated), strstr.str() }); + table.OutputLine({ Resource::LocString(Resource::String::SourceListUpdated), strstr.str() }); } table.Complete(); @@ -195,7 +195,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& sd : sources) { Repository::Source source{ sd.Name }; - context.Reporter.Info() << Resource::String::SourceUpdateOne << ' ' << sd.Name << "..."_liv << std::endl; + context.Reporter.Info() << Resource::String::SourceUpdateOne(sd.Name) << std::endl; auto updateFunction = [&](IProgressCallback& progress)->std::vector { return source.Update(progress); }; if (!context.Reporter.ExecuteWithProgress(updateFunction).empty()) { @@ -221,7 +221,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& sd : sources) { Repository::Source source{ sd.Name }; - context.Reporter.Info() << Resource::String::SourceRemoveOne << ' ' << sd.Name << "..."_liv << std::endl; + context.Reporter.Info() << Resource::String::SourceRemoveOne(sd.Name) << std::endl; auto removeFunction = [&](IProgressCallback& progress)->bool { return source.Remove(progress); }; if (context.Reporter.ExecuteWithProgress(removeFunction)) { @@ -257,7 +257,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& source : sources) { - context.Reporter.Info() << Resource::String::SourceResetOne << ' ' << source.Name << "..."_liv; + context.Reporter.Info() << Resource::String::SourceResetOne(source.Name); Repository::Source::DropSource(source.Name); context.Reporter.Info() << Resource::String::Done << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 19c2d395da..002f79dbd8 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -252,17 +252,17 @@ namespace AppInstaller::CLI::Workflow if (m_isHResult) { - context.Reporter.Error() << Resource::String::UninstallFailedWithCode << ' ' << GetUserPresentableMessage(uninstallResult) << std::endl; + context.Reporter.Error() << Resource::String::UninstallFailedWithCode(GetUserPresentableMessage(uninstallResult)) << std::endl; } else { - context.Reporter.Error() << Resource::String::UninstallFailedWithCode << ' ' << uninstallResult << std::endl; + context.Reporter.Error() << Resource::String::UninstallFailedWithCode(uninstallResult) << std::endl; } // Show installer log path if exists if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get())) { - context.Reporter.Info() << Resource::String::InstallerLogAvailable << ' ' << context.Get().u8string() << std::endl; + context.Reporter.Info() << Resource::String::InstallerLogAvailable(context.Get().u8string()) << std::endl; } AICLI_TERMINATE_CONTEXT(m_hr); diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index bcc280237a..64c872b6f3 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -56,7 +56,7 @@ namespace AppInstaller::CLI::Workflow if (!sourceName.empty() && !sources.empty()) { // A bad name was given, try to help. - context.Reporter.Error() << Resource::String::OpenSourceFailedNoMatch << ' ' << sourceName << std::endl; + context.Reporter.Error() << Resource::String::OpenSourceFailedNoMatch(sourceName) << std::endl; context.Reporter.Info() << Resource::String::OpenSourceFailedNoMatchHelp << std::endl; for (const auto& details : sources) { @@ -88,7 +88,7 @@ namespace AppInstaller::CLI::Workflow // We'll only report the source update failure as warning and continue for (const auto& s : updateFailures) { - context.Reporter.Warn() << Resource::String::SourceOpenWithFailedUpdate << ' ' << s.Name << std::endl; + context.Reporter.Warn() << Resource::String::SourceOpenWithFailedUpdate(s.Name) << std::endl; } } catch (const wil::ResultException& re) @@ -306,7 +306,7 @@ namespace AppInstaller::CLI::Workflow catch (const Settings::GroupPolicyException& e) { auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy << ": "_liv << policy.PolicyName() << std::endl; + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } catch (const Resource::ResourceOpenException& e) @@ -627,7 +627,7 @@ namespace AppInstaller::CLI::Workflow auto warn = context.Reporter.Warn(); for (const auto& failure : searchResult.Failures) { - warn << Resource::String::SearchFailureWarning << ' ' << failure.SourceName << std::endl; + warn << Resource::String::SearchFailureWarning(failure.SourceName) << std::endl; } } else @@ -636,7 +636,7 @@ namespace AppInstaller::CLI::Workflow auto error = context.Reporter.Error(); for (const auto& failure : searchResult.Failures) { - error << Resource::String::SearchFailureError << ' ' << failure.SourceName << std::endl; + error << Resource::String::SearchFailureError(failure.SourceName) << std::endl; HRESULT failureHR = HandleException(context, failure.Exception); // Just take first failure for now @@ -947,13 +947,13 @@ namespace AppInstaller::CLI::Workflow if (!std::filesystem::exists(path)) { - context.Reporter.Error() << Resource::String::VerifyFileFailedNotExist << ' ' << path.u8string() << std::endl; + context.Reporter.Error() << Resource::String::VerifyFileFailedNotExist(path.u8string()) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } if (std::filesystem::is_directory(path)) { - context.Reporter.Error() << Resource::String::VerifyFileFailedIsDirectory << ' ' << path.u8string() << std::endl; + context.Reporter.Error() << Resource::String::VerifyFileFailedIsDirectory(path.u8string()) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_DIRECTORY_NOT_SUPPORTED)); } } @@ -964,7 +964,7 @@ namespace AppInstaller::CLI::Workflow if (!std::filesystem::exists(path)) { - context.Reporter.Error() << Resource::String::VerifyPathFailedNotExist << ' ' << path.u8string() << std::endl; + context.Reporter.Error() << Resource::String::VerifyPathFailedNotExist(path.u8string()) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)); } } @@ -1097,8 +1097,7 @@ namespace AppInstaller::CLI::Workflow { if (!Settings::ExperimentalFeature::IsEnabled(m_feature)) { - context.Reporter.Error() << Resource::String::FeatureDisabledMessage << " : '" << - Settings::ExperimentalFeature::GetFeature(m_feature).JsonName() << '\'' << std::endl; + context.Reporter.Error() << Resource::String::FeatureDisabledMessage(Settings::ExperimentalFeature::GetFeature(m_feature).JsonName()) << std::endl; AICLI_LOG(CLI, Error, << Settings::ExperimentalFeature::GetFeature(m_feature).Name() << " feature is disabled. Execution cancelled."); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED); } From cb262e2e1040919eff24d4c9e1152a5cb3363466 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 11 Aug 2022 19:15:27 -0700 Subject: [PATCH 09/52] Fixed build errors --- src/AppInstallerCLICore/Core.cpp | 2 +- src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp | 2 +- src/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index 5a2e3f8086..e068aacc7f 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -136,7 +136,7 @@ namespace AppInstaller::CLI // Report any action blocked by Group Policy. auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); AICLI_LOG(CLI, Error, << "Operation blocked by Group Policy: " << policy.RegValueName()); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()) << std::endl; + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } diff --git a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp index c9e0f53bf2..c080067bf2 100644 --- a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp @@ -55,7 +55,7 @@ namespace AppInstaller::CLI::Workflow else if (!std::filesystem::exists(nestedInstallerPath)) { AICLI_LOG(CLI, Error, << "Unable to locate nested installer at: " << nestedInstallerPath); - context.Reporter.Error() << Resource::String::NestedInstallerNotFound(nestedInstallerPath) << std::endl; + context.Reporter.Error() << Resource::String::NestedInstallerNotFound(nestedInstallerPath.u8string()) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND); } else diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 64c872b6f3..a3d43cd7e5 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -306,7 +306,7 @@ namespace AppInstaller::CLI::Workflow catch (const Settings::GroupPolicyException& e) { auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()) << std::endl; + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } catch (const Resource::ResourceOpenException& e) From fd92fef55add85062b0fedf70171cbe746f85190 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 09:02:22 -0700 Subject: [PATCH 10/52] Updated resources and tests --- src/AppInstallerCLICore/Command.cpp | 29 ++-------- src/AppInstallerCLICore/Command.h | 3 - .../Workflows/ImportExportFlow.cpp | 8 +-- .../Workflows/InstallFlow.cpp | 8 +-- .../Workflows/MSStoreInstallerHandler.cpp | 10 +++- .../Workflows/ShowFlow.cpp | 18 ++---- .../Workflows/WorkflowBase.cpp | 14 +---- src/AppInstallerCLITests/Command.cpp | 56 +++++-------------- src/AppInstallerCLITests/WorkFlow.cpp | 14 ++--- .../Public/winget/Resources.h | 2 +- 10 files changed, 47 insertions(+), 115 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index dafc54e833..73daf9a10b 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -42,8 +42,9 @@ namespace AppInstaller::CLI void Command::OutputIntroHeader(Execution::Reporter& reporter) const { - auto headerStringId = Runtime::IsReleaseBuild() ? Resource::String::WindowsPackageManager : Resource::String::WindowsPackageManagerPreview; - reporter.Info() << headerStringId(Runtime::GetClientVersion()) << std::endl << Resource::String::MainCopyrightNotice << std::endl; + auto productName = Runtime::IsReleaseBuild() ? Resource::String::WindowsPackageManager : Resource::String::WindowsPackageManagerPreview; + auto productVersion = Utility::Format("v{0}", Runtime::GetClientVersion()); + reporter.Info() << productName << " " << productVersion << std::endl << Resource::String::MainCopyrightNotice << std::endl; } void Command::OutputHelp(Execution::Reporter& reporter, const CommandException* exception) const @@ -55,29 +56,7 @@ namespace AppInstaller::CLI // Error if given if (exception) { - auto error = reporter.Error(); - error << exception->Message(); - - if (!exception->Params().empty()) - { - error << " :"_liv; - bool first = true; - for (const auto& param : exception->Params()) - { - if (first) - { - first = false; - } - else - { - error << ','; - } - error << " '"_liv << param << '\''; - } - } - - error << std::endl << - std::endl; + reporter.Error() << exception->Message() << std::endl << std::endl; } // Description diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 345786f9a3..78e12ebc2f 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -23,13 +23,10 @@ namespace AppInstaller::CLI struct CommandException { CommandException(Resource::LocString message) : m_message(std::move(message)) {} - const Utility::LocIndString Message() const; - const std::vector& Params() const { return m_params; } private: Resource::LocString m_message; - std::vector m_params; }; struct Command diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index c3933575b7..3cc0d9659e 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -81,12 +81,8 @@ namespace AppInstaller::CLI::Workflow << "Installed package version is not available." << " Package Id [" << availablePackageVersion->GetProperty(PackageVersionProperty::Id) << "], Version [" << version << "], Channel [" << channel << "]" << ". Found Version [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "], Channel [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "]"); - context.Reporter.Warn() - << Resource::String::InstalledPackageVersionNotAvailable( - availablePackageVersion->GetProperty(PackageVersionProperty::Id), - version, - channel) - << std::endl; + auto packageInfo = Utility::Format("{0} {1} {2}", availablePackageVersion->GetProperty(PackageVersionProperty::Id), version, channel); + context.Reporter.Warn() << Resource::String::InstalledPackageVersionNotAvailable(packageInfo) << std::endl; } } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 2260d28750..41f16d43df 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -240,7 +240,7 @@ namespace AppInstaller::CLI::Workflow if (!installationNotes.empty()) { - context.Reporter.Info() << Resource::String::Notes << ' ' << installationNotes << std::endl; + context.Reporter.Info() << Resource::String::Notes(installationNotes) << std::endl; } } } @@ -472,17 +472,17 @@ namespace AppInstaller::CLI::Workflow if (m_isHResult) { - context.Reporter.Error() << Resource::String::InstallerFailedWithCode << ' ' << GetUserPresentableMessage(installResult) << std::endl; + context.Reporter.Error() << Resource::String::InstallerFailedWithCode(GetUserPresentableMessage(installResult)) << std::endl; } else { - context.Reporter.Error() << Resource::String::InstallerFailedWithCode << ' ' << installResult << std::endl; + context.Reporter.Error() << Resource::String::InstallerFailedWithCode(installResult) << std::endl; } // Show installer log path if exists if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get())) { - context.Reporter.Info() << Resource::String::InstallerLogAvailable << ' ' << context.Get().u8string() << std::endl; + context.Reporter.Info() << Resource::String::InstallerLogAvailable(context.Get().u8string()) << std::endl; } // Show a specific message if we can identify the return code diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp index 4a0cbf97cc..5c6c3625c2 100644 --- a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -137,8 +137,12 @@ namespace AppInstaller::CLI::Workflow } else { - context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed << ' ' << WINGET_OSTREAM_FORMAT_HRESULT(errorCode) << std::endl; - AICLI_LOG(CLI, Error, << "MSStore install failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << WINGET_OSTREAM_FORMAT_HRESULT(errorCode)); + // TODO: Replace with GetUserPresentableMessage? + std::ostringstream ssError; + ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); + + context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(ssError.str()) << std::endl; + AICLI_LOG(CLI, Error, << "MSStore install failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << ssError.str()); AICLI_TERMINATE_CONTEXT(errorCode); } } @@ -185,7 +189,7 @@ namespace AppInstaller::CLI::Workflow ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(ssError.str()) << std::endl; - AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << WINGET_OSTREAM_FORMAT_HRESULT(errorCode)); + AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << ssError.str()); AICLI_TERMINATE_CONTEXT(errorCode); } } diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 91b8f36198..8d41a271b2 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -12,18 +12,10 @@ using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; namespace { - void ShowMultiLineField(Execution::OutputStream& outputStream, AppInstaller::StringResource::StringId label, std::string& value) + std::string MultiLineFieldFormat(std::string& value) { bool isMultiLine = FindAndReplace(value, "\n", "\n "); - outputStream << Execution::ManifestInfoEmphasis << label; - if (isMultiLine) - { - outputStream << std::endl << " "_liv << value << std::endl; - } - else - { - outputStream << ' ' << value << std::endl; - } + return Format(isMultiLine ? "\n {0}" : " {0}", value); } } @@ -69,7 +61,7 @@ namespace AppInstaller::CLI::Workflow } if (!description.empty()) { - ShowMultiLineField(info, Resource::String::ShowLabelDescription, description); + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelDescription(MultiLineFieldFormat(description)) << std::endl; } auto homepage = manifest.CurrentLocalization.Get(); if (!homepage.empty()) @@ -100,7 +92,7 @@ namespace AppInstaller::CLI::Workflow auto releaseNotes = manifest.CurrentLocalization.Get(); if (!releaseNotes.empty()) { - ShowMultiLineField(info, Resource::String::ShowLabelReleaseNotes, releaseNotes); + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelReleaseNotes(MultiLineFieldFormat(releaseNotes)) << std::endl; } auto releaseNotesUrl = manifest.CurrentLocalization.Get(); if (!releaseNotesUrl.empty()) @@ -110,7 +102,7 @@ namespace AppInstaller::CLI::Workflow auto installationNotes = manifest.CurrentLocalization.Get(); if (!installationNotes.empty()) { - ShowMultiLineField(info, Resource::String::ShowLabelInstallationNotes, installationNotes); + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallationNotes(MultiLineFieldFormat(installationNotes)) << std::endl; } const auto& documentations = manifest.CurrentLocalization.Get(); if (!documentations.empty()) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index a3d43cd7e5..481a40141a 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -908,18 +908,8 @@ namespace AppInstaller::CLI::Workflow if (!manifest) { - auto errorStream = context.Reporter.Error(); - errorStream << Resource::String::GetManifestResultVersionNotFound << ' '; - if (!m_version.empty()) - { - errorStream << m_version; - } - if (!m_channel.empty()) - { - errorStream << '[' << m_channel << ']'; - } - - errorStream << std::endl; + auto versionInfo = Utility::Format(m_channel.empty() ? "{0}{1}" : "{0}[{1}]", m_version, m_channel); + context.Reporter.Error() << Resource::String::GetManifestResultVersionNotFound(versionInfo) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } diff --git a/src/AppInstallerCLITests/Command.cpp b/src/AppInstallerCLITests/Command.cpp index f1b213dfb1..fd27e1e98c 100644 --- a/src/AppInstallerCLITests/Command.cpp +++ b/src/AppInstallerCLITests/Command.cpp @@ -202,55 +202,29 @@ struct TestCommand : public Command // Matcher that lets us verify CommandExceptions. struct CommandExceptionMatcher : public Catch::MatcherBase { - CommandExceptionMatcher(const std::string &arg) : m_expectedArg(arg) {} + CommandExceptionMatcher(CLI::Resource::LocString message) : m_expectedMessage(std::move(message)) {} bool match(const CommandException& ce) const override { - const auto& params = ce.Params(); - return params.size() == 1 && params[0].get() == m_expectedArg; + return ce.Message() == m_expectedMessage; } std::string describe() const override { std::ostringstream result; - result << "has param == " << m_expectedArg; + result << "has message == " << m_expectedMessage; return result.str(); } private: - std::string m_expectedArg; + CLI::Resource::LocString m_expectedMessage; }; namespace Catch { template<> struct StringMaker { static std::string convert(CommandException const& ce) { - std::string result{ "CommandException{ '" }; - result += ce.Message().get(); - result += '\''; - - bool first = true; - for (const auto& param : ce.Params()) - { - if (first) - { - first = false; - result += ", ['"; - } - else - { - result += "', '"; - } - result += param.get(); - } - - if (!first) - { - result += "']"; - } - - result += " }"; - return result; + return Utility::Format("CommandException{ '{0}' }", ce.Message().get()); } }; } @@ -313,7 +287,7 @@ TEST_CASE("ParseArguments_TooManyPositional", "[command]") std::vector values{ "val1", "--", "-std1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[2]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::ExtraPositionalError(values[2])); } TEST_CASE("ParseArguments_InvalidChar", "[command]") @@ -328,7 +302,7 @@ TEST_CASE("ParseArguments_InvalidChar", "[command]") std::vector values{ "val1", "-", "-std1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[1]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidArgumentSpecifierError(values[1])); } TEST_CASE("ParseArguments_InvalidAlias", "[command]") @@ -343,7 +317,7 @@ TEST_CASE("ParseArguments_InvalidAlias", "[command]") std::vector values{ "val1", "-b", "-std1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[1]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidAliasError(values[1])); } TEST_CASE("ParseArguments_MultiFlag", "[command]") @@ -378,7 +352,7 @@ TEST_CASE("ParseArguments_FlagThenUnknown", "[command]") std::vector values{ "val1", "-sr", "val2" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[1]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(values[1])); } TEST_CASE("ParseArguments_FlagThenNonFlag", "[command]") @@ -394,7 +368,7 @@ TEST_CASE("ParseArguments_FlagThenNonFlag", "[command]") std::vector values{ "val1", "-sp", "val2" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[1]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFlagError(values[1])); } TEST_CASE("ParseArguments_NameUsingAliasSpecifier", "[command]") @@ -410,7 +384,7 @@ TEST_CASE("ParseArguments_NameUsingAliasSpecifier", "[command]") std::vector values{ "another", "-flag1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[1]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(values[1])); } TEST_CASE("ParseArguments_AliasWithAdjoinedValue", "[command]") @@ -459,7 +433,7 @@ TEST_CASE("ParseArguments_AliasWithSeparatedValueMissing", "[command]") std::vector values{ "-s" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[0]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(values[0])); } TEST_CASE("ParseArguments_NameWithAdjoinedValue", "[command]") @@ -528,7 +502,7 @@ TEST_CASE("ParseArguments_NameFlagWithAdjoinedValue", "[command]") std::vector values{ "another", "--flag1=arbitrary" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[1]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::FlagContainAdjoinedError(values[1])); } TEST_CASE("ParseArguments_NameWithSeparatedValue", "[command]") @@ -562,7 +536,7 @@ TEST_CASE("ParseArguments_NameWithSeparatedValueMissing", "[command]") std::vector values{ "--pos2" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[0]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(values[0])); } TEST_CASE("ParseArguments_UnknownName", "[command]") @@ -578,5 +552,5 @@ TEST_CASE("ParseArguments_UnknownName", "[command]") std::vector values{ "another", "--nope" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), values[1]); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidNameError(values[1])); } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index a4873d0e85..6193f0a6ea 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1065,7 +1065,7 @@ TEST_CASE("InstallFlow_Zip_BadRelativePath", "[InstallFlow][workflow]") // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NestedInstallerNotFound).get()) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NestedInstallerNotFound("")).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_MissingNestedInstaller", "[InstallFlow][workflow]") @@ -2089,7 +2089,7 @@ TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotFound", "[UpdateFlow][workflow] // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); - REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::GetManifestResultVersionNotFound).get()) != std::string::npos); + REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::GetManifestResultVersionNotFound("1.2.3.4")).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } @@ -2613,7 +2613,7 @@ TEST_CASE("ImportFlow_PackageAlreadyInstalled", "[ImportFlow][workflow]") // Exe should not have been installed again REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportPackageAlreadyInstalled).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportPackageAlreadyInstalled("AppInstallerCliTest.TestExeInstaller")).get()) != std::string::npos); } TEST_CASE("ImportFlow_IgnoreVersions", "[ImportFlow][workflow]") @@ -2651,7 +2651,7 @@ TEST_CASE("ImportFlow_MissingSource", "[ImportFlow][workflow]") // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSourceNotInstalled).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSourceNotInstalled("TestSource")).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } @@ -2671,7 +2671,7 @@ TEST_CASE("ImportFlow_MissingPackage", "[ImportFlow][workflow]") // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("MissingPackage")).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND); } @@ -2693,7 +2693,7 @@ TEST_CASE("ImportFlow_IgnoreMissingPackage", "[ImportFlow][workflow]") // Verify installer was called for the package that was available. REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("MissingPackage")).get()) != std::string::npos); } TEST_CASE("ImportFlow_MissingVersion", "[ImportFlow][workflow]") @@ -2712,7 +2712,7 @@ TEST_CASE("ImportFlow_MissingVersion", "[ImportFlow][workflow]") // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("AppInstallerCliTest.TestExeInstaller")).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND); } diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 9ad6ba993c..a6816b61b7 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -82,7 +82,7 @@ namespace AppInstaller Utility::LocIndString operator()(T ... args) const { auto resolvedId = AppInstaller::Resource::Loader::Instance().ResolveString(*this); - return Utility::LocIndString(Utility::Format(resolvedId, args...)); + return Utility::LocIndString(Utility::Format(resolvedId, std::forward(args)...)); } }; From cdd4f47a2765eee296e4e08dc92239eeaede6bab Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 09:10:54 -0700 Subject: [PATCH 11/52] Updated resw --- .../Shared/Strings/en-us/winget.resw | 158 +++++++++--------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 2eed8113b5..8e567345bc 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -118,10 +118,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Adjoined alias is not a flag + Adjoined alias is not a flag: '{0}' - Adjoined flag alias not found + Adjoined flag alias not found: '{0}' The following arguments are available: @@ -189,10 +189,10 @@ Experimental feature example - Found a positional argument when none was expected + Found a positional argument when none was expected: '{0}' - This feature is a work in progress, and may be changed dramatically or removed altogether in the future. To enable it, edit your settings ('winget settings') to include the experimental feature + This feature is a work in progress, and may be changed dramatically or removed altogether in the future. To enable it, edit your settings ('winget settings') to include the experimental feature: '{0}' {Locked="winget settings"} @@ -229,7 +229,7 @@ They can be configured through the settings file 'winget settings'. File to be hashed - Flag argument cannot contain adjoined value + Flag argument cannot contain adjoined value: '{0}' Computes the hash of a local file, appropriate for entry into a manifest. It can also compute the hash of the signature file of an MSIX package to enable streaming installations. @@ -244,7 +244,7 @@ They can be configured through the settings file 'winget settings'. For more details on a specific command, pass it the help argument. - More help can be found at: + More help can be found at: {0} Filter results by id @@ -296,13 +296,13 @@ They can be configured through the settings file 'winget settings'. Request interactive installation; user input may be needed - Argument alias was not recognized for the current command + Argument alias was not recognized for the current command: '{0}' - Invalid argument specifier + Invalid argument specifier: '{0}' - Argument name was not recognized for the current command + Argument name was not recognized for the current command: '{0}' Locale to use (BCP47 format) @@ -348,7 +348,7 @@ They can be configured through the settings file 'winget settings'. Manifest validation succeeded with warnings. - Argument value required, but none found + Argument value required, but none found: '{0}' Filter results by moniker @@ -363,7 +363,7 @@ They can be configured through the settings file 'winget settings'. Failed to install or update Microsoft Store package because the specific app is blocked by policy - Failed to install or update Microsoft Store package. Error code: + Failed to install or update Microsoft Store package. Error code: {0} Verifying/Requesting package acquisition failed: network error @@ -416,7 +416,7 @@ They can be configured through the settings file 'winget settings'. Override arguments to be passed on to the installer - Package + Package: {0} A software package @@ -435,7 +435,7 @@ They can be configured through the settings file 'winget settings'. Progress display a rainbow of colors - Required argument not provided + Required argument not provided: '{0}' Progress display as the default color @@ -495,7 +495,7 @@ They can be configured through the settings file 'winget settings'. Request silent installation - Only the single character alias can occur after a single - + Only the single character alias can occur after a single -: '{0}' A source with the given name already exists and refers to a different location: @@ -554,7 +554,7 @@ They can be configured through the settings file 'winget settings'. The source's unique identifier. - Did not find a source named: + Did not find a source named: {0} There are no sources configured. @@ -596,7 +596,7 @@ They can be configured through the settings file 'winget settings'. Remove current sources - Removing source: + Removing source: {0} Resetting all sources... @@ -615,7 +615,7 @@ They can be configured through the settings file 'winget settings'. {Locked="--force"} - Resetting source: + Resetting source: {0} Type of the source @@ -630,7 +630,7 @@ They can be configured through the settings file 'winget settings'. Update current sources - Updating source: + Updating source: {0} Filter results by tag @@ -651,16 +651,16 @@ They can be configured through the settings file 'winget settings'. Display the version of the tool - Argument provided more times than allowed + Argument provided more times than allowed: '{0}' - More than one execution behavior argument provided + More than one execution behavior argument provided: '{0}' An unexpected error occurred while executing the command: - Unrecognized command + Unrecognized command: '{0}' Update all installed packages to latest if available @@ -676,7 +676,7 @@ They can be configured through the settings file 'winget settings'. Shows and performs available upgrades - usage + usage: {0} {1} The way to use the software @@ -704,10 +704,10 @@ They can be configured through the settings file 'winget settings'. The value provided before completion is requested - No version found matching: + No version found matching: {0} - No sources match the given value: + No sources match the given value: {0} The configured sources are: @@ -720,16 +720,16 @@ They can be configured through the settings file 'winget settings'. Found - Path is a directory: + Path is a directory: {0} - File does not exist: + File does not exist: {0} Both local manifest and search query arguments are provided - Logs + Logs: {0} Diagnostic files containing information about application use. @@ -742,7 +742,7 @@ They can be configured through the settings file 'winget settings'. An anti-virus product reports an infection in the installer - Failed in attempting to update the source: + Failed in attempting to update the source: {0} Uninstalls the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. @@ -765,7 +765,7 @@ They can be configured through the settings file 'winget settings'. Uninstallation abandoned - Uninstall failed with exit code: + Uninstall failed with exit code: {0} Exports a list of the installed packages @@ -793,16 +793,16 @@ They can be configured through the settings file 'winget settings'. One or more imported packages failed to install - Package not found for import: + Package not found for import: {0} - Source required for import is not installed: + Source required for import is not installed: {0} - Installed package is not available from any source: + Installed package is not available from any source: {0} - Installed version of package is not available from any source: + Installed version of package is not available from any source: {0} No packages found in import file @@ -811,7 +811,7 @@ They can be configured through the settings file 'winget settings'. JSON file is not valid - Package is already installed: + Package is already installed: {0} Ignore unavailable packages @@ -823,7 +823,7 @@ They can be configured through the settings file 'winget settings'. Ignore package versions from import file - Path does not exist: + Path does not exist: {0} The JSON file does not specify a recognized schema. @@ -833,11 +833,11 @@ They can be configured through the settings file 'winget settings'. This argument allows the user to select between installing for just the user or for the entire machine. - The value provided for the `%1` argument is invalid; valid values are + The value provided for the `{0}` argument is invalid; valid values are: {1} {Locked="%1"} The value will be replaced with the argument name - This operation is disabled by Group Policy + This operation is disabled by Group Policy: {0} Enable Additional Windows App Installer Sources @@ -928,7 +928,7 @@ Configuration is disabled due to Group Policy. A source that the user is allowed to add. - The value provided for the `%1` argument is invalid + The value provided for the `{0}` argument is invalid {Locked="%1"} The value will be replaced with the argument name @@ -952,18 +952,18 @@ Configuration is disabled due to Group Policy. Dependency source not found - Package search yield more than one result. + Package search yield more than one result: {0} When node package id search yield too many matches - Latest version not found for package + Latest version not found for package: {0} When no suitable version found for the specific package. - No installers found + No installers found: {0} - Minimum required version not available for package + Minimum required version not available for package: {0} No matches @@ -974,7 +974,7 @@ Configuration is disabled due to Group Policy. Dependency graph has loop - No suitable installer found for manifest + No suitable installer found for manifest: {0} Attempt to get preferred installer for manifest failed. @@ -1012,7 +1012,7 @@ Configuration is disabled due to Group Policy. Accept all license agreements for packages - Exported package requires license agreement to install: + Exported package requires license agreement to install: {0} The publisher requires that you view the above information and accept the agreements before installing. @@ -1025,49 +1025,49 @@ Do you agree to the terms? Agreements: - Author: + Author: {0} - Description: + Description: {0} Installer: - Installer Locale: + Installer Locale: {0} - Store Product Id: + Store Product Id: {0} - Installer SHA256: + Installer SHA256: {0} - Installer Type: + Installer Type: {0} - Installer Url: + Installer Url: {0} - License: + License: {0} - License Url: + License Url: {0} - Moniker: + Moniker: {0} - Homepage: + Homepage: {0} - Publisher: + Publisher: {0} Tags: - Version: + Version: {0} Dependencies: @@ -1097,7 +1097,7 @@ Do you agree to the terms? Accept all source agreements during source operations - The `%1` source requires that you view the following agreements before using. + The `{0}` source requires that you view the following agreements before using. {Locked="%1"} The value will be replaced with the source name @@ -1119,40 +1119,40 @@ Do you agree to the terms? Ignoring the optional header as it is not applicable for this source. - The optional header is not applicable without specifying a source + The optional header is not applicable without specifying a source: '{0}' - Release Date: + Release Date: {0} - Publisher Url: + Publisher Url: {0} - Publisher Support Url: + Publisher Support Url: {0} - Privacy Url: + Privacy Url: {0} - Copyright: + Copyright: {0} - Copyright Url: + Copyright Url: {0} - Release Notes: + Release Notes: {0} - Release Notes Url: + Release Notes Url: {0} - Failed when searching source; results will not be included: + Failed when searching source; results will not be included: {0} - Failed when searching source: + Failed when searching source: {0} - This feature needs to be enabled by administrators. To enable it, run 'winget settings --enable %1' as administrator. + This feature needs to be enabled by administrators. To enable it, run 'winget settings --enable {0}' as administrator. {Locked="winget settings --enable %1"} The value will be replaced with the admin setting @@ -1162,7 +1162,7 @@ Do you agree to the terms? Disables the specific administrator setting - Too many admin setting arguments provided + Too many admin setting arguments provided: '{0}' Admin setting enabled. @@ -1219,10 +1219,10 @@ Do you agree to the terms? Installation abandoned - Installer failed with exit code: + Installer failed with exit code: {0} - Installer log is available at: + Installer log is available at: {0} The following packages were found among the working sources. @@ -1287,7 +1287,7 @@ Please specify one of them using the `--source` option to proceed. The arguments provided can only be used with a query. - System Architecture + System Architecture: {0} Retains all files and directories created by the package (portable) @@ -1326,7 +1326,7 @@ Please specify one of them using the `--source` option to proceed. The specified filename is not a valid filename - Overwriting existing file: + Overwriting existing file: {0} No package selection argument was provided; see the help for details about finding a package. @@ -1346,7 +1346,7 @@ Please specify one of them using the `--source` option to proceed. {Locked="--force"} - Files remain in install directory: + Files remain in install directory: {0} Purging install directory... @@ -1361,10 +1361,10 @@ Please specify one of them using the `--source` option to proceed. Documentation: - Notes: + Notes: {0} - Installation Notes: + Installation Notes: {0} Portable symlink not deleted as it was modified and points to a different target exe @@ -1379,7 +1379,7 @@ Please specify one of them using the `--source` option to proceed. Failed to extract the contents of the archive - Nested installer file does not exist. Ensure the specified relative path of the nested installer matches: + Nested installer file does not exist. Ensure the specified relative path of the nested installer matches: {0} Invalid relative file path to the nested installer; path points to a location outside of the install directory From aafa6b77002476618fcc6ec18c0868acdac1aad6 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 09:36:08 -0700 Subject: [PATCH 12/52] Fixed dependencies test --- src/AppInstallerCLITests/Dependencies.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/Dependencies.cpp b/src/AppInstallerCLITests/Dependencies.cpp index 04f3d6f7bf..de02bf8836 100644 --- a/src/AppInstallerCLITests/Dependencies.cpp +++ b/src/AppInstallerCLITests/Dependencies.cpp @@ -161,7 +161,7 @@ TEST_CASE("DependencyNodeProcessor_NoInstallers", "[dependencies]") Dependency rootAsDependency(DependencyType::Package, manifest.Id); DependencyNodeProcessorResult result = nodeProcessor.EvaluateDependencies(rootAsDependency); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowNoInstallerFound)) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowNoInstallerFound("withoutInstallers"))) != std::string::npos); REQUIRE(result == DependencyNodeProcessorResult::Error); } From d9d5197dccca42f6d1e6b10e2859ee28cc2f8c7b Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 10:25:42 -0700 Subject: [PATCH 13/52] Moved loader to cpp --- src/AppInstallerCLICore/Resources.h | 17 +- .../Public/winget/Resources.h | 36 +---- src/AppInstallerCommonCore/Resources.cpp | 148 ++++++++++-------- 3 files changed, 101 insertions(+), 100 deletions(-) diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index 51232cc6bf..bdf852d71c 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -10,8 +10,6 @@ namespace AppInstaller::CLI::Resource { using AppInstaller::StringResource::StringId; - using AppInstaller::Resource::LocString; - using AppInstaller::Resource::Loader; using AppInstaller::Resource::ResourceOpenException; // Resource string identifiers. @@ -405,6 +403,21 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(WordArgumentDescription); }; + // A localized string + struct LocString : public Utility::LocIndString + { + LocString() = default; + + LocString(StringId id) : Utility::LocIndString(id()) {} + LocString(const LocIndString& lis) : Utility::LocIndString(lis) {} + + LocString(const LocString&) = default; + LocString& operator=(const LocString&) = default; + + LocString(LocString&&) = default; + LocString& operator=(LocString&&) = default; + }; + // Fixed strings are not localized, but we use a similar system to prevent duplication enum class FixedString { diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index a6816b61b7..d585b7a15a 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -25,37 +25,6 @@ namespace AppInstaller std::pair GetResourceAsBytes(int resourceName, int resourceType); std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType); - // A localized string - struct LocString : public Utility::LocIndString - { - LocString() = default; - - LocString(StringResource::StringId id); - LocString(const LocIndString& lis) : Utility::LocIndString(lis) {} - - LocString(const LocString&) = default; - LocString& operator=(const LocString&) = default; - - LocString(LocString&&) = default; - LocString& operator=(LocString&&) = default; - }; - - // Utility class to load resources - class Loader - { - public: - // Gets the singleton instance of the resource loader. - static const Loader& Instance(); - - // Gets the the string resource value. - std::string ResolveString(std::wstring_view resKey) const; - - private: - winrt::Windows::ApplicationModel::Resources::ResourceLoader m_wingetLoader; - - Loader(); - }; - struct ResourceOpenException : std::exception { ResourceOpenException(const winrt::hresult_error& hre); @@ -81,9 +50,10 @@ namespace AppInstaller template Utility::LocIndString operator()(T ... args) const { - auto resolvedId = AppInstaller::Resource::Loader::Instance().ResolveString(*this); - return Utility::LocIndString(Utility::Format(resolvedId, std::forward(args)...)); + return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; } + private: + std::string Resolve() const; }; // Resource string identifiers. diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index ee057576d0..5595eb8d97 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -6,94 +6,112 @@ #include "Public/AppInstallerStrings.h" #include "Public//AppInstallerErrors.h" -namespace AppInstaller::Resource +namespace AppInstaller { - namespace + namespace Resource { - std::pair GetResourceData(PCWSTR resourceName, PCWSTR resourceType) + namespace { - HMODULE resourceModule = nullptr; - GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - reinterpret_cast(GetResourceData), - &resourceModule); - THROW_LAST_ERROR_IF_NULL(resourceModule); + std::pair GetResourceData(PCWSTR resourceName, PCWSTR resourceType) + { + HMODULE resourceModule = nullptr; + GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(GetResourceData), + &resourceModule); + THROW_LAST_ERROR_IF_NULL(resourceModule); - HRSRC resourceInfoHandle = FindResourceW(resourceModule, resourceName, resourceType); - THROW_LAST_ERROR_IF_NULL(resourceInfoHandle); + HRSRC resourceInfoHandle = FindResourceW(resourceModule, resourceName, resourceType); + THROW_LAST_ERROR_IF_NULL(resourceInfoHandle); - HGLOBAL resourceMemoryHandle = LoadResource(resourceModule, resourceInfoHandle); - THROW_LAST_ERROR_IF_NULL(resourceMemoryHandle); + HGLOBAL resourceMemoryHandle = LoadResource(resourceModule, resourceInfoHandle); + THROW_LAST_ERROR_IF_NULL(resourceMemoryHandle); - DWORD resourceSize = SizeofResource(resourceModule, resourceInfoHandle); - THROW_LAST_ERROR_IF(resourceSize == 0); + DWORD resourceSize = SizeofResource(resourceModule, resourceInfoHandle); + THROW_LAST_ERROR_IF(resourceSize == 0); - void* resourceContent = LockResource(resourceMemoryHandle); - THROW_HR_IF_NULL(E_UNEXPECTED, resourceContent); + void* resourceContent = LockResource(resourceMemoryHandle); + THROW_HR_IF_NULL(E_UNEXPECTED, resourceContent); - return std::make_pair(resourceContent, static_cast(resourceSize)); + return std::make_pair(resourceContent, static_cast(resourceSize)); + } } - } - std::string_view GetResourceAsString(int resourceName, int resourceType) - { - return GetResourceAsString(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); - } + // Utility class to load resources + class Loader + { + public: + // Gets the singleton instance of the resource loader. + static const Loader& Instance() + { + static Loader instance; + return instance; + } - std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType) - { - auto resourceData = GetResourceData(resourceName, resourceType); - return { reinterpret_cast(resourceData.first), resourceData.second }; - } + // Gets the the string resource value. + std::string ResolveString(std::wstring_view resKey) const + { + return Utility::ConvertToUTF8(m_wingetLoader.GetString(resKey)); + } - std::pair GetResourceAsBytes(int resourceName, int resourceType) - { - return GetResourceAsBytes(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); - } + private: + winrt::Windows::ApplicationModel::Resources::ResourceLoader m_wingetLoader; - std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType) - { - auto resourceData = GetResourceData(resourceName, resourceType); - return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); - } + Loader() : m_wingetLoader(nullptr) + { + try + { + // The default constructor of ResourceLoader throws a winrt::hresult_error exception + // when resource.pri is not found. ResourceLoader::GetForViewIndependentUse also throws + // a winrt::hresult_error but for reasons unknown it only gets catch when running on the + // debugger. Running without a debugger will result in a crash that not even adding a + // catch all fix. To provide a good error message we call the default constructor + // before calling GetForViewIndependentUse. + m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader(); + m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse(L"winget"); + } + catch (const winrt::hresult_error& hre) + { + // This message cannot be localized. + AICLI_LOG(CLI, Error, << "Failure loading resource file with error: " << hre.code()); + throw ResourceOpenException(hre); + } + } + }; - LocString::LocString(StringResource::StringId id) : Utility::LocIndString(id()) {} + std::string_view GetResourceAsString(int resourceName, int resourceType) + { + return GetResourceAsString(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); + } - const Loader& Loader::Instance() - { - static Loader instance; - return instance; - } + std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType) + { + auto resourceData = GetResourceData(resourceName, resourceType); + return { reinterpret_cast(resourceData.first), resourceData.second }; + } - Loader::Loader() : m_wingetLoader(nullptr) - { - try + std::pair GetResourceAsBytes(int resourceName, int resourceType) { - // The default constructor of ResourceLoader throws a winrt::hresult_error exception - // when resource.pri is not found. ResourceLoader::GetForViewIndependentUse also throws - // a winrt::hresult_error but for reasons unknown it only gets catch when running on the - // debugger. Running without a debugger will result in a crash that not even adding a - // catch all fix. To provide a good error message we call the default constructor - // before calling GetForViewIndependentUse. - m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader(); - m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse(L"winget"); + return GetResourceAsBytes(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); } - catch (const winrt::hresult_error& hre) + + std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType) { - // This message cannot be localized. - AICLI_LOG(CLI, Error, << "Failure loading resource file with error: " << hre.code()); - throw ResourceOpenException(hre); + auto resourceData = GetResourceData(resourceName, resourceType); + return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); } - } - std::string Loader::ResolveString( - std::wstring_view resKey) const - { - return Utility::ConvertToUTF8(m_wingetLoader.GetString(resKey)); + ResourceOpenException::ResourceOpenException(const winrt::hresult_error& hre) + { + m_message = "Could not open the resource file: " + GetUserPresentableMessage(hre); + } } - ResourceOpenException::ResourceOpenException(const winrt::hresult_error& hre) + namespace StringResource { - m_message = "Could not open the resource file: " + GetUserPresentableMessage(hre); + std::string StringId::Resolve() const + { + return Resource::Loader::Instance().ResolveString(*this); + } } } From e5403633ae291ed2372a6e6ecf993db8699a23f4 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 10:58:07 -0700 Subject: [PATCH 14/52] Moved functions in same file --- .../Public/winget/Resources.h | 47 ++++++++-------- src/AppInstallerCommonCore/Resources.cpp | 54 +++++++++---------- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index d585b7a15a..ce5b6aa20f 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -12,30 +12,6 @@ using namespace std::string_view_literals; namespace AppInstaller { - namespace StringResource { struct StringId; } - namespace Resource - { - // Get an embedded resource from the binary and return as std::string_view. - // Resource data is valid as long as the binary is loaded. - std::string_view GetResourceAsString(int resourceName, int resourceType); - std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType); - - // Get an embedded resource from the binary and return as std::pair. - // Resource data is valid as long as the binary is loaded. - std::pair GetResourceAsBytes(int resourceName, int resourceType); - std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType); - - struct ResourceOpenException : std::exception - { - ResourceOpenException(const winrt::hresult_error& hre); - - const char* what() const noexcept override { return m_message.c_str(); } - - private: - std::string m_message; - }; - } - namespace StringResource { #define WINGET_WIDE_STRINGIFY_HELP(_id_) L ## _id_ @@ -77,4 +53,27 @@ namespace AppInstaller WINGET_DEFINE_RESOURCE_STRINGID(SettingsWarningParseError); }; } + + namespace Resource + { + // Get an embedded resource from the binary and return as std::string_view. + // Resource data is valid as long as the binary is loaded. + std::string_view GetResourceAsString(int resourceName, int resourceType); + std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType); + + // Get an embedded resource from the binary and return as std::pair. + // Resource data is valid as long as the binary is loaded. + std::pair GetResourceAsBytes(int resourceName, int resourceType); + std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType); + + struct ResourceOpenException : std::exception + { + ResourceOpenException(const winrt::hresult_error& hre); + + const char* what() const noexcept override { return m_message.c_str(); } + + private: + std::string m_message; + }; + } } diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 5595eb8d97..9edef38ac2 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -37,6 +37,33 @@ namespace AppInstaller } } + std::string_view GetResourceAsString(int resourceName, int resourceType) + { + return GetResourceAsString(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); + } + + std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType) + { + auto resourceData = GetResourceData(resourceName, resourceType); + return { reinterpret_cast(resourceData.first), resourceData.second }; + } + + std::pair GetResourceAsBytes(int resourceName, int resourceType) + { + return GetResourceAsBytes(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); + } + + std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType) + { + auto resourceData = GetResourceData(resourceName, resourceType); + return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); + } + + ResourceOpenException::ResourceOpenException(const winrt::hresult_error& hre) + { + m_message = "Could not open the resource file: " + GetUserPresentableMessage(hre); + } + // Utility class to load resources class Loader { @@ -78,33 +105,6 @@ namespace AppInstaller } } }; - - std::string_view GetResourceAsString(int resourceName, int resourceType) - { - return GetResourceAsString(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); - } - - std::string_view GetResourceAsString(PCWSTR resourceName, PCWSTR resourceType) - { - auto resourceData = GetResourceData(resourceName, resourceType); - return { reinterpret_cast(resourceData.first), resourceData.second }; - } - - std::pair GetResourceAsBytes(int resourceName, int resourceType) - { - return GetResourceAsBytes(MAKEINTRESOURCE(resourceName), MAKEINTRESOURCE(resourceType)); - } - - std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType) - { - auto resourceData = GetResourceData(resourceName, resourceType); - return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); - } - - ResourceOpenException::ResourceOpenException(const winrt::hresult_error& hre) - { - m_message = "Could not open the resource file: " + GetUserPresentableMessage(hre); - } } namespace StringResource From dbb5568e02c201fe7c654935890d7f8f33f5f061 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 11:41:45 -0700 Subject: [PATCH 15/52] Added more placeholders --- src/AppInstallerCLICore/Workflows/SettingsFlow.cpp | 4 ++-- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp index 9834bc7605..7b71668877 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp @@ -36,7 +36,7 @@ namespace AppInstaller::CLI::Workflow { if (warning.IsFieldWarning) { - warn << ' ' << Resource::String::SettingsWarningField << ' ' << warning.Path; + warn << ' ' << Resource::String::SettingsWarningField(warning.Path); } else { @@ -48,7 +48,7 @@ namespace AppInstaller::CLI::Workflow { if (warning.IsFieldWarning) { - warn << ' ' << Resource::String::SettingsWarningValue << ' ' << warning.Data; + warn << ' ' << Resource::String::SettingsWarningValue(warning.Data); } else { diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 8e567345bc..3317af2ef0 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -883,7 +883,7 @@ They can be configured through the settings file 'winget settings'. Enable Windows App Installer Local Manifest Files - Field: + Field: {0} Invalid field format. @@ -901,7 +901,7 @@ They can be configured through the settings file 'winget settings'. Error parsing file: - Value: + Value: {0} The following experimental features are in progress. From 49c79a7a5f527bd5d543207f5265f0eb170212c1 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 13:06:21 -0700 Subject: [PATCH 16/52] Simplify command --- src/AppInstallerCLICore/Command.cpp | 5 ----- src/AppInstallerCLICore/Command.h | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 73daf9a10b..cb087b2f7d 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -13,11 +13,6 @@ namespace AppInstaller::CLI { constexpr std::string_view s_Command_ArgName_SilentAndInteractive = "silent|interactive"sv; - const Utility::LocIndString CommandException::Message() const - { - return Utility::LocIndString{ m_message.get() }; - } - Command::Command( std::string_view name, std::vector aliases, diff --git a/src/AppInstallerCLICore/Command.h b/src/AppInstallerCLICore/Command.h index 78e12ebc2f..d4db307f62 100644 --- a/src/AppInstallerCLICore/Command.h +++ b/src/AppInstallerCLICore/Command.h @@ -23,7 +23,7 @@ namespace AppInstaller::CLI struct CommandException { CommandException(Resource::LocString message) : m_message(std::move(message)) {} - const Utility::LocIndString Message() const; + const Utility::LocIndString Message() const { return m_message; } private: Resource::LocString m_message; From 76b784569b1daf6bf829322c0c96faf28cfedb33 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 14:28:48 -0700 Subject: [PATCH 17/52] Revert changes for ShowFlow.cpp --- .../Workflows/ShowFlow.cpp | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp index 8d41a271b2..4127834a0f 100644 --- a/src/AppInstallerCLICore/Workflows/ShowFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ShowFlow.cpp @@ -12,10 +12,18 @@ using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; namespace { - std::string MultiLineFieldFormat(std::string& value) + void ShowMultiLineField(Execution::OutputStream& outputStream, AppInstaller::StringResource::StringId label, std::string& value) { bool isMultiLine = FindAndReplace(value, "\n", "\n "); - return Format(isMultiLine ? "\n {0}" : " {0}", value); + outputStream << Execution::ManifestInfoEmphasis << label; + if (isMultiLine) + { + outputStream << std::endl << " "_liv << value << std::endl; + } + else + { + outputStream << ' ' << value << std::endl; + } } } @@ -32,26 +40,26 @@ namespace AppInstaller::CLI::Workflow auto info = context.Reporter.Info(); // TODO: Come up with a prettier format - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelVersion(manifest.Version) << std::endl; - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisher(manifest.CurrentLocalization.Get()) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelVersion << ' ' << manifest.Version << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisher << ' ' << manifest.CurrentLocalization.Get() << std::endl; auto publisherUrl = manifest.CurrentLocalization.Get(); if (!publisherUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherUrl(publisherUrl) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherUrl << ' ' << publisherUrl << std::endl; } auto publisherSupportUrl = manifest.CurrentLocalization.Get(); if (!publisherSupportUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherSupportUrl(publisherSupportUrl) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPublisherSupportUrl << ' ' << publisherSupportUrl << std::endl; } auto author = manifest.CurrentLocalization.Get(); if (!author.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelAuthor(author) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelAuthor << ' ' << author << std::endl; } if (!manifest.Moniker.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelMoniker(manifest.Moniker) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelMoniker << ' ' << manifest.Moniker << std::endl; } auto description = manifest.CurrentLocalization.Get(); if (description.empty()) @@ -61,48 +69,48 @@ namespace AppInstaller::CLI::Workflow } if (!description.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelDescription(MultiLineFieldFormat(description)) << std::endl; + ShowMultiLineField(info, Resource::String::ShowLabelDescription, description); } auto homepage = manifest.CurrentLocalization.Get(); if (!homepage.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPackageUrl(homepage) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPackageUrl << ' ' << homepage << std::endl; } - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicense(manifest.CurrentLocalization.Get()) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicense << ' ' << manifest.CurrentLocalization.Get() << std::endl; auto licenseUrl = manifest.CurrentLocalization.Get(); if (!licenseUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicenseUrl(licenseUrl) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelLicenseUrl << ' ' << licenseUrl << std::endl; } auto privacyUrl = manifest.CurrentLocalization.Get(); if (!privacyUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPrivacyUrl(privacyUrl) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelPrivacyUrl << ' ' << privacyUrl << std::endl; } auto copyright = manifest.CurrentLocalization.Get(); if (!copyright.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyright(copyright) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyright << ' ' << copyright << std::endl; } auto copyrightUrl = manifest.CurrentLocalization.Get(); if (!copyrightUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyrightUrl(copyrightUrl) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelCopyrightUrl << ' ' << copyrightUrl << std::endl; } auto releaseNotes = manifest.CurrentLocalization.Get(); if (!releaseNotes.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelReleaseNotes(MultiLineFieldFormat(releaseNotes)) << std::endl; + ShowMultiLineField(info, Resource::String::ShowLabelReleaseNotes, releaseNotes); } auto releaseNotesUrl = manifest.CurrentLocalization.Get(); if (!releaseNotesUrl.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelReleaseNotesUrl(releaseNotesUrl) << std::endl; + info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelReleaseNotesUrl << ' ' << releaseNotesUrl << std::endl; } auto installationNotes = manifest.CurrentLocalization.Get(); if (!installationNotes.empty()) { - info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallationNotes(MultiLineFieldFormat(installationNotes)) << std::endl; + ShowMultiLineField(info, Resource::String::ShowLabelInstallationNotes, installationNotes); } const auto& documentations = manifest.CurrentLocalization.Get(); if (!documentations.empty()) @@ -165,26 +173,26 @@ namespace AppInstaller::CLI::Workflow info << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstaller << std::endl; if (installer) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerType(Manifest::InstallerTypeToString(installer->InstallerType)) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerType << ' ' << Manifest::InstallerTypeToString(installer->InstallerType) << std::endl; if (!installer->Locale.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerLocale(installer->Locale) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerLocale << ' ' << installer->Locale << std::endl; } if (!installer->Url.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerUrl(installer->Url) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerUrl << ' ' << installer->Url << std::endl; } if (!installer->Sha256.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerSha256(Utility::SHA256::ConvertToString(installer->Sha256)) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerSha256 << ' ' << Utility::SHA256::ConvertToString(installer->Sha256) << std::endl; } if (!installer->ProductId.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerProductId(installer->ProductId) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerProductId << ' ' << installer->ProductId << std::endl; } if (!installer->ReleaseDate.empty()) { - info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerReleaseDate(installer->ReleaseDate) << std::endl; + info << " "_liv << Execution::ManifestInfoEmphasis << Resource::String::ShowLabelInstallerReleaseDate << ' ' << installer->ReleaseDate << std::endl; } if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::Dependencies)) From 3aa289b6514df7f5e0ce1f65de4505385e99a67a Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 12 Aug 2022 14:35:21 -0700 Subject: [PATCH 18/52] Revert ShowFlow.cpp resw data --- .../Shared/Strings/en-us/winget.resw | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 3317af2ef0..620861b5e2 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1025,49 +1025,49 @@ Do you agree to the terms? Agreements: - Author: {0} + Author: - Description: {0} + Description: Installer: - Installer Locale: {0} + Installer Locale: - Store Product Id: {0} + Store Product Id: - Installer SHA256: {0} + Installer SHA256: - Installer Type: {0} + Installer Type: - Installer Url: {0} + Installer Url: - License: {0} + License: - License Url: {0} + License Url: - Moniker: {0} + Moniker: - Homepage: {0} + Homepage: - Publisher: {0} + Publisher: Tags: - Version: {0} + Version: Dependencies: @@ -1122,28 +1122,28 @@ Do you agree to the terms? The optional header is not applicable without specifying a source: '{0}' - Release Date: {0} + Release Date: - Publisher Url: {0} + Publisher Url: - Publisher Support Url: {0} + Publisher Support Url: - Privacy Url: {0} + Privacy Url: - Copyright: {0} + Copyright: - Copyright Url: {0} + Copyright Url: - Release Notes: {0} + Release Notes: - Release Notes Url: {0} + Release Notes Url: Failed when searching source; results will not be included: {0} @@ -1364,7 +1364,7 @@ Please specify one of them using the `--source` option to proceed. Notes: {0} - Installation Notes: {0} + Installation Notes: Portable symlink not deleted as it was modified and points to a different target exe From 2a1a142dbd2108bfea57d31fa3e73449b4609ec0 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Sat, 13 Aug 2022 01:39:37 -0700 Subject: [PATCH 19/52] Added type constraints --- src/AppInstallerCLICore/ChannelStreams.h | 30 --------- src/AppInstallerCLICore/Command.cpp | 28 ++++----- .../Commands/InstallCommand.cpp | 4 +- .../Commands/RootCommand.cpp | 3 +- src/AppInstallerCLICore/Resources.h | 21 +------ .../Workflows/ArchiveFlow.cpp | 4 +- .../Workflows/DependencyNodeProcessor.cpp | 13 ++-- .../Workflows/ImportExportFlow.cpp | 6 +- .../Workflows/InstallFlow.cpp | 11 +++- .../Workflows/MSStoreInstallerHandler.cpp | 4 +- .../Workflows/PortableFlow.cpp | 4 +- .../Workflows/SettingsFlow.cpp | 4 +- .../Workflows/SourceFlow.cpp | 8 +-- .../Workflows/UninstallFlow.cpp | 11 +++- .../Workflows/WorkflowBase.cpp | 25 +++++--- .../Shared/Strings/en-us/winget.resw | 2 +- src/AppInstallerCLITests/Command.cpp | 20 +++--- src/AppInstallerCLITests/Completion.cpp | 34 +++++------ src/AppInstallerCLITests/Dependencies.cpp | 3 +- src/AppInstallerCLITests/WorkFlow.cpp | 17 +++--- .../AppInstallerStrings.cpp | 17 ++++++ .../Public/AppInstallerStrings.h | 21 +------ .../Public/winget/Resources.h | 61 +++++++++++++++++-- 23 files changed, 191 insertions(+), 160 deletions(-) diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index 0a2cbe8d1c..7c0bbc7493 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -11,36 +11,6 @@ namespace AppInstaller::CLI::Execution { - namespace details - { - // List of approved types for output, others are potentially not localized. - template - struct IsApprovedForOutput - { - static constexpr bool value = false; - }; - -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) \ - template <> \ - struct IsApprovedForOutput<_t_> \ - { \ - static constexpr bool value = true; \ - } - - // It is assumed that single char values need not be localized, as they are matched - // ordinally or they are punctuation / other. - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(char); - // Localized strings (and from an Id for one for convenience). - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Resource::StringId); - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Resource::LocString); - // Strings explicitly declared as localization independent. - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndView); - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndString); - // Normalized strings come from user data and should therefore already by localized - // by how they are chosen (or there is no localized version). - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::NormalizedString); - } - // The base stream for all channels. struct BaseStream { diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index cb087b2f7d..b11c9e15a2 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -11,7 +11,7 @@ using namespace AppInstaller::Settings; namespace AppInstaller::CLI { - constexpr std::string_view s_Command_ArgName_SilentAndInteractive = "silent|interactive"sv; + constexpr Utility::LocIndView s_Command_ArgName_SilentAndInteractive = "silent|interactive"_liv; Command::Command( std::string_view name, @@ -246,7 +246,7 @@ namespace AppInstaller::CLI } // Finally, the link to the documentation pages - std::string helpLink = HelpLink(); + auto helpLink = Utility::LocIndString{ HelpLink() }; if (!helpLink.empty()) { infoOut << std::endl << Resource::String::HelpLinkPreamble(helpLink) << std::endl; @@ -280,7 +280,7 @@ namespace AppInstaller::CLI { auto feature = ExperimentalFeature::GetFeature(command->Feature()); AICLI_LOG(CLI, Error, << "Trying to use command: " << *itr << " without enabling feature " << feature.JsonName()); - throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); + throw CommandException(Resource::String::FeatureDisabledMessage(Utility::LocIndView{ feature.JsonName() })); } if (!Settings::GroupPolicies().IsEnabled(command->GroupPolicy())) @@ -297,7 +297,7 @@ namespace AppInstaller::CLI } // TODO: If we get to a large number of commands, do a fuzzy search much like git - throw CommandException(Resource::String::UnrecognizedCommand(*itr)); + throw CommandException(Resource::String::UnrecognizedCommand(Utility::LocIndView{ *itr })); } // The argument parsing state machine. @@ -398,7 +398,7 @@ namespace AppInstaller::CLI // If the next argument was to be a value, but none was provided, convert it to an exception. else if (m_state.Type() && m_invocationItr == m_invocation.end()) { - throw CommandException(Resource::String::MissingArgumentError(m_state.Arg())); + throw CommandException(Resource::String::MissingArgumentError(Utility::LocIndView{ m_state.Arg() })); } } @@ -454,7 +454,7 @@ namespace AppInstaller::CLI const CLI::Argument* nextPositional = NextPositional(); if (!nextPositional) { - return CommandException(Resource::String::ExtraPositionalError(currArg)); + return CommandException(Resource::String::ExtraPositionalError(Utility::LocIndView{ currArg })); } m_executionArgs.AddArg(nextPositional->ExecArgType(), currArg); @@ -462,7 +462,7 @@ namespace AppInstaller::CLI // The currentArg must not be empty, and starts with a - else if (currArg.length() == 1) { - return CommandException(Resource::String::InvalidArgumentSpecifierError(currArg)); + return CommandException(Resource::String::InvalidArgumentSpecifierError(Utility::LocIndView{ currArg })); } // Now it must be at least 2 chars else if (currArg[1] != APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR) @@ -473,7 +473,7 @@ namespace AppInstaller::CLI auto itr = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr == m_arguments.end()) { - return CommandException(Resource::String::InvalidAliasError(currArg)); + return CommandException(Resource::String::InvalidAliasError(Utility::LocIndView{ currArg })); } if (itr->Type() == ArgumentType::Flag) @@ -487,11 +487,11 @@ namespace AppInstaller::CLI auto itr2 = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr2 == m_arguments.end()) { - return CommandException(Resource::String::AdjoinedNotFoundError(currArg)); + return CommandException(Resource::String::AdjoinedNotFoundError(Utility::LocIndView{ currArg })); } else if (itr2->Type() != ArgumentType::Flag) { - return CommandException(Resource::String::AdjoinedNotFlagError(currArg)); + return CommandException(Resource::String::AdjoinedNotFlagError(Utility::LocIndView{ currArg })); } else { @@ -507,7 +507,7 @@ namespace AppInstaller::CLI } else { - return CommandException(Resource::String::SingleCharAfterDashError(currArg)); + return CommandException(Resource::String::SingleCharAfterDashError(Utility::LocIndView{ currArg })); } } else @@ -550,7 +550,7 @@ namespace AppInstaller::CLI { if (hasValue) { - return CommandException(Resource::String::FlagContainAdjoinedError(currArg)); + return CommandException(Resource::String::FlagContainAdjoinedError(Utility::LocIndView{ currArg })); } m_executionArgs.AddArg(arg.ExecArgType()); @@ -570,7 +570,7 @@ namespace AppInstaller::CLI if (!argFound) { - return CommandException(Resource::String::InvalidNameError(currArg)); + return CommandException(Resource::String::InvalidNameError(Utility::LocIndView{ currArg })); } } @@ -630,7 +630,7 @@ namespace AppInstaller::CLI { auto feature = ExperimentalFeature::GetFeature(arg.Feature()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " without enabling feature " << feature.JsonName()); - throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); + throw CommandException(Resource::String::FeatureDisabledMessage(Utility::LocIndView{ feature.JsonName() })); } if (arg.Required() && !execArgs.Contains(arg.ExecArgType())) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 6ba58b0bf7..f26bfc6f29 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -113,7 +113,7 @@ namespace AppInstaller::CLI { if (ConvertToScopeEnum(execArgs.GetArg(Args::Type::InstallScope)) == Manifest::ScopeEnum::Unknown) { - auto validOptions = Utility::Format("'{0}', '{1}'", "user"_lis, "machine"_lis); + auto validOptions = Utility::LocIndString{ Utility::Format("'{0}', '{1}'", "user"_lis, "machine"_lis) }; throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Scope, validOptions)); } } @@ -127,7 +127,7 @@ namespace AppInstaller::CLI { applicableArchitectures.emplace_back(Utility::Format("'{0}'", Utility::ConvertFromArchitectureEnum(i))); } - auto validOptions = Utility::Join(", ", applicableArchitectures); + auto validOptions = Utility::Join(", "_liv, applicableArchitectures); throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Architecture, validOptions)); } } diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index a340bbd405..9d472fd0d7 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -185,7 +185,8 @@ namespace AppInstaller::CLI info << Resource::String::Package(Runtime::GetPackageVersion()) << std::endl; }; - info << std::endl << Resource::String::Logs(Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string()) << std::endl; + auto logPath = Utility::LocIndString{ Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() }; + info << std::endl << Resource::String::Logs(logPath) << std::endl; info << std::endl; diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index bdf852d71c..d34cd2f728 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -10,6 +10,7 @@ namespace AppInstaller::CLI::Resource { using AppInstaller::StringResource::StringId; + using AppInstaller::Resource::LocString; using AppInstaller::Resource::ResourceOpenException; // Resource string identifiers. @@ -403,21 +404,6 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(WordArgumentDescription); }; - // A localized string - struct LocString : public Utility::LocIndString - { - LocString() = default; - - LocString(StringId id) : Utility::LocIndString(id()) {} - LocString(const LocIndString& lis) : Utility::LocIndString(lis) {} - - LocString(const LocString&) = default; - LocString& operator=(const LocString&) = default; - - LocString(LocString&&) = default; - LocString& operator=(LocString&&) = default; - }; - // Fixed strings are not localized, but we use a similar system to prevent duplication enum class FixedString { @@ -427,11 +413,6 @@ namespace AppInstaller::CLI::Resource Utility::LocIndView GetFixedString(FixedString fs); } -inline std::ostream& operator<<(std::ostream& out, AppInstaller::CLI::Resource::StringId si) -{ - return (out << AppInstaller::CLI::Resource::LocString{ si }); -} - inline std::ostream& operator<<(std::ostream& out, AppInstaller::CLI::Resource::FixedString fs) { return (out << GetFixedString(fs)); diff --git a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp index c080067bf2..7411a43b09 100644 --- a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp @@ -55,7 +55,9 @@ namespace AppInstaller::CLI::Workflow else if (!std::filesystem::exists(nestedInstallerPath)) { AICLI_LOG(CLI, Error, << "Unable to locate nested installer at: " << nestedInstallerPath); - context.Reporter.Error() << Resource::String::NestedInstallerNotFound(nestedInstallerPath.u8string()) << std::endl; + context.Reporter.Error() + << Resource::String::NestedInstallerNotFound(Utility::LocIndView{ nestedInstallerPath.u8string() }) + << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND); } else diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp index c920d96ab2..ce30b73b07 100644 --- a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp +++ b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp @@ -31,7 +31,8 @@ namespace AppInstaller::CLI::Workflow if (matches.size() > 1) { - error << Resource::String::DependenciesFlowSourceTooManyMatches(Utility::Normalize(dependencyNode.Id)); + auto dependencyNodeId = Utility::LocIndString{ Utility::Normalize(dependencyNode.Id) }; + error << Resource::String::DependenciesFlowSourceTooManyMatches(dependencyNodeId); AICLI_LOG(CLI, Error, << "Too many matches for package " << dependencyNode.Id); return DependencyNodeProcessorResult::Error; } @@ -52,14 +53,14 @@ namespace AppInstaller::CLI::Workflow if (!m_nodePackageLatestVersion) { - error << Resource::String::DependenciesFlowPackageVersionNotFound(Utility::Normalize(packageId)); + error << Resource::String::DependenciesFlowPackageVersionNotFound(Utility::LocIndView{ Utility::Normalize(packageId) }); AICLI_LOG(CLI, Error, << "Latest available version not found for package " << packageId); return DependencyNodeProcessorResult::Error; } if (!dependencyNode.IsVersionOk(Utility::Version(m_nodePackageLatestVersion->GetProperty(PackageVersionProperty::Version)))) { - error << Resource::String::DependenciesFlowNoMinVersion(Utility::Normalize(packageId)); + error << Resource::String::DependenciesFlowNoMinVersion(Utility::LocIndView{ Utility::Normalize(packageId) }); AICLI_LOG(CLI, Error, << "No suitable min version found for package " << packageId); return DependencyNodeProcessorResult::Error; } @@ -69,7 +70,7 @@ namespace AppInstaller::CLI::Workflow if (m_nodeManifest.Installers.empty()) { - error << Resource::String::DependenciesFlowNoInstallerFound(Utility::Normalize(m_nodeManifest.Id)); + error << Resource::String::DependenciesFlowNoInstallerFound(Utility::LocIndView{ Utility::Normalize(m_nodeManifest.Id) }); AICLI_LOG(CLI, Error, << "Installer not found for manifest " << m_nodeManifest.Id << " with version" << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } @@ -85,7 +86,9 @@ namespace AppInstaller::CLI::Workflow if (!installer.has_value()) { - error << Resource::String::DependenciesFlowNoSuitableInstallerFound(Utility::Normalize(m_nodeManifest.Id)) << m_nodeManifest.Version; + auto manifestId = Utility::LocIndString{ Utility::Normalize(m_nodeManifest.Id) }; + auto manifestVersion = Utility::LocIndString{ m_nodeManifest.Version }; + error << Resource::String::DependenciesFlowNoSuitableInstallerFound(manifestId, manifestVersion); AICLI_LOG(CLI, Error, << "No suitable installer found for manifest " << m_nodeManifest.Id << " with version " << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index 3cc0d9659e..ae6d166000 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -81,7 +81,7 @@ namespace AppInstaller::CLI::Workflow << "Installed package version is not available." << " Package Id [" << availablePackageVersion->GetProperty(PackageVersionProperty::Id) << "], Version [" << version << "], Channel [" << channel << "]" << ". Found Version [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "], Channel [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "]"); - auto packageInfo = Utility::Format("{0} {1} {2}", availablePackageVersion->GetProperty(PackageVersionProperty::Id), version, channel); + auto packageInfo = Utility::LocIndString{ Utility::Format("{0} {1} {2}", availablePackageVersion->GetProperty(PackageVersionProperty::Id), version, channel) }; context.Reporter.Warn() << Resource::String::InstalledPackageVersionNotAvailable(packageInfo) << std::endl; } } @@ -229,7 +229,9 @@ namespace AppInstaller::CLI::Workflow else { AICLI_LOG(CLI, Error, << "Missing required source: " << requiredSource.Details.Name); - context.Reporter.Warn() << Resource::String::ImportSourceNotInstalled(requiredSource.Details.Name) << std::endl; + context.Reporter.Warn() + << Resource::String::ImportSourceNotInstalled(Utility::LocIndView{ requiredSource.Details.Name }) + << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 41f16d43df..f4fbfd1361 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -472,17 +472,22 @@ namespace AppInstaller::CLI::Workflow if (m_isHResult) { - context.Reporter.Error() << Resource::String::InstallerFailedWithCode(GetUserPresentableMessage(installResult)) << std::endl; + context.Reporter.Error() + << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ GetUserPresentableMessage(installResult) }) + << std::endl; } else { - context.Reporter.Error() << Resource::String::InstallerFailedWithCode(installResult) << std::endl; + context.Reporter.Error() + << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ std::to_string(installResult) }) + << std::endl; } // Show installer log path if exists if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get())) { - context.Reporter.Info() << Resource::String::InstallerLogAvailable(context.Get().u8string()) << std::endl; + auto installerLogPath = Utility::LocIndString{ context.Get().u8string() }; + context.Reporter.Info() << Resource::String::InstallerLogAvailable(installerLogPath) << std::endl; } // Show a specific message if we can identify the return code diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp index 5c6c3625c2..4ad12619ee 100644 --- a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -141,7 +141,7 @@ namespace AppInstaller::CLI::Workflow std::ostringstream ssError; ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); - context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(ssError.str()) << std::endl; + context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(Utility::LocIndView{ ssError.str() }) << std::endl; AICLI_LOG(CLI, Error, << "MSStore install failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << ssError.str()); AICLI_TERMINATE_CONTEXT(errorCode); } @@ -188,7 +188,7 @@ namespace AppInstaller::CLI::Workflow std::ostringstream ssError; ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); - context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(ssError.str()) << std::endl; + context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(Utility::LocIndView{ ssError.str() }) << std::endl; AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << ssError.str()); AICLI_TERMINATE_CONTEXT(errorCode); } diff --git a/src/AppInstallerCLICore/Workflows/PortableFlow.cpp b/src/AppInstallerCLICore/Workflows/PortableFlow.cpp index d9c616c446..71d51f1a97 100644 --- a/src/AppInstallerCLICore/Workflows/PortableFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/PortableFlow.cpp @@ -432,7 +432,9 @@ namespace AppInstaller::CLI::Workflow else if (std::filesystem::remove(symlinkFullPath)) { AICLI_LOG(CLI, Info, << "Removed existing file at " << symlinkFullPath); - context.Reporter.Warn() << Resource::String::OverwritingExistingFileAtMessage(symlinkFullPath.u8string()) << std::endl; + context.Reporter.Warn() + << Resource::String::OverwritingExistingFileAtMessage(Utility::LocIndView{ symlinkFullPath.u8string() }) + << std::endl; } std::filesystem::create_symlink(targetFullPath, symlinkFullPath); diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp index 7b71668877..9f53904220 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp @@ -36,7 +36,7 @@ namespace AppInstaller::CLI::Workflow { if (warning.IsFieldWarning) { - warn << ' ' << Resource::String::SettingsWarningField(warning.Path); + warn << ' ' << Resource::String::SettingsWarningField(Utility::LocIndView{ warning.Path }); } else { @@ -48,7 +48,7 @@ namespace AppInstaller::CLI::Workflow { if (warning.IsFieldWarning) { - warn << ' ' << Resource::String::SettingsWarningValue(warning.Data); + warn << ' ' << Resource::String::SettingsWarningValue(Utility::LocIndView{ warning.Data }); } else { diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp index ce6e15fbf9..5ecea09b5b 100644 --- a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp @@ -35,7 +35,7 @@ namespace AppInstaller::CLI::Workflow } } - context.Reporter.Error() << Resource::String::SourceListNoneFound(name) << std::endl; + context.Reporter.Error() << Resource::String::SourceListNoneFound(Utility::LocIndView{ name }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } else @@ -195,7 +195,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& sd : sources) { Repository::Source source{ sd.Name }; - context.Reporter.Info() << Resource::String::SourceUpdateOne(sd.Name) << std::endl; + context.Reporter.Info() << Resource::String::SourceUpdateOne(Utility::LocIndView{ sd.Name }) << std::endl; auto updateFunction = [&](IProgressCallback& progress)->std::vector { return source.Update(progress); }; if (!context.Reporter.ExecuteWithProgress(updateFunction).empty()) { @@ -221,7 +221,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& sd : sources) { Repository::Source source{ sd.Name }; - context.Reporter.Info() << Resource::String::SourceRemoveOne(sd.Name) << std::endl; + context.Reporter.Info() << Resource::String::SourceRemoveOne(Utility::LocIndView{ sd.Name }) << std::endl; auto removeFunction = [&](IProgressCallback& progress)->bool { return source.Remove(progress); }; if (context.Reporter.ExecuteWithProgress(removeFunction)) { @@ -257,7 +257,7 @@ namespace AppInstaller::CLI::Workflow for (const auto& source : sources) { - context.Reporter.Info() << Resource::String::SourceResetOne(source.Name); + context.Reporter.Info() << Resource::String::SourceResetOne(Utility::LocIndView{ source.Name }); Repository::Source::DropSource(source.Name); context.Reporter.Info() << Resource::String::Done << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 002f79dbd8..4e4c4e062f 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -252,17 +252,22 @@ namespace AppInstaller::CLI::Workflow if (m_isHResult) { - context.Reporter.Error() << Resource::String::UninstallFailedWithCode(GetUserPresentableMessage(uninstallResult)) << std::endl; + context.Reporter.Error() + << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ GetUserPresentableMessage(uninstallResult) }) + << std::endl; } else { - context.Reporter.Error() << Resource::String::UninstallFailedWithCode(uninstallResult) << std::endl; + context.Reporter.Error() + << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ std::to_string(uninstallResult) }) + << std::endl; } // Show installer log path if exists if (context.Contains(Execution::Data::LogPath) && std::filesystem::exists(context.Get())) { - context.Reporter.Info() << Resource::String::InstallerLogAvailable(context.Get().u8string()) << std::endl; + auto installerLogPath = Utility::LocIndString{ context.Get().u8string() }; + context.Reporter.Info() << Resource::String::InstallerLogAvailable(installerLogPath) << std::endl; } AICLI_TERMINATE_CONTEXT(m_hr); diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 481a40141a..07c6f4f0ec 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -56,7 +56,7 @@ namespace AppInstaller::CLI::Workflow if (!sourceName.empty() && !sources.empty()) { // A bad name was given, try to help. - context.Reporter.Error() << Resource::String::OpenSourceFailedNoMatch(sourceName) << std::endl; + context.Reporter.Error() << Resource::String::OpenSourceFailedNoMatch(Utility::LocIndView{ sourceName }) << std::endl; context.Reporter.Info() << Resource::String::OpenSourceFailedNoMatchHelp << std::endl; for (const auto& details : sources) { @@ -88,7 +88,7 @@ namespace AppInstaller::CLI::Workflow // We'll only report the source update failure as warning and continue for (const auto& s : updateFailures) { - context.Reporter.Warn() << Resource::String::SourceOpenWithFailedUpdate(s.Name) << std::endl; + context.Reporter.Warn() << Resource::String::SourceOpenWithFailedUpdate(Utility::LocIndView{ s.Name }) << std::endl; } } catch (const wil::ResultException& re) @@ -166,7 +166,10 @@ namespace AppInstaller::CLI::Workflow } // Show source agreements - context.Reporter.Info() << Execution::SourceInfoEmphasis << Resource::String::SourceAgreementsTitle(details.Name) << std::endl; + context.Reporter.Info() + << Execution::SourceInfoEmphasis + << Resource::String::SourceAgreementsTitle(Utility::LocIndView{ details.Name }) + << std::endl; const auto& agreements = source.GetInformation().SourceAgreements; @@ -627,7 +630,7 @@ namespace AppInstaller::CLI::Workflow auto warn = context.Reporter.Warn(); for (const auto& failure : searchResult.Failures) { - warn << Resource::String::SearchFailureWarning(failure.SourceName) << std::endl; + warn << Resource::String::SearchFailureWarning(Utility::LocIndView{ failure.SourceName }) << std::endl; } } else @@ -636,7 +639,7 @@ namespace AppInstaller::CLI::Workflow auto error = context.Reporter.Error(); for (const auto& failure : searchResult.Failures) { - error << Resource::String::SearchFailureError(failure.SourceName) << std::endl; + error << Resource::String::SearchFailureError(Utility::LocIndView{ failure.SourceName }) << std::endl; HRESULT failureHR = HandleException(context, failure.Exception); // Just take first failure for now @@ -909,7 +912,7 @@ namespace AppInstaller::CLI::Workflow if (!manifest) { auto versionInfo = Utility::Format(m_channel.empty() ? "{0}{1}" : "{0}[{1}]", m_version, m_channel); - context.Reporter.Error() << Resource::String::GetManifestResultVersionNotFound(versionInfo) << std::endl; + context.Reporter.Error() << Resource::String::GetManifestResultVersionNotFound(Utility::LocIndView{ versionInfo }) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } @@ -937,13 +940,13 @@ namespace AppInstaller::CLI::Workflow if (!std::filesystem::exists(path)) { - context.Reporter.Error() << Resource::String::VerifyFileFailedNotExist(path.u8string()) << std::endl; + context.Reporter.Error() << Resource::String::VerifyFileFailedNotExist(Utility::LocIndView{ path.u8string() }) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } if (std::filesystem::is_directory(path)) { - context.Reporter.Error() << Resource::String::VerifyFileFailedIsDirectory(path.u8string()) << std::endl; + context.Reporter.Error() << Resource::String::VerifyFileFailedIsDirectory(Utility::LocIndView{ path.u8string() }) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_DIRECTORY_NOT_SUPPORTED)); } } @@ -954,7 +957,7 @@ namespace AppInstaller::CLI::Workflow if (!std::filesystem::exists(path)) { - context.Reporter.Error() << Resource::String::VerifyPathFailedNotExist(path.u8string()) << std::endl; + context.Reporter.Error() << Resource::String::VerifyPathFailedNotExist(Utility::LocIndView{ path.u8string() }) << std::endl; AICLI_TERMINATE_CONTEXT(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)); } } @@ -1087,7 +1090,9 @@ namespace AppInstaller::CLI::Workflow { if (!Settings::ExperimentalFeature::IsEnabled(m_feature)) { - context.Reporter.Error() << Resource::String::FeatureDisabledMessage(Settings::ExperimentalFeature::GetFeature(m_feature).JsonName()) << std::endl; + context.Reporter.Error() + << Resource::String::FeatureDisabledMessage(Utility::LocIndView{ Settings::ExperimentalFeature::GetFeature(m_feature).JsonName() }) + << std::endl; AICLI_LOG(CLI, Error, << Settings::ExperimentalFeature::GetFeature(m_feature).Name() << " feature is disabled. Execution cancelled."); AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_EXPERIMENTAL_FEATURE_DISABLED); } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 620861b5e2..016cf031c0 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -974,7 +974,7 @@ Configuration is disabled due to Group Policy. Dependency graph has loop - No suitable installer found for manifest: {0} + No suitable installer found for manifest: {0}{1} Attempt to get preferred installer for manifest failed. diff --git a/src/AppInstallerCLITests/Command.cpp b/src/AppInstallerCLITests/Command.cpp index fd27e1e98c..3d1cfa4f2e 100644 --- a/src/AppInstallerCLITests/Command.cpp +++ b/src/AppInstallerCLITests/Command.cpp @@ -287,7 +287,7 @@ TEST_CASE("ParseArguments_TooManyPositional", "[command]") std::vector values{ "val1", "--", "-std1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::ExtraPositionalError(values[2])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::ExtraPositionalError(Utility::LocIndView{ values[2] })); } TEST_CASE("ParseArguments_InvalidChar", "[command]") @@ -302,7 +302,7 @@ TEST_CASE("ParseArguments_InvalidChar", "[command]") std::vector values{ "val1", "-", "-std1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidArgumentSpecifierError(values[1])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidArgumentSpecifierError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_InvalidAlias", "[command]") @@ -317,7 +317,7 @@ TEST_CASE("ParseArguments_InvalidAlias", "[command]") std::vector values{ "val1", "-b", "-std1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidAliasError(values[1])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidAliasError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_MultiFlag", "[command]") @@ -352,7 +352,7 @@ TEST_CASE("ParseArguments_FlagThenUnknown", "[command]") std::vector values{ "val1", "-sr", "val2" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(values[1])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_FlagThenNonFlag", "[command]") @@ -368,7 +368,7 @@ TEST_CASE("ParseArguments_FlagThenNonFlag", "[command]") std::vector values{ "val1", "-sp", "val2" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFlagError(values[1])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFlagError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_NameUsingAliasSpecifier", "[command]") @@ -384,7 +384,7 @@ TEST_CASE("ParseArguments_NameUsingAliasSpecifier", "[command]") std::vector values{ "another", "-flag1" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(values[1])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::AdjoinedNotFoundError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_AliasWithAdjoinedValue", "[command]") @@ -433,7 +433,7 @@ TEST_CASE("ParseArguments_AliasWithSeparatedValueMissing", "[command]") std::vector values{ "-s" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(values[0])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(Utility::LocIndView{ values[0] })); } TEST_CASE("ParseArguments_NameWithAdjoinedValue", "[command]") @@ -502,7 +502,7 @@ TEST_CASE("ParseArguments_NameFlagWithAdjoinedValue", "[command]") std::vector values{ "another", "--flag1=arbitrary" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::FlagContainAdjoinedError(values[1])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::FlagContainAdjoinedError(Utility::LocIndView{ values[1] })); } TEST_CASE("ParseArguments_NameWithSeparatedValue", "[command]") @@ -536,7 +536,7 @@ TEST_CASE("ParseArguments_NameWithSeparatedValueMissing", "[command]") std::vector values{ "--pos2" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(values[0])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::MissingArgumentError(Utility::LocIndView{ values[0] })); } TEST_CASE("ParseArguments_UnknownName", "[command]") @@ -552,5 +552,5 @@ TEST_CASE("ParseArguments_UnknownName", "[command]") std::vector values{ "another", "--nope" }; Invocation inv{ std::vector(values) }; - REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidNameError(values[1])); + REQUIRE_COMMAND_EXCEPTION(command.ParseArguments(inv, args), CLI::Resource::String::InvalidNameError(Utility::LocIndView{ values[1] })); } diff --git a/src/AppInstallerCLITests/Completion.cpp b/src/AppInstallerCLITests/Completion.cpp index 2af3d1d223..e80ae27683 100644 --- a/src/AppInstallerCLITests/Completion.cpp +++ b/src/AppInstallerCLITests/Completion.cpp @@ -233,7 +233,7 @@ struct CompletionTestCommand : public Command using Command::Complete; - void Complete(Execution::Context& context, Execution::Args::Type valueType) const override + void Complete(CLI::Execution::Context& context, CLI::Execution::Args::Type valueType) const override { if (ArgumentValueCallback) { @@ -243,7 +243,7 @@ struct CompletionTestCommand : public Command std::vector SubCommandNames; std::vector Arguments; - std::function ArgumentValueCallback; + std::function ArgumentValueCallback; }; struct CompletionTestContext @@ -251,7 +251,7 @@ struct CompletionTestContext CompletionTestContext(std::string_view word, std::string_view commandLine, std::string_view position) : context(out, in) { - context.Reporter.SetChannel(Execution::Reporter::Channel::Completion); + context.Reporter.SetChannel(CLI::Execution::Reporter::Channel::Completion); context.Add(CompletionData{ word, commandLine, position }); } @@ -267,7 +267,7 @@ TEST_CASE("CommandComplete_Simple", "[complete]") CompletionTestCommand command; command.SubCommandNames = { "test1", "test2" }; command.Arguments = { Argument{ "arg1", 'a', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard } }; - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -284,7 +284,7 @@ TEST_CASE("CommandComplete_PartialCommandMatch", "[complete]") CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -301,7 +301,7 @@ TEST_CASE("CommandComplete_CommandsNotAllowed", "[complete]") CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -322,7 +322,7 @@ TEST_CASE("CommandComplete_Routing1", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -343,7 +343,7 @@ TEST_CASE("CommandComplete_Routing2", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -364,7 +364,7 @@ TEST_CASE("CommandComplete_PositionalRouting", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -387,7 +387,7 @@ TEST_CASE("CommandComplete_PositionalRoutingAfterArgs", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -409,7 +409,7 @@ TEST_CASE("CommandComplete_PositionalRoutingAfterDoubleDash", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -429,7 +429,7 @@ TEST_CASE("CommandComplete_ArgNamesAfterDash", "[complete]") Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -449,7 +449,7 @@ TEST_CASE("CommandComplete_AliasNames", "[complete]") Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -470,7 +470,7 @@ TEST_CASE("CommandComplete_ArgNamesFilter", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, Argument{ "foo1", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -491,7 +491,7 @@ TEST_CASE("CommandComplete_IgnoreBadArgs", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -511,7 +511,7 @@ TEST_CASE("CommandComplete_OtherArgsParsed", "[complete]") Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -536,7 +536,7 @@ TEST_CASE("CommandComplete_Complex", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values diff --git a/src/AppInstallerCLITests/Dependencies.cpp b/src/AppInstallerCLITests/Dependencies.cpp index de02bf8836..7874734b82 100644 --- a/src/AppInstallerCLITests/Dependencies.cpp +++ b/src/AppInstallerCLITests/Dependencies.cpp @@ -23,6 +23,7 @@ using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; +using namespace AppInstaller::Utility::literals; TEST_CASE("DependencyGraph_BFirst", "[dependencyGraph][dependencies]") { @@ -161,7 +162,7 @@ TEST_CASE("DependencyNodeProcessor_NoInstallers", "[dependencies]") Dependency rootAsDependency(DependencyType::Package, manifest.Id); DependencyNodeProcessorResult result = nodeProcessor.EvaluateDependencies(rootAsDependency); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowNoInstallerFound("withoutInstallers"))) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::DependenciesFlowNoInstallerFound("withoutInstallers"_liv))) != std::string::npos); REQUIRE(result == DependencyNodeProcessorResult::Error); } diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index 6193f0a6ea..5669cac348 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -52,8 +52,7 @@ using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; -using namespace AppInstaller::Settings; - +using namespace AppInstaller::Utility::literals; #define REQUIRE_TERMINATED_WITH(_context_,_hr_) \ REQUIRE(_context_.IsTerminated()); \ @@ -1065,7 +1064,7 @@ TEST_CASE("InstallFlow_Zip_BadRelativePath", "[InstallFlow][workflow]") // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NestedInstallerNotFound("")).get()) != std::string::npos); + REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NestedInstallerNotFound(""_liv)).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_MissingNestedInstaller", "[InstallFlow][workflow]") @@ -2089,7 +2088,7 @@ TEST_CASE("UpdateFlow_UpdateExeSpecificVersionNotFound", "[UpdateFlow][workflow] // Verify Installer is not called. REQUIRE(!std::filesystem::exists(updateResultPath.GetPath())); - REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::GetManifestResultVersionNotFound("1.2.3.4")).get()) != std::string::npos); + REQUIRE(updateOutput.str().find(Resource::LocString(Resource::String::GetManifestResultVersionNotFound("1.2.3.4"_liv)).get()) != std::string::npos); REQUIRE(context.GetTerminationHR() == APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } @@ -2613,7 +2612,7 @@ TEST_CASE("ImportFlow_PackageAlreadyInstalled", "[ImportFlow][workflow]") // Exe should not have been installed again REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportPackageAlreadyInstalled("AppInstallerCliTest.TestExeInstaller")).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportPackageAlreadyInstalled("AppInstallerCliTest.TestExeInstaller"_liv)).get()) != std::string::npos); } TEST_CASE("ImportFlow_IgnoreVersions", "[ImportFlow][workflow]") @@ -2651,7 +2650,7 @@ TEST_CASE("ImportFlow_MissingSource", "[ImportFlow][workflow]") // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSourceNotInstalled("TestSource")).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSourceNotInstalled("TestSource"_liv)).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } @@ -2671,7 +2670,7 @@ TEST_CASE("ImportFlow_MissingPackage", "[ImportFlow][workflow]") // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("MissingPackage")).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("MissingPackage"_liv)).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND); } @@ -2693,7 +2692,7 @@ TEST_CASE("ImportFlow_IgnoreMissingPackage", "[ImportFlow][workflow]") // Verify installer was called for the package that was available. REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("MissingPackage")).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("MissingPackage"_liv)).get()) != std::string::npos); } TEST_CASE("ImportFlow_MissingVersion", "[ImportFlow][workflow]") @@ -2712,7 +2711,7 @@ TEST_CASE("ImportFlow_MissingVersion", "[ImportFlow][workflow]") // Installer should not be called REQUIRE(!std::filesystem::exists(exeInstallResultPath.GetPath())); - REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("AppInstallerCliTest.TestExeInstaller")).get()) != std::string::npos); + REQUIRE(importOutput.str().find(Resource::LocString(Resource::String::ImportSearchFailed("AppInstallerCliTest.TestExeInstaller"_liv)).get()) != std::string::npos); REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NOT_ALL_PACKAGES_FOUND); } diff --git a/src/AppInstallerCommonCore/AppInstallerStrings.cpp b/src/AppInstallerCommonCore/AppInstallerStrings.cpp index c8b4193499..f649f0dd39 100644 --- a/src/AppInstallerCommonCore/AppInstallerStrings.cpp +++ b/src/AppInstallerCommonCore/AppInstallerStrings.cpp @@ -705,4 +705,21 @@ namespace AppInstaller::Utility return result; } + + LocIndString Join(LocIndView separator, const std::vector& vector) + { + auto vectorSize = vector.size(); + if (vectorSize == 0) + { + return {}; + } + + std::ostringstream ssJoin; + ssJoin << vector[0]; + for (int i = 1; i < vectorSize; ++i) + { + ssJoin << separator << vector[i]; + } + return LocIndString{ ssJoin.str() }; + } } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index a56c3d6b37..e90bdeab41 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace AppInstaller::Utility { @@ -228,24 +229,8 @@ namespace AppInstaller::Utility // Converts the given hexadecimal string into bytes. std::vector ParseFromHexString(const std::string& value, size_t byteCount = 0); - // Join vector values using the provided separator. - template - std::string Join(std::string_view separator, const std::vector& vector) - { - auto vectorSize = vector.size(); - if (vectorSize == 0) - { - return {}; - } - - std::ostringstream ssJoin; - ssJoin << vector[0]; - for (int i = 1; i < vectorSize; ++i) - { - ssJoin << separator << vector[i]; - } - return ssJoin.str(); - } + // Join a string vector using the provided separator. + LocIndString Join(LocIndView separator, const std::vector& vector); // Superset of std::to_string supporting string convertibles as input. template diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index ce5b6aa20f..7145552e14 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -24,10 +24,7 @@ namespace AppInstaller explicit constexpr StringId(std::wstring_view id) : std::wstring_view(id) {} template - Utility::LocIndString operator()(T ... args) const - { - return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; - } + Utility::LocIndString operator()(T ... args) const; private: std::string Resolve() const; }; @@ -75,5 +72,61 @@ namespace AppInstaller private: std::string m_message; }; + + // A localized string + struct LocString : public Utility::LocIndString + { + LocString() = default; + + LocString(StringResource::StringId id) : Utility::LocIndString(id()) {} + LocString(Utility::LocIndString locIndString) : Utility::LocIndString(std::move(locIndString)) {} + + LocString(const LocString&) = default; + LocString& operator=(const LocString&) = default; + + LocString(LocString&&) = default; + LocString& operator=(LocString&&) = default; + }; + } + + namespace Execution::details + { + // List of approved types for output, others are potentially not localized. + template + struct IsApprovedForOutput : std::false_type {}; + +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) template <> struct IsApprovedForOutput<_t_> : std::true_type {}; +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE(_t_) " '" #_t_ "'" +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(F) \ + /* It is assumed that single char values need not be localized, as they are matched + ordinally or they are punctuation / other. */ \ + F(char) \ + /* Localized strings (and from an Id for one for convenience).*/ \ + F(AppInstaller::StringResource::StringId) \ + F(AppInstaller::Resource::LocString) \ + /* Strings explicitly declared as localization independent.*/ \ + F(AppInstaller::Utility::LocIndView) \ + F(AppInstaller::Utility::LocIndString) \ + /* Normalized strings come from user dataand should therefore already by localized + by how they are chosen (or there is no localized version).*/ \ + F(AppInstaller::Utility::NormalizedString) + +#define WINGET_ISAPPROVEDFOROUTPUT_ERROR \ + "Only the following types are approved for output:" \ + WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE) + + WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION) } + + template + Utility::LocIndString StringResource::StringId::operator()(T ... args) const + { + static_assert((Execution::details::IsApprovedForOutput::value && ...), WINGET_ISAPPROVEDFOROUTPUT_ERROR); + return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; + } +} + +inline std::ostream& operator<<(std::ostream& out, AppInstaller::StringResource::StringId si) +{ + return (out << AppInstaller::Resource::LocString{ si }); } From 6fc16bc1967bc679f88f93bc7397889660ad5c33 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Sat, 13 Aug 2022 10:54:29 -0700 Subject: [PATCH 20/52] Updated resw for WPM --- src/AppInstallerCLICore/Command.cpp | 3 +-- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index b11c9e15a2..b0ff3908df 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -38,8 +38,7 @@ namespace AppInstaller::CLI void Command::OutputIntroHeader(Execution::Reporter& reporter) const { auto productName = Runtime::IsReleaseBuild() ? Resource::String::WindowsPackageManager : Resource::String::WindowsPackageManagerPreview; - auto productVersion = Utility::Format("v{0}", Runtime::GetClientVersion()); - reporter.Info() << productName << " " << productVersion << std::endl << Resource::String::MainCopyrightNotice << std::endl; + reporter.Info() << productName(Runtime::GetClientVersion()) << std::endl << Resource::String::MainCopyrightNotice << std::endl; } void Command::OutputHelp(Execution::Reporter& reporter, const CommandException* exception) const diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 016cf031c0..51a14f3ca6 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1238,7 +1238,7 @@ Please specify one of them using the `--source` option to proceed. {Locked="https://github.com/microsoft/winget-cli"} - Windows Package Manager + Windows Package Manager v{0} The product name. @@ -1254,7 +1254,7 @@ Please specify one of them using the `--source` option to proceed. The install technology of the newer version specified is different from the current version installed. Please uninstall the package and install the newer version. - Windows Package Manager (Preview) + Windows Package Manager (Preview) v{0} The product name plus an indicator that this is a pre-release version. From 0eefc3ef7297b11b5fb9ed307b75bb9c2a3c3dc8 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Sat, 13 Aug 2022 16:45:00 -0700 Subject: [PATCH 21/52] Init resw comments --- src/AppInstallerCLICore/Command.cpp | 3 +- .../Workflows/DependencyNodeProcessor.cpp | 5 +- .../Workflows/PortableFlow.cpp | 4 +- .../Shared/Strings/en-us/winget.resw | 98 +++++++++++++++---- 4 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index b0ff3908df..ec43532c22 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -80,7 +80,8 @@ namespace AppInstaller::CLI } // Output the command preamble and command chain - infoOut << Resource::String::Usage("winget"_liv, Utility::LocIndView{ commandChain }); + auto commandUsage = Utility::LocIndString{ Utility::Format("{0} {1}", "winget", commandChain) }; + infoOut << Resource::String::Usage(commandUsage); auto commandAliases = Aliases(); auto commands = GetVisibleCommands(); diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp index ce30b73b07..d3241abd53 100644 --- a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp +++ b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp @@ -86,9 +86,8 @@ namespace AppInstaller::CLI::Workflow if (!installer.has_value()) { - auto manifestId = Utility::LocIndString{ Utility::Normalize(m_nodeManifest.Id) }; - auto manifestVersion = Utility::LocIndString{ m_nodeManifest.Version }; - error << Resource::String::DependenciesFlowNoSuitableInstallerFound(manifestId, manifestVersion); + auto manifestInfo = Utility::LocIndString{ Utility::Format("{0}{1}", Utility::Normalize(m_nodeManifest.Id), m_nodeManifest.Version)}; + error << Resource::String::DependenciesFlowNoSuitableInstallerFound(manifestInfo); AICLI_LOG(CLI, Error, << "No suitable installer found for manifest " << m_nodeManifest.Id << " with version " << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } diff --git a/src/AppInstallerCLICore/Workflows/PortableFlow.cpp b/src/AppInstallerCLICore/Workflows/PortableFlow.cpp index 71d51f1a97..40e6d6ea36 100644 --- a/src/AppInstallerCLICore/Workflows/PortableFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/PortableFlow.cpp @@ -403,7 +403,9 @@ namespace AppInstaller::CLI::Workflow } else { - context.Reporter.Warn() << Resource::String::FilesRemainInInstallDirectory << installDirectoryValue << std::endl; + context.Reporter.Warn() + << Resource::String::FilesRemainInInstallDirectory(Utility::LocIndView{ installDirectoryValue.u8string() }) + << std::endl; } } else diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 51a14f3ca6..07c91485dc 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -119,43 +119,51 @@ Adjoined alias is not a flag: '{0}' + Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Adjoined flag alias not found: '{0}' + Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). The following arguments are available: + <Message displayed to infom the user about the available command line arguments. The following command aliases are available: + Message displayed to inform the user about the available command line alias arguments. The following commands are available: - Commands the tool supports + Message displayed to inform the user about the avialable command line command line command arguments. Available As in "a new version is available to update to". + Label displayed when a new application package is available. The following options are available: + Message displayed to inform the user about the available options. The following sub-commands are available: - Nested commands that can be run in context of the selected command + Message displayed to inform the user about the available nested command line command arguments that runs in context of the selected command. upgrades available. + Message displayed to inform the user about available application package upgrades. Use the specified channel; default is general audience command - A command to give the software + Label displayed for a command to give the software. Filter results by command + Description message displayed to inform the user about filtering the search reuslts by a command line command. The full command line for completion @@ -174,9 +182,11 @@ Done + Label displayed when an operation completes or is done executing. Find package using exact match + Description message displayed to inform the user about finding an application package using an exact matching criteria. Experimental argument for demonstration purposes @@ -190,10 +200,11 @@ Found a positional argument when none was expected: '{0}' + Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. This feature is a work in progress, and may be changed dramatically or removed altogether in the future. To enable it, edit your settings ('winget settings') to include the experimental feature: '{0}' - {Locked="winget settings"} + Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Shows the status of experimental features. Experimental features can be turn on via 'winget settings'. @@ -230,6 +241,7 @@ They can be configured through the settings file 'winget settings'. Flag argument cannot contain adjoined value: '{0}' + Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Computes the hash of a local file, appropriate for entry into a manifest. It can also compute the hash of the signature file of an MSIX package to enable streaming installations. @@ -245,6 +257,7 @@ They can be configured through the settings file 'winget settings'. More help can be found at: {0} + Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Filter results by id @@ -297,12 +310,15 @@ They can be configured through the settings file 'winget settings'. Argument alias was not recognized for the current command: '{0}' + Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Invalid argument specifier: '{0}' + Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). Argument name was not recognized for the current command: '{0}' + Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's arugment name input (e.g. '--example'). Locale to use (BCP47 format) @@ -349,6 +365,7 @@ They can be configured through the settings file 'winget settings'. Argument value required, but none found: '{0}' + Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Filter results by moniker @@ -364,6 +381,7 @@ They can be configured through the settings file 'winget settings'. Failed to install or update Microsoft Store package. Error code: {0} + Error message displayed when a Microsoft Store application package fails to install or update. {0} is a placeholder replaced by an error code. Verifying/Requesting package acquisition failed: network error @@ -417,7 +435,7 @@ They can be configured through the settings file 'winget settings'. Package: {0} - A software package + Label displayed for an application/software package. {0} is a placeholder replaced by an application package version. Oops, we forgot to do this... @@ -436,6 +454,7 @@ They can be configured through the settings file 'winget settings'. Required argument not provided: '{0}' + Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Progress display as the default color @@ -496,6 +515,7 @@ They can be configured through the settings file 'winget settings'. Only the single character alias can occur after a single -: '{0}' + Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. A source with the given name already exists and refers to a different location: @@ -555,6 +575,7 @@ They can be configured through the settings file 'winget settings'. Did not find a source named: {0} + Error message displayed when the user provides a repository source name that was not found. {0} is a placeholder replaced by the user input. There are no sources configured. @@ -596,7 +617,8 @@ They can be configured through the settings file 'winget settings'. Remove current sources - Removing source: {0} + Removing source: {0}... + Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Resetting all sources... @@ -615,7 +637,8 @@ They can be configured through the settings file 'winget settings'. {Locked="--force"} - Resetting source: {0} + Resetting source: {0}... + Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Type of the source @@ -630,7 +653,8 @@ They can be configured through the settings file 'winget settings'. Update current sources - Updating source: {0} + Updating source: {0}... + Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Filter results by tag @@ -652,15 +676,18 @@ They can be configured through the settings file 'winget settings'. Argument provided more times than allowed: '{0}' + Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. More than one execution behavior argument provided: '{0}' + Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). An unexpected error occurred while executing the command: Unrecognized command: '{0}' + Error message displayed when the user provides an unrecognized command line command. {0} is a placeholder replaced by the user input. Update all installed packages to latest if available @@ -676,8 +703,8 @@ They can be configured through the settings file 'winget settings'. Shows and performs available upgrades - usage: {0} {1} - The way to use the software + usage: {0} + Message displayed to provide the user with instructions on how to use a command line command. {0} is a placeholder replaced by the command usage provided by the program. Validates a manifest using a strict set of guidelines. This is intended to enable you to check your manifest before submitting to a repo. @@ -705,9 +732,11 @@ They can be configured through the settings file 'winget settings'. No version found matching: {0} + Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. No sources match the given value: {0} + Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. The configured sources are: @@ -721,16 +750,18 @@ They can be configured through the settings file 'winget settings'. Path is a directory: {0} + Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. File does not exist: {0} + Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Both local manifest and search query arguments are provided Logs: {0} - Diagnostic files containing information about application use. + Label displayed for a diagnostic files containing information about the application use. {0} is a placeholder replaced by the diagnostic file path. The installer is blocked by policy @@ -743,6 +774,7 @@ They can be configured through the settings file 'winget settings'. Failed in attempting to update the source: {0} + Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Uninstalls the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. @@ -766,6 +798,7 @@ They can be configured through the settings file 'winget settings'. Uninstall failed with exit code: {0} + Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Exports a list of the installed packages @@ -794,15 +827,19 @@ They can be configured through the settings file 'winget settings'. Package not found for import: {0} + Error message displayed when the user attempts to import an application package that was not found. {0} is a placeholder replaced by the import package name . Source required for import is not installed: {0} + Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Installed package is not available from any source: {0} + Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. Installed version of package is not available from any source: {0} + Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package information. No packages found in import file @@ -812,6 +849,7 @@ They can be configured through the settings file 'winget settings'. Package is already installed: {0} + Message displayed to inform the user that an import application package is already installed. {0} is a placeholder replaced by the package identifier. Ignore unavailable packages @@ -824,6 +862,7 @@ They can be configured through the settings file 'winget settings'. Path does not exist: {0} + Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. The JSON file does not specify a recognized schema. @@ -834,10 +873,11 @@ They can be configured through the settings file 'winget settings'. The value provided for the `{0}` argument is invalid; valid values are: {1} - {Locked="%1"} The value will be replaced with the argument name + Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. This operation is disabled by Group Policy: {0} + Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Enable Additional Windows App Installer Sources @@ -884,6 +924,7 @@ They can be configured through the settings file 'winget settings'. Field: {0} + Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Invalid field format. @@ -902,6 +943,7 @@ They can be configured through the settings file 'winget settings'. Value: {0} + Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. The following experimental features are in progress. @@ -929,7 +971,7 @@ Configuration is disabled due to Group Policy. The value provided for the `{0}` argument is invalid - {Locked="%1"} The value will be replaced with the argument name + Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Cancelled @@ -953,17 +995,19 @@ Configuration is disabled due to Group Policy. Package search yield more than one result: {0} - When node package id search yield too many matches + Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Latest version not found for package: {0} - When no suitable version found for the specific package. + Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. No installers found: {0} + Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Minimum required version not available for package: {0} + Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. No matches @@ -974,8 +1018,8 @@ Configuration is disabled due to Group Policy. Dependency graph has loop - No suitable installer found for manifest: {0}{1} - Attempt to get preferred installer for manifest failed. + No suitable installer found for manifest: {0} + Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest information. Error processing package dependencies, do you wish to continue installation? @@ -1013,6 +1057,7 @@ Configuration is disabled due to Group Policy. Exported package requires license agreement to install: {0} + Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. The publisher requires that you view the above information and accept the agreements before installing. @@ -1098,7 +1143,7 @@ Do you agree to the terms? The `{0}` source requires that you view the following agreements before using. - {Locked="%1"} The value will be replaced with the source name + Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Do you agree to all the source agreements terms? @@ -1120,6 +1165,7 @@ Do you agree to the terms? The optional header is not applicable without specifying a source: '{0}' + Error message displayed when the user performs an operation (e.g install) and provide the 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Release Date: @@ -1147,13 +1193,15 @@ Do you agree to the terms? Failed when searching source; results will not be included: {0} + Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Failed when searching source: {0} + Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. This feature needs to be enabled by administrators. To enable it, run 'winget settings --enable {0}' as administrator. - {Locked="winget settings --enable %1"} The value will be replaced with the admin setting + Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Enables the specific administrator setting @@ -1163,6 +1211,7 @@ Do you agree to the terms? Too many admin setting arguments provided: '{0}' + Error message displayed when the user provides too many admin setting arguments. {0} is a placeholder replaced by the admin setting arguments (e.g. 'enable|disable'). Admin setting enabled. @@ -1220,9 +1269,11 @@ Do you agree to the terms? Installer failed with exit code: {0} + Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. Installer log is available at: {0} + Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. The following packages were found among the working sources. @@ -1239,7 +1290,7 @@ Please specify one of them using the `--source` option to proceed. Windows Package Manager v{0} - The product name. + Label displaying the product name and version. {0} is a placeholder replaced by the product version. Ignore package versions in import file @@ -1255,7 +1306,7 @@ Please specify one of them using the `--source` option to proceed. Windows Package Manager (Preview) v{0} - The product name plus an indicator that this is a pre-release version. + Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Select the architecture to install @@ -1288,6 +1339,7 @@ Please specify one of them using the `--source` option to proceed. System Architecture: {0} + Label displayed for the system architecture. {0} is a placeholder replaced by the system architecture. Retains all files and directories created by the package (portable) @@ -1327,6 +1379,7 @@ Please specify one of them using the `--source` option to proceed. Overwriting existing file: {0} + Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. No package selection argument was provided; see the help for details about finding a package. @@ -1347,6 +1400,7 @@ Please specify one of them using the `--source` option to proceed. Files remain in install directory: {0} + Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Purging install directory... @@ -1362,6 +1416,7 @@ Please specify one of them using the `--source` option to proceed. Notes: {0} + Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Installation Notes: @@ -1380,6 +1435,7 @@ Please specify one of them using the `--source` option to proceed. Nested installer file does not exist. Ensure the specified relative path of the nested installer matches: {0} + Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Invalid relative file path to the nested installer; path points to a location outside of the install directory From cf5111dda5d5e3dd9727cc5252de10cc6b31f5f1 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Sat, 13 Aug 2022 16:47:06 -0700 Subject: [PATCH 22/52] resw typo --- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 07c91485dc..a8aeba4d65 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -135,7 +135,7 @@ The following commands are available: - Message displayed to inform the user about the avialable command line command line command arguments. + Message displayed to inform the user about the avialable command line command arguments. Available @@ -163,7 +163,7 @@ Filter results by command - Description message displayed to inform the user about filtering the search reuslts by a command line command. + Description message displayed to inform the user about filtering the search results by a command line command. The full command line for completion From 97ee31b116553e9990e31b998eec559fa2353aa6 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Sat, 13 Aug 2022 19:14:05 -0700 Subject: [PATCH 23/52] Fixed resw syntax + added downloading --- src/AppInstallerCLICore/Resources.h | 2 +- src/AppInstallerCLICore/Workflows/DownloadFlow.cpp | 2 +- .../Shared/Strings/en-us/winget.resw | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index d34cd2f728..b898b73b53 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -60,6 +60,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(CountOutOfBoundsError); WINGET_DEFINE_RESOURCE_STRINGID(DisabledByGroupPolicy); WINGET_DEFINE_RESOURCE_STRINGID(Done); + WINGET_DEFINE_RESOURCE_STRINGID(Downloading); WINGET_DEFINE_RESOURCE_STRINGID(ExactArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExperimentalArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ExperimentalCommandLongDescription); @@ -117,7 +118,6 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer1); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimer2); WINGET_DEFINE_RESOURCE_STRINGID(InstallationDisclaimerMSStore); - WINGET_DEFINE_RESOURCE_STRINGID(InstallationRequiresHigherWindows); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstallCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(InstalledPackageNotAvailable); diff --git a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp index ccd5fc8370..b123c357dc 100644 --- a/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/DownloadFlow.cpp @@ -235,7 +235,7 @@ namespace AppInstaller::CLI::Workflow // Use the SHA256 hash of the installer as the identifier for the download downloadInfo.ContentId = SHA256::ConvertToString(installer.Sha256); - context.Reporter.Info() << "Downloading " << Execution::UrlEmphasis << installer.Url << std::endl; + context.Reporter.Info() << Resource::String::Downloading << ' ' << Execution::UrlEmphasis << installer.Url << std::endl; std::optional> hash; diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index a8aeba4d65..795cf8ec41 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -127,7 +127,7 @@ The following arguments are available: - <Message displayed to infom the user about the available command line arguments. + Message displayed to infom the user about the available command line arguments. The following command aliases are available: @@ -272,9 +272,6 @@ They can be configured through the settings file 'winget settings'. This package is provided through Microsoft Store. winget may need to acquire the package from Microsoft Store on behalf of the current user. {Locked="winget"} - - Cannot install package, as it requires a higher version of Windows: - Installs the selected package, either found by searching a configured source or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. id, name, and moniker are all named values in our context, and may benefit from not being translated. The match must be for any of them, with comparison ignoring case. @@ -1453,4 +1450,8 @@ Please specify one of them using the `--source` option to proceed. The following packages have an upgrade available, but require explicit targeting for upgrade: "require explicit targeting for upgrade" means that the package will not be upgraded with all others unless an extra flag is added, or the package is mentioned explicitly + + Downloading + Label displayed while downloading an application installer. + \ No newline at end of file From ca67eda50d3ac2871563cf76e9a1a8990a61515d Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Sat, 13 Aug 2022 21:22:53 -0700 Subject: [PATCH 24/52] Added Test --- src/AppInstallerCLICore/ChannelStreams.h | 1 + .../AppInstallerCLITests.vcxproj | 1 + .../AppInstallerCLITests.vcxproj.filters | 3 ++ src/AppInstallerCLITests/Resources.cpp | 39 +++++++++++++++++ src/AppInstallerCLITests/Strings.cpp | 24 ++++++++++- .../AppInstallerCommonCore.vcxproj | 1 + .../AppInstallerCommonCore.vcxproj.filters | 3 ++ .../Public/winget/ChannelStreams.h | 42 +++++++++++++++++++ .../Public/winget/Resources.h | 36 ---------------- 9 files changed, 113 insertions(+), 37 deletions(-) create mode 100644 src/AppInstallerCLITests/Resources.cpp create mode 100644 src/AppInstallerCommonCore/Public/winget/ChannelStreams.h diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index 7c0bbc7493..47271c608c 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -4,6 +4,7 @@ #include "Resources.h" #include "VTSupport.h" #include +#include #include #include diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index be21310a47..cc9267d7ca 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -216,6 +216,7 @@ + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 6dd121187b..927b266097 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -224,6 +224,9 @@ Source Files\Common + + Source Files\Common + diff --git a/src/AppInstallerCLITests/Resources.cpp b/src/AppInstallerCLITests/Resources.cpp new file mode 100644 index 0000000000..1d2499f284 --- /dev/null +++ b/src/AppInstallerCLITests/Resources.cpp @@ -0,0 +1,39 @@ + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestCommon.h" +#include +#include +#include + +using namespace std::string_view_literals; +using namespace AppInstaller::CLI::Resource; +using namespace AppInstaller::Utility; +using namespace AppInstaller::Utility::literals; +using namespace AppInstaller::CLI::Execution; + +#define WINGET_TEST_OUTPUT_STREAM(_expected_, _input_) \ + std::istringstream iInput; \ + std::ostringstream oInput; \ + std::istringstream iExpected; \ + std::ostringstream oExpected; \ + Reporter(oInput, iInput).Info() << _input_; \ + Reporter(oExpected, iExpected).Info() << _expected_; \ + REQUIRE(oExpected.str()== oInput.str()); + +TEST_CASE("Resources_StringId", "[resources]") +{ + WINGET_TEST_OUTPUT_STREAM( + "Filter results by command"_liv, + String::CommandArgumentDescription + ); +} + +TEST_CASE("Resources_StringIdWithPlaceholders", "[resources]") +{ + WINGET_TEST_OUTPUT_STREAM( + "The value provided for the `First` argument is invalid; valid values are: Second"_liv , + String::InvalidArgumentValueError("First"_liv, "Second"_liv) + ); +} diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp index b234e6c2ee..a4ba08352c 100644 --- a/src/AppInstallerCLITests/Strings.cpp +++ b/src/AppInstallerCLITests/Strings.cpp @@ -8,7 +8,7 @@ using namespace std::string_view_literals; using namespace AppInstaller::Utility; - +using namespace AppInstaller::Utility::literals; TEST_CASE("UTF8Length", "[strings]") { @@ -225,3 +225,25 @@ TEST_CASE("HexStrings", "[strings]") REQUIRE(value == ConvertToHexString(buffer)); REQUIRE(std::equal(buffer.begin(), buffer.end(), ParseFromHexString(value).begin())); } + +TEST_CASE("Join", "[strings]") +{ + std::vector list_0{ }; + std::vector list_1{ "A"_lis }; + std::vector list_2{ "A"_lis, "B"_lis }; + + REQUIRE(""_lis == Join(", "_liv, list_0)); + REQUIRE("A"_lis == Join(", "_liv, list_1)); + REQUIRE("A, B"_lis == Join(", "_liv, list_2)); + REQUIRE("AB"_lis == Join(""_liv, list_2)); +} + +TEST_CASE("Format", "[strings]") +{ + REQUIRE("Hello World" == Format("{0} {1}", "Hello", "World")); + REQUIRE("Hello World" == Format("{1} {0}", "World", "Hello")); + REQUIRE("Hello World" == Format("{0} {1}", "Hello", "World", "(Extra", "Input", "Ignored)")); + + // Note: C++20 std::format will throw an exception for this test case + REQUIRE("Hello {1}" == Format("{0} {1}", "Hello")); +} diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index 56fb145d8b..6ef813e15e 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -308,6 +308,7 @@ + diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters index 860a24bc2e..9767c055ba 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -213,6 +213,9 @@ Header Files + + Public\winget + diff --git a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h new file mode 100644 index 0000000000..67fde15ba7 --- /dev/null +++ b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h @@ -0,0 +1,42 @@ +#pragma once +#include "Resources.h" + +namespace AppInstaller +{ + namespace Execution::details + { + // List of approved types for output, others are potentially not localized. + template + struct IsApprovedForOutput : std::false_type {}; + +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) template <> struct IsApprovedForOutput<_t_> : std::true_type {}; +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE(_t_) " '" #_t_ "'" + +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(F) \ + /* It is assumed that single char values need not be localized, as they are matched + ordinally or they are punctuation / other. */ \ + F(char) \ + /* Localized strings (and from an Id for one for convenience).*/ \ + F(AppInstaller::StringResource::StringId) \ + F(AppInstaller::Resource::LocString) \ + /* Strings explicitly declared as localization independent.*/ \ + F(AppInstaller::Utility::LocIndView) \ + F(AppInstaller::Utility::LocIndString) \ + /* Normalized strings come from user dataand should therefore already by localized + by how they are chosen (or there is no localized version).*/ \ + F(AppInstaller::Utility::NormalizedString) + +#define WINGET_ISAPPROVEDFOROUTPUT_ERROR \ + "Only the following types are approved for output:" \ + WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE) + + WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION) + } + + template + Utility::LocIndString StringResource::StringId::operator()(T ... args) const + { + static_assert((Execution::details::IsApprovedForOutput::value && ...), WINGET_ISAPPROVEDFOROUTPUT_ERROR); + return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; + } +} diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 7145552e14..1ce9d4a40e 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -88,42 +88,6 @@ namespace AppInstaller LocString& operator=(LocString&&) = default; }; } - - namespace Execution::details - { - // List of approved types for output, others are potentially not localized. - template - struct IsApprovedForOutput : std::false_type {}; - -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) template <> struct IsApprovedForOutput<_t_> : std::true_type {}; -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE(_t_) " '" #_t_ "'" -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(F) \ - /* It is assumed that single char values need not be localized, as they are matched - ordinally or they are punctuation / other. */ \ - F(char) \ - /* Localized strings (and from an Id for one for convenience).*/ \ - F(AppInstaller::StringResource::StringId) \ - F(AppInstaller::Resource::LocString) \ - /* Strings explicitly declared as localization independent.*/ \ - F(AppInstaller::Utility::LocIndView) \ - F(AppInstaller::Utility::LocIndString) \ - /* Normalized strings come from user dataand should therefore already by localized - by how they are chosen (or there is no localized version).*/ \ - F(AppInstaller::Utility::NormalizedString) - -#define WINGET_ISAPPROVEDFOROUTPUT_ERROR \ - "Only the following types are approved for output:" \ - WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE) - - WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION) - } - - template - Utility::LocIndString StringResource::StringId::operator()(T ... args) const - { - static_assert((Execution::details::IsApprovedForOutput::value && ...), WINGET_ISAPPROVEDFOROUTPUT_ERROR); - return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; - } } inline std::ostream& operator<<(std::ostream& out, AppInstaller::StringResource::StringId si) From 1428e1218e26ebeaa2d3f3a5dc8e2a83903224b4 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Mon, 15 Aug 2022 09:11:13 -0700 Subject: [PATCH 25/52] Revert /MP --- src/AppInstallerCLICore/AppInstallerCLICore.vcxproj | 1 - src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj | 1 - .../AppInstallerRepositoryCore.vcxproj | 1 - 3 files changed, 3 deletions(-) diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj index 8ee677f401..f09cd3c3ab 100644 --- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj +++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj @@ -174,7 +174,6 @@ true true true - true false diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index 6ef813e15e..e24311b5f9 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -190,7 +190,6 @@ true true true - true false diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index bf28f0e43f..6259cbcfa0 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -173,7 +173,6 @@ true true true - true false From 54eff4f398377ca81f879d12b0dfa39d0a44f931 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Mon, 15 Aug 2022 10:34:57 -0700 Subject: [PATCH 26/52] Simplified loc strings --- src/AppInstallerCLICore/Command.cpp | 20 +++++++++---------- .../Commands/SettingsCommand.cpp | 5 +++-- src/AppInstallerCLICore/Core.cpp | 3 ++- .../Workflows/InstallFlow.cpp | 2 +- .../Workflows/UninstallFlow.cpp | 2 +- .../Workflows/WorkflowBase.cpp | 3 ++- .../Public/AppInstallerStrings.h | 6 +++--- 7 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index ec43532c22..22cad72c2c 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -80,7 +80,7 @@ namespace AppInstaller::CLI } // Output the command preamble and command chain - auto commandUsage = Utility::LocIndString{ Utility::Format("{0} {1}", "winget", commandChain) }; + auto commandUsage = Utility::LocIndString{ Utility::Format("{0} {1}", "winget"_lis, commandChain) }; infoOut << Resource::String::Usage(commandUsage); auto commandAliases = Aliases(); @@ -438,7 +438,7 @@ namespace AppInstaller::CLI // 4. If the argument is only a double --, all further arguments are only considered as positional. ParseArgumentsStateMachine::State ParseArgumentsStateMachine::StepInternal() { - std::string_view currArg = *m_invocationItr; + auto currArg = Utility::LocIndView{ *m_invocationItr }; ++m_invocationItr; // If the previous step indicated a value was needed, set it and forget it. @@ -454,7 +454,7 @@ namespace AppInstaller::CLI const CLI::Argument* nextPositional = NextPositional(); if (!nextPositional) { - return CommandException(Resource::String::ExtraPositionalError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::ExtraPositionalError(currArg)); } m_executionArgs.AddArg(nextPositional->ExecArgType(), currArg); @@ -462,7 +462,7 @@ namespace AppInstaller::CLI // The currentArg must not be empty, and starts with a - else if (currArg.length() == 1) { - return CommandException(Resource::String::InvalidArgumentSpecifierError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::InvalidArgumentSpecifierError(currArg)); } // Now it must be at least 2 chars else if (currArg[1] != APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR) @@ -473,7 +473,7 @@ namespace AppInstaller::CLI auto itr = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr == m_arguments.end()) { - return CommandException(Resource::String::InvalidAliasError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::InvalidAliasError(currArg)); } if (itr->Type() == ArgumentType::Flag) @@ -487,11 +487,11 @@ namespace AppInstaller::CLI auto itr2 = std::find_if(m_arguments.begin(), m_arguments.end(), [&](const Argument& arg) { return (currChar == arg.Alias()); }); if (itr2 == m_arguments.end()) { - return CommandException(Resource::String::AdjoinedNotFoundError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::AdjoinedNotFoundError(currArg)); } else if (itr2->Type() != ArgumentType::Flag) { - return CommandException(Resource::String::AdjoinedNotFlagError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::AdjoinedNotFlagError(currArg)); } else { @@ -507,7 +507,7 @@ namespace AppInstaller::CLI } else { - return CommandException(Resource::String::SingleCharAfterDashError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::SingleCharAfterDashError(currArg)); } } else @@ -550,7 +550,7 @@ namespace AppInstaller::CLI { if (hasValue) { - return CommandException(Resource::String::FlagContainAdjoinedError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::FlagContainAdjoinedError(currArg)); } m_executionArgs.AddArg(arg.ExecArgType()); @@ -570,7 +570,7 @@ namespace AppInstaller::CLI if (!argFound) { - return CommandException(Resource::String::InvalidNameError(Utility::LocIndView{ currArg })); + return CommandException(Resource::String::InvalidNameError(currArg)); } } diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index 81d35fc90f..af410c2dc4 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -16,6 +16,7 @@ namespace AppInstaller::CLI constexpr Utility::LocIndView s_ArgumentName_Enable = "enable"_liv; constexpr Utility::LocIndView s_ArgumentName_Disable = "disable"_liv; constexpr Utility::LocIndView s_ArgName_EnableAndDisable = "enable|disable"_liv; + constexpr Utility::LocIndView s_ArgValue_EnableAndDisable_LocalManifestFiles = "LocalManifestFiles"_liv; } std::vector SettingsCommand::GetArguments() const @@ -50,12 +51,12 @@ namespace AppInstaller::CLI if (execArgs.Contains(Execution::Args::Type::AdminSettingEnable) && AdminSetting::Unknown == StringToAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingEnable))) { - throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Enable, "LocalManifestFiles"_lis)); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Enable, s_ArgValue_EnableAndDisable_LocalManifestFiles)); } if (execArgs.Contains(Execution::Args::Type::AdminSettingDisable) && AdminSetting::Unknown == StringToAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingDisable))) { - throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Disable, "LocalManifestFiles"_lis)); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Disable, s_ArgValue_EnableAndDisable_LocalManifestFiles)); } } diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index e068aacc7f..7a08639236 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -135,8 +135,9 @@ namespace AppInstaller::CLI { // Report any action blocked by Group Policy. auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); + auto policyNameId = policy.PolicyName(); AICLI_LOG(CLI, Error, << "Operation blocked by Group Policy: " << policy.RegValueName()); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()()) << std::endl; + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policyNameId()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index f4fbfd1361..1273037844 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -479,7 +479,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ std::to_string(installResult) }) + << Resource::String::InstallerFailedWithCode(Utility::ToString(installResult)) << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 4e4c4e062f..d52cbc4e28 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -259,7 +259,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ std::to_string(uninstallResult) }) + << Resource::String::UninstallFailedWithCode(Utility::ToString(uninstallResult)) << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 07c6f4f0ec..22b32c1385 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -309,7 +309,8 @@ namespace AppInstaller::CLI::Workflow catch (const Settings::GroupPolicyException& e) { auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()()) << std::endl; + auto policyNameId = policy.PolicyName(); + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policyNameId()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } catch (const Resource::ResourceOpenException& e) diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index e90bdeab41..e763f914b8 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -234,15 +234,15 @@ namespace AppInstaller::Utility // Superset of std::to_string supporting string convertibles as input. template - std::string ToString(T value) + Utility::LocIndString ToString(T value) { if constexpr (std::is_convertible_v || std::is_convertible_v) { - return std::string(value); + return LocIndString{ value }; } else { - return std::to_string(value); + return LocIndString{ std::to_string(value) }; } } From 19870fea041ea9205d863f064190d155be977335 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Mon, 15 Aug 2022 11:01:04 -0700 Subject: [PATCH 27/52] Updated common channel stream code --- .../Public/winget/ChannelStreams.h | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h index 67fde15ba7..6305c47353 100644 --- a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h +++ b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h @@ -1,42 +1,41 @@ #pragma once #include "Resources.h" -namespace AppInstaller -{ - namespace Execution::details - { - // List of approved types for output, others are potentially not localized. - template - struct IsApprovedForOutput : std::false_type {}; - #define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) template <> struct IsApprovedForOutput<_t_> : std::true_type {}; -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE(_t_) " '" #_t_ "'" +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_STRING_TYPE(_t_) " '" #_t_ "'" -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(F) \ +#define WINGET_APPROVED_FOR_OUTPUT_LIST(_f_) \ /* It is assumed that single char values need not be localized, as they are matched ordinally or they are punctuation / other. */ \ - F(char) \ + _f_(char) \ /* Localized strings (and from an Id for one for convenience).*/ \ - F(AppInstaller::StringResource::StringId) \ - F(AppInstaller::Resource::LocString) \ + _f_(AppInstaller::StringResource::StringId) \ + _f_(AppInstaller::Resource::LocString) \ /* Strings explicitly declared as localization independent.*/ \ - F(AppInstaller::Utility::LocIndView) \ - F(AppInstaller::Utility::LocIndString) \ + _f_(AppInstaller::Utility::LocIndView) \ + _f_(AppInstaller::Utility::LocIndString) \ /* Normalized strings come from user dataand should therefore already by localized by how they are chosen (or there is no localized version).*/ \ - F(AppInstaller::Utility::NormalizedString) + _f_(AppInstaller::Utility::NormalizedString) -#define WINGET_ISAPPROVEDFOROUTPUT_ERROR \ +#define WINGET_ISAPPROVEDFOROUTPUT_ERROR_MESSAGE \ "Only the following types are approved for output:" \ - WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_TYPE) + WINGET_APPROVED_FOR_OUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_STRING_TYPE) - WINGET_CREATE_ISAPPROVEDFOROUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION) +namespace AppInstaller +{ + namespace Execution::details + { + // List of approved types for output, others are potentially not localized. + template + struct IsApprovedForOutput : std::false_type {}; + WINGET_APPROVED_FOR_OUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION); } template Utility::LocIndString StringResource::StringId::operator()(T ... args) const { - static_assert((Execution::details::IsApprovedForOutput::value && ...), WINGET_ISAPPROVEDFOROUTPUT_ERROR); + static_assert((Execution::details::IsApprovedForOutput::value && ...), WINGET_ISAPPROVEDFOROUTPUT_ERROR_MESSAGE); return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; } } From d60771739d34e1ca03bfef5ed04c7aa644f70e47 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Mon, 15 Aug 2022 22:26:25 -0700 Subject: [PATCH 28/52] Updated loc in command --- src/AppInstallerCLICore/Command.cpp | 16 ++++++++-------- .../Shared/Strings/en-us/winget.resw | 2 +- src/AppInstallerCommonCore/AdminSettings.cpp | 10 +++++----- .../Public/winget/AdminSettings.h | 2 +- .../Public/winget/ExperimentalFeature.h | 3 ++- .../Public/winget/Resources.h | 1 + 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 22cad72c2c..e5b297a730 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -80,8 +80,7 @@ namespace AppInstaller::CLI } // Output the command preamble and command chain - auto commandUsage = Utility::LocIndString{ Utility::Format("{0} {1}", "winget"_lis, commandChain) }; - infoOut << Resource::String::Usage(commandUsage); + infoOut << Resource::String::Usage("winget"_lis, Utility::LocIndView{ commandChain }); auto commandAliases = Aliases(); auto commands = GetVisibleCommands(); @@ -245,6 +244,7 @@ namespace AppInstaller::CLI } } + // TODO https://task.ms/40934960 Change HelpLink return value to LocIndString // Finally, the link to the documentation pages auto helpLink = Utility::LocIndString{ HelpLink() }; if (!helpLink.empty()) @@ -280,7 +280,7 @@ namespace AppInstaller::CLI { auto feature = ExperimentalFeature::GetFeature(command->Feature()); AICLI_LOG(CLI, Error, << "Trying to use command: " << *itr << " without enabling feature " << feature.JsonName()); - throw CommandException(Resource::String::FeatureDisabledMessage(Utility::LocIndView{ feature.JsonName() })); + throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); } if (!Settings::GroupPolicies().IsEnabled(command->GroupPolicy())) @@ -333,14 +333,14 @@ namespace AppInstaller::CLI const std::optional& Type() const { return m_type; } // The actual argument string associated with Type. - const std::string& Arg() const { return m_arg; } + const Utility::LocIndString& Arg() const { return m_arg; } // If set, indicates that the last argument produced an error. const std::optional& Exception() const { return m_exception; } private: std::optional m_type; - std::string m_arg; + Utility::LocIndString m_arg; std::optional m_exception; }; @@ -398,7 +398,7 @@ namespace AppInstaller::CLI // If the next argument was to be a value, but none was provided, convert it to an exception. else if (m_state.Type() && m_invocationItr == m_invocation.end()) { - throw CommandException(Resource::String::MissingArgumentError(Utility::LocIndView{ m_state.Arg() })); + throw CommandException(Resource::String::MissingArgumentError(m_state.Arg())); } } @@ -623,14 +623,14 @@ namespace AppInstaller::CLI { auto setting = Settings::AdminSettingToString(arg.AdminSetting()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " disabled by admin setting " << setting); - throw CommandException(Resource::String::FeatureDisabledByAdminSettingMessage(Utility::LocIndView{ setting })); + throw CommandException(Resource::String::FeatureDisabledByAdminSettingMessage(setting)); } if (!ExperimentalFeature::IsEnabled(arg.Feature()) && execArgs.Contains(arg.ExecArgType())) { auto feature = ExperimentalFeature::GetFeature(arg.Feature()); AICLI_LOG(CLI, Error, << "Trying to use argument: " << arg.Name() << " without enabling feature " << feature.JsonName()); - throw CommandException(Resource::String::FeatureDisabledMessage(Utility::LocIndView{ feature.JsonName() })); + throw CommandException(Resource::String::FeatureDisabledMessage(feature.JsonName())); } if (arg.Required() && !execArgs.Contains(arg.ExecArgType())) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 795cf8ec41..47ed645ce0 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -700,7 +700,7 @@ They can be configured through the settings file 'winget settings'. Shows and performs available upgrades - usage: {0} + usage: {0} {1} Message displayed to provide the user with instructions on how to use a command line command. {0} is a placeholder replaced by the command usage provided by the program. diff --git a/src/AppInstallerCommonCore/AdminSettings.cpp b/src/AppInstallerCommonCore/AdminSettings.cpp index 7d8457f2a4..cd565c4899 100644 --- a/src/AppInstallerCommonCore/AdminSettings.cpp +++ b/src/AppInstallerCommonCore/AdminSettings.cpp @@ -11,12 +11,12 @@ namespace AppInstaller::Settings { using namespace std::string_view_literals; - using namespace Utility; + using namespace Utility::literals; namespace { - constexpr std::string_view s_AdminSettingsYaml_LocalManifestFiles = "LocalManifestFiles"sv; - constexpr std::string_view s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore = "BypassCertificatePinningForMicrosoftStore"sv; + constexpr Utility::LocIndView s_AdminSettingsYaml_LocalManifestFiles = "LocalManifestFiles"_liv; + constexpr Utility::LocIndView s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore = "BypassCertificatePinningForMicrosoftStore"_liv; // Attempts to read a single scalar value from the node. template @@ -168,7 +168,7 @@ namespace AppInstaller::Settings return result; } - std::string_view AdminSettingToString(AdminSetting setting) + Utility::LocIndView AdminSettingToString(AdminSetting setting) { switch (setting) { @@ -177,7 +177,7 @@ namespace AppInstaller::Settings case AdminSetting::BypassCertificatePinningForMicrosoftStore: return s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore; default: - return "Unknown"sv; + return "Unknown"_liv; } } diff --git a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h index 5b5ca913bb..e74276b452 100644 --- a/src/AppInstallerCommonCore/Public/winget/AdminSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/AdminSettings.h @@ -16,7 +16,7 @@ namespace AppInstaller::Settings AdminSetting StringToAdminSetting(std::string_view in); - std::string_view AdminSettingToString(AdminSetting setting); + Utility::LocIndView AdminSettingToString(AdminSetting setting); void EnableAdminSetting(AdminSetting setting); diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 02e894ed52..3d4736abb2 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -4,6 +4,7 @@ #include #include #include +#include "AppInstallerStrings.h" namespace AppInstaller::Settings { @@ -55,7 +56,7 @@ namespace AppInstaller::Settings static std::vector GetAllFeatures(); std::string_view Name() const { return m_name; } - std::string_view JsonName() const { return m_jsonName; } + Utility::LocIndView JsonName() const { return Utility::LocIndView{ m_jsonName }; } std::string_view Link() const { return m_link; } Feature GetFeature() const { return m_feature; } diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 1ce9d4a40e..5c1739d411 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -73,6 +73,7 @@ namespace AppInstaller std::string m_message; }; + // TODO https://task.ms/40934960 Consider renaming or removing LocString // A localized string struct LocString : public Utility::LocIndString { From 02d357b1f08d97fc1ac4c053b169d3794c51a8c9 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Mon, 15 Aug 2022 23:25:35 -0700 Subject: [PATCH 29/52] Updated loc in more commands --- src/AppInstallerCLICore/Commands/InstallCommand.cpp | 2 +- src/AppInstallerCLICore/Commands/RootCommand.cpp | 1 + src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp | 9 ++++----- .../Workflows/MSStoreInstallerHandler.cpp | 10 ++++++---- .../Shared/Strings/en-us/winget.resw | 4 ++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index f26bfc6f29..3479da1079 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -113,7 +113,7 @@ namespace AppInstaller::CLI { if (ConvertToScopeEnum(execArgs.GetArg(Args::Type::InstallScope)) == Manifest::ScopeEnum::Unknown) { - auto validOptions = Utility::LocIndString{ Utility::Format("'{0}', '{1}'", "user"_lis, "machine"_lis) }; + auto validOptions = Utility::Join(", "_liv, std::vector{ "user"_lis, "machine"_lis}); throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Scope, validOptions)); } } diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index 9d472fd0d7..9f98012e83 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -178,6 +178,7 @@ namespace AppInstaller::CLI info << std::endl << "Windows: "_liv << Runtime::GetOSVersion() << std::endl; + // TODO https://task.ms/40934960 Maintain consistency for label definition info << Resource::String::SystemArchitecture(Utility::ConvertFromArchitectureEnum(Utility::GetSystemArchitecture())) << std::endl; if (Runtime::IsRunningInPackagedContext()) diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index ae6d166000..c266d19ff8 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -59,8 +59,8 @@ namespace AppInstaller::CLI::Workflow std::shared_ptr GetAvailableVersionForInstalledPackage( Execution::Context& context, std::shared_ptr package, - std::string_view version, - std::string_view channel, + Utility::LocIndView version, + Utility::LocIndView channel, bool checkVersion) { if (!checkVersion) @@ -81,8 +81,7 @@ namespace AppInstaller::CLI::Workflow << "Installed package version is not available." << " Package Id [" << availablePackageVersion->GetProperty(PackageVersionProperty::Id) << "], Version [" << version << "], Channel [" << channel << "]" << ". Found Version [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "], Channel [" << availablePackageVersion->GetProperty(PackageVersionProperty::Version) << "]"); - auto packageInfo = Utility::LocIndString{ Utility::Format("{0} {1} {2}", availablePackageVersion->GetProperty(PackageVersionProperty::Id), version, channel) }; - context.Reporter.Warn() << Resource::String::InstalledPackageVersionNotAvailable(packageInfo) << std::endl; + context.Reporter.Warn() << Resource::String::InstalledPackageVersionNotAvailable(availablePackageVersion->GetProperty(PackageVersionProperty::Id), version, channel) << std::endl; } } @@ -104,7 +103,7 @@ namespace AppInstaller::CLI::Workflow auto channel = installedPackageVersion->GetProperty(PackageVersionProperty::Channel); // Find an available version of this package to determine its source. - auto availablePackageVersion = GetAvailableVersionForInstalledPackage(context, packageMatch.Package, version, channel, includeVersions); + auto availablePackageVersion = GetAvailableVersionForInstalledPackage(context, packageMatch.Package, Utility::LocIndView{ version }, Utility::LocIndView{ channel }, includeVersions); if (!availablePackageVersion) { // Report package not found and move to next package. diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp index 4ad12619ee..b35c2f2ee1 100644 --- a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -140,9 +140,10 @@ namespace AppInstaller::CLI::Workflow // TODO: Replace with GetUserPresentableMessage? std::ostringstream ssError; ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); + auto errorCodeString = Utility::LocIndString{ ssError.str() }; - context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(Utility::LocIndView{ ssError.str() }) << std::endl; - AICLI_LOG(CLI, Error, << "MSStore install failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << ssError.str()); + context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(errorCodeString) << std::endl; + AICLI_LOG(CLI, Error, << "MSStore install failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << errorCodeString); AICLI_TERMINATE_CONTEXT(errorCode); } } @@ -187,9 +188,10 @@ namespace AppInstaller::CLI::Workflow // TODO: Replace with GetUserPresentableMessage? std::ostringstream ssError; ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); + auto errorCodeString = Utility::LocIndString{ ssError.str() }; - context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(Utility::LocIndView{ ssError.str() }) << std::endl; - AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << ssError.str()); + context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(errorCodeString) << std::endl; + AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << errorCodeString); AICLI_TERMINATE_CONTEXT(errorCode); } } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 47ed645ce0..bc831b00d5 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -835,8 +835,8 @@ They can be configured through the settings file 'winget settings'. Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. - Installed version of package is not available from any source: {0} - Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package information. + Installed version of package is not available from any source: {0} {1} {2} + Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. No packages found in import file From 637fa2831891d9b629c2dcc8852498e751ba27a1 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 16 Aug 2022 00:35:23 -0700 Subject: [PATCH 30/52] Updated loc in more commands --- src/AppInstallerCLICore/Workflows/InstallFlow.cpp | 2 +- src/AppInstallerCLICore/Workflows/SettingsFlow.cpp | 4 ++-- src/AppInstallerCLICore/Workflows/SourceFlow.cpp | 4 ++-- src/AppInstallerCLICore/Workflows/UninstallFlow.cpp | 2 +- .../Public/AppInstallerStrings.h | 13 ++++--------- .../Public/winget/UserSettings.h | 4 ++-- 6 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 1273037844..e6ed8dc6b6 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -479,7 +479,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::InstallerFailedWithCode(Utility::ToString(installResult)) + << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ Utility::ToString(installResult) }) << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp index 9f53904220..7b71668877 100644 --- a/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SettingsFlow.cpp @@ -36,7 +36,7 @@ namespace AppInstaller::CLI::Workflow { if (warning.IsFieldWarning) { - warn << ' ' << Resource::String::SettingsWarningField(Utility::LocIndView{ warning.Path }); + warn << ' ' << Resource::String::SettingsWarningField(warning.Path); } else { @@ -48,7 +48,7 @@ namespace AppInstaller::CLI::Workflow { if (warning.IsFieldWarning) { - warn << ' ' << Resource::String::SettingsWarningValue(Utility::LocIndView{ warning.Data }); + warn << ' ' << Resource::String::SettingsWarningValue(warning.Data); } else { diff --git a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp index 5ecea09b5b..a38a6671d7 100644 --- a/src/AppInstallerCLICore/Workflows/SourceFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/SourceFlow.cpp @@ -22,7 +22,7 @@ namespace AppInstaller::CLI::Workflow auto currentSources = Repository::Source::GetCurrentSources(); if (context.Args.Contains(Args::Type::SourceName)) { - std::string_view name = context.Args.GetArg(Args::Type::SourceName); + auto name = Utility::LocIndString{ context.Args.GetArg(Args::Type::SourceName) }; for (auto const& source : currentSources) { @@ -35,7 +35,7 @@ namespace AppInstaller::CLI::Workflow } } - context.Reporter.Error() << Resource::String::SourceListNoneFound(Utility::LocIndView{ name }) << std::endl; + context.Reporter.Error() << Resource::String::SourceListNoneFound(name) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_SOURCE_NAME_DOES_NOT_EXIST); } else diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index d52cbc4e28..6df5027cca 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -259,7 +259,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::UninstallFailedWithCode(Utility::ToString(uninstallResult)) + << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ Utility::ToString(uninstallResult) }) << std::endl; } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index e763f914b8..47526b955a 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -234,16 +234,11 @@ namespace AppInstaller::Utility // Superset of std::to_string supporting string convertibles as input. template - Utility::LocIndString ToString(T value) + std::string ToString(T value) { - if constexpr (std::is_convertible_v || std::is_convertible_v) - { - return LocIndString{ value }; - } - else - { - return LocIndString{ std::to_string(value) }; - } + std::ostringstream oss; + oss << value; + return oss.str(); } // Format an input string by replacing placeholders {index} with provided values at corresponding indices. diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index ef66182813..1974e22a44 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -171,8 +171,8 @@ namespace AppInstaller::Settings Message(message), Path(settingPath), Data(settingValue), IsFieldWarning(isField) {} StringResource::StringId Message; - std::string Path; - std::string Data; + Utility::LocIndString Path; + Utility::LocIndString Data; bool IsFieldWarning = true; }; From 1ee37cd44b6b629720818641280f16a86bf48c58 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 16 Aug 2022 09:48:52 -0700 Subject: [PATCH 31/52] Fix typos and format --- src/AppInstallerCLICore/Commands/InstallCommand.cpp | 2 +- .../Workflows/DependencyNodeProcessor.cpp | 5 +++-- .../Shared/Strings/en-us/winget.resw | 10 +++++----- src/AppInstallerCommonCore/AppInstallerStrings.cpp | 2 +- .../Public/AppInstallerStrings.h | 6 +++--- .../Public/winget/ChannelStreams.h | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 3479da1079..968f233327 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -125,7 +125,7 @@ namespace AppInstaller::CLI std::vector applicableArchitectures; for (Utility::Architecture i : Utility::GetApplicableArchitectures()) { - applicableArchitectures.emplace_back(Utility::Format("'{0}'", Utility::ConvertFromArchitectureEnum(i))); + applicableArchitectures.emplace_back(Utility::ConvertFromArchitectureEnum(i)); } auto validOptions = Utility::Join(", "_liv, applicableArchitectures); throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Architecture, validOptions)); diff --git a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp index d3241abd53..ce30b73b07 100644 --- a/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp +++ b/src/AppInstallerCLICore/Workflows/DependencyNodeProcessor.cpp @@ -86,8 +86,9 @@ namespace AppInstaller::CLI::Workflow if (!installer.has_value()) { - auto manifestInfo = Utility::LocIndString{ Utility::Format("{0}{1}", Utility::Normalize(m_nodeManifest.Id), m_nodeManifest.Version)}; - error << Resource::String::DependenciesFlowNoSuitableInstallerFound(manifestInfo); + auto manifestId = Utility::LocIndString{ Utility::Normalize(m_nodeManifest.Id) }; + auto manifestVersion = Utility::LocIndString{ m_nodeManifest.Version }; + error << Resource::String::DependenciesFlowNoSuitableInstallerFound(manifestId, manifestVersion); AICLI_LOG(CLI, Error, << "No suitable installer found for manifest " << m_nodeManifest.Id << " with version " << m_nodeManifest.Version); return DependencyNodeProcessorResult::Error; } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 7897af63e5..33eb14c0ef 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -127,7 +127,7 @@ The following arguments are available: - Message displayed to infom the user about the available command line arguments. + Message displayed to inform the user about the available command line arguments. The following command aliases are available: @@ -135,7 +135,7 @@ The following commands are available: - Message displayed to inform the user about the avialable command line command arguments. + Message displayed to inform the user about the available command line command arguments. Available @@ -315,7 +315,7 @@ They can be configured through the settings file 'winget settings'. Argument name was not recognized for the current command: '{0}' - Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's arugment name input (e.g. '--example'). + Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Locale to use (BCP47 format) @@ -1015,8 +1015,8 @@ Configuration is disabled due to Group Policy. Dependency graph has loop - No suitable installer found for manifest: {0} - Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest information. + No suitable installer found for manifest: {0} {1} + Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Error processing package dependencies, do you wish to continue installation? diff --git a/src/AppInstallerCommonCore/AppInstallerStrings.cpp b/src/AppInstallerCommonCore/AppInstallerStrings.cpp index f649f0dd39..3f522b89a4 100644 --- a/src/AppInstallerCommonCore/AppInstallerStrings.cpp +++ b/src/AppInstallerCommonCore/AppInstallerStrings.cpp @@ -716,7 +716,7 @@ namespace AppInstaller::Utility std::ostringstream ssJoin; ssJoin << vector[0]; - for (int i = 1; i < vectorSize; ++i) + for (size_t i = 1; i < vectorSize; ++i) { ssJoin << separator << vector[i]; } diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index 47526b955a..3886a3c9a4 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -236,9 +236,9 @@ namespace AppInstaller::Utility template std::string ToString(T value) { - std::ostringstream oss; - oss << value; - return oss.str(); + std::ostringstream result; + result << value; + return result.str(); } // Format an input string by replacing placeholders {index} with provided values at corresponding indices. diff --git a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h index 6305c47353..2175c2822a 100644 --- a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h +++ b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h @@ -14,7 +14,7 @@ /* Strings explicitly declared as localization independent.*/ \ _f_(AppInstaller::Utility::LocIndView) \ _f_(AppInstaller::Utility::LocIndString) \ - /* Normalized strings come from user dataand should therefore already by localized + /* Normalized strings come from user data and should therefore already by localized by how they are chosen (or there is no localized version).*/ \ _f_(AppInstaller::Utility::NormalizedString) From 0de00d09ab3cc279e5a6a9046f36e792fce7f63e Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 16 Aug 2022 11:23:59 -0700 Subject: [PATCH 32/52] Revert ToString --- src/AppInstallerCLICore/Commands/InstallCommand.cpp | 2 +- src/AppInstallerCLICore/Commands/RootCommand.cpp | 2 +- src/AppInstallerCLICore/Workflows/InstallFlow.cpp | 2 +- .../Workflows/ManifestComparator.cpp | 8 ++++---- src/AppInstallerCLICore/Workflows/UninstallFlow.cpp | 2 +- src/AppInstallerCommonCore/Architecture.cpp | 2 +- .../Manifest/ManifestValidation.cpp | 2 +- .../Public/AppInstallerArchitecture.h | 2 +- .../Public/AppInstallerStrings.h | 11 +---------- .../Microsoft/ARPHelper.cpp | 2 +- 10 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 968f233327..d1dd5936d4 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -125,7 +125,7 @@ namespace AppInstaller::CLI std::vector applicableArchitectures; for (Utility::Architecture i : Utility::GetApplicableArchitectures()) { - applicableArchitectures.emplace_back(Utility::ConvertFromArchitectureEnum(i)); + applicableArchitectures.emplace_back(Utility::ToString(i)); } auto validOptions = Utility::Join(", "_liv, applicableArchitectures); throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Architecture, validOptions)); diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index 9f98012e83..ae5fb58d6a 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -179,7 +179,7 @@ namespace AppInstaller::CLI "Windows: "_liv << Runtime::GetOSVersion() << std::endl; // TODO https://task.ms/40934960 Maintain consistency for label definition - info << Resource::String::SystemArchitecture(Utility::ConvertFromArchitectureEnum(Utility::GetSystemArchitecture())) << std::endl; + info << Resource::String::SystemArchitecture(Utility::ToString(Utility::GetSystemArchitecture())) << std::endl; if (Runtime::IsRunningInPackagedContext()) { diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index e1c6d57fae..a9f760a1e3 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -396,7 +396,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ Utility::ToString(installResult) }) + << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ std::to_string(installResult) }) << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp index 888bf095ec..1daeb51c27 100644 --- a/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp +++ b/src/AppInstallerCLICore/Workflows/ManifestComparator.cpp @@ -12,7 +12,7 @@ using namespace AppInstaller::Manifest; std::ostream& operator<<(std::ostream& out, const AppInstaller::Manifest::ManifestInstaller& installer) { return out << '[' << - AppInstaller::Utility::ConvertFromArchitectureEnum(installer.Arch) << ',' << + AppInstaller::Utility::ToString(installer.Arch) << ',' << AppInstaller::Manifest::InstallerTypeToString(installer.EffectiveInstallerType()) << ',' << AppInstaller::Manifest::ScopeToString(installer.Scope) << ',' << installer.Locale << ']'; @@ -74,7 +74,7 @@ namespace AppInstaller::CLI::Workflow MachineArchitectureComparator(std::vector allowedArchitectures) : details::ComparisonField("Machine Architecture"), m_allowedArchitectures(std::move(allowedArchitectures)) { - AICLI_LOG(CLI, Verbose, << "Architecture Comparator created with allowed architectures: " << Utility::ConvertContainerToString(m_allowedArchitectures, Utility::ConvertFromArchitectureEnum)); + AICLI_LOG(CLI, Verbose, << "Architecture Comparator created with allowed architectures: " << Utility::ConvertContainerToString(m_allowedArchitectures, Utility::ToString)); } // TODO: At some point we can do better about matching the currently installed architecture @@ -142,7 +142,7 @@ namespace AppInstaller::CLI::Workflow if (Utility::IsApplicableArchitecture(installer.Arch) == Utility::InapplicableArchitecture) { result = "Machine is not compatible with "; - result += Utility::ConvertFromArchitectureEnum(installer.Arch); + result += Utility::ToString(installer.Arch); } else if (IsSystemArchitectureUnsupportedByInstaller(installer)) { @@ -151,7 +151,7 @@ namespace AppInstaller::CLI::Workflow else { result = "Architecture was excluded by caller : "; - result += Utility::ConvertFromArchitectureEnum(installer.Arch); + result += Utility::ToString(installer.Arch); } return result; diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 6df5027cca..4e4c4e062f 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -259,7 +259,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ Utility::ToString(uninstallResult) }) + << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ std::to_string(uninstallResult) }) << std::endl; } diff --git a/src/AppInstallerCommonCore/Architecture.cpp b/src/AppInstallerCommonCore/Architecture.cpp index 7069b99385..93d1fe8a34 100644 --- a/src/AppInstallerCommonCore/Architecture.cpp +++ b/src/AppInstallerCommonCore/Architecture.cpp @@ -187,7 +187,7 @@ namespace AppInstaller::Utility return Architecture::Unknown; } - LocIndView ConvertFromArchitectureEnum(Architecture architecture) + LocIndView ToString(Architecture architecture) { switch (architecture) { diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp index 48116ea18d..0370d46730 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -82,7 +82,7 @@ namespace AppInstaller::Manifest if (!duplicateInstallerFound && !installerSet.insert(installer).second) { AICLI_LOG(Core, Error, << "Duplicate installer: Type[" << InstallerTypeToString(installer.EffectiveInstallerType()) << - "] Architecture[" << Utility::ConvertFromArchitectureEnum(installer.Arch) << "] Locale[" << installer.Locale << + "] Architecture[" << Utility::ToString(installer.Arch) << "] Locale[" << installer.Locale << "] Scope[" << ScopeToString(installer.Scope) << "]"); resultErrors.emplace_back(ManifestError::DuplicateInstallerEntry); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h b/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h index bd372bb75d..5e20cf14e2 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerArchitecture.h @@ -23,7 +23,7 @@ namespace AppInstaller::Utility Architecture ConvertToArchitectureEnum(const std::string& archStr); // Converts an Architecture to a string_view - LocIndView ConvertFromArchitectureEnum(Architecture architecture); + LocIndView ToString(Architecture architecture); // Gets the system's architecture as Architecture enum AppInstaller::Utility::Architecture GetSystemArchitecture(); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index 3886a3c9a4..5b78455a98 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -232,22 +232,13 @@ namespace AppInstaller::Utility // Join a string vector using the provided separator. LocIndString Join(LocIndView separator, const std::vector& vector); - // Superset of std::to_string supporting string convertibles as input. - template - std::string ToString(T value) - { - std::ostringstream result; - result << value; - return result.str(); - } - // Format an input string by replacing placeholders {index} with provided values at corresponding indices. // Note: After upgrading to C++20, this function should be deprecated in favor of std::format. template std::string Format(std::string inputStr, T ... args) { int index = 0; - (FindAndReplace(inputStr, "{" + std::to_string(index++) + "}", ToString(args)), ...); + (FindAndReplace(inputStr, "{" + std::to_string(index++) + "}", (std::ostringstream() << args).str()),...); return inputStr; } } diff --git a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp index be551a7da9..699deeaa9d 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp @@ -312,7 +312,7 @@ namespace AppInstaller::Repository::Microsoft if (arpRootKey) { - PopulateIndexFromKey(index, arpRootKey, Manifest::ScopeToString(scope), Utility::ConvertFromArchitectureEnum(architecture), upgradeCodes); + PopulateIndexFromKey(index, arpRootKey, Manifest::ScopeToString(scope), Utility::ToString(architecture), upgradeCodes); } } } From aa8182005af4995a158d56e10baf83b41f1b6691 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:00:03 -0700 Subject: [PATCH 33/52] Added TODO --- src/AppInstallerCLICore/Commands/InstallCommand.h | 1 + src/AppInstallerCommonCore/Public/AppInstallerStrings.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.h b/src/AppInstallerCLICore/Commands/InstallCommand.h index cb78ca538c..83d695b565 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.h +++ b/src/AppInstallerCLICore/Commands/InstallCommand.h @@ -16,6 +16,7 @@ namespace AppInstaller::CLI void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; + // TODO https://task.ms/40934960 Consider changing the return type to loc independent std::string HelpLink() const override; protected: diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index 5b78455a98..440ba302cd 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -19,6 +19,7 @@ namespace AppInstaller::Utility // Converts the given UTF8 string to UTF32 std::u32string ConvertToUTF32(std::string_view input); + // TODO https://task.ms/40934960 Consider changing the return type to loc independent // Normalizes a UTF8 string to the given form. std::string Normalize(std::string_view input, NORM_FORM form = NORM_FORM::NormalizationKC); From 873cc08853f1cb598593831530b2d127c99f9f65 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 16 Aug 2022 14:42:21 -0700 Subject: [PATCH 34/52] Addressed comments --- src/AppInstallerCLITests/Resources.cpp | 1 - src/AppInstallerCommonCore/Public/winget/ChannelStreams.h | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLITests/Resources.cpp b/src/AppInstallerCLITests/Resources.cpp index 1d2499f284..b7d83b6b82 100644 --- a/src/AppInstallerCLITests/Resources.cpp +++ b/src/AppInstallerCLITests/Resources.cpp @@ -1,4 +1,3 @@ - // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" diff --git a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h index 2175c2822a..4e7f15f348 100644 --- a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h +++ b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #pragma once #include "Resources.h" From 71ac74c2dab9d6820a807dec7a45369cec8c7806 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 16 Aug 2022 15:52:31 -0700 Subject: [PATCH 35/52] Addressed comment + renamed header file --- src/AppInstallerCLICore/ChannelStreams.h | 2 +- .../AppInstallerCommonCore.vcxproj | 2 +- .../AppInstallerCommonCore.vcxproj.filters | 2 +- .../Public/winget/ChannelStreams.h | 43 ------------------ .../Public/winget/LocalizationFormat.h | 44 +++++++++++++++++++ 5 files changed, 47 insertions(+), 46 deletions(-) delete mode 100644 src/AppInstallerCommonCore/Public/winget/ChannelStreams.h create mode 100644 src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index 47271c608c..7efcb44e2a 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -4,7 +4,7 @@ #include "Resources.h" #include "VTSupport.h" #include -#include +#include #include #include diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index e24311b5f9..24a8e92623 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -307,7 +307,7 @@ - + diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters index 9767c055ba..7ca8728079 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -213,7 +213,7 @@ Header Files - + Public\winget diff --git a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h b/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h deleted file mode 100644 index 4e7f15f348..0000000000 --- a/src/AppInstallerCommonCore/Public/winget/ChannelStreams.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "Resources.h" - -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) template <> struct IsApprovedForOutput<_t_> : std::true_type {}; -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_STRING_TYPE(_t_) " '" #_t_ "'" - -#define WINGET_APPROVED_FOR_OUTPUT_LIST(_f_) \ - /* It is assumed that single char values need not be localized, as they are matched - ordinally or they are punctuation / other. */ \ - _f_(char) \ - /* Localized strings (and from an Id for one for convenience).*/ \ - _f_(AppInstaller::StringResource::StringId) \ - _f_(AppInstaller::Resource::LocString) \ - /* Strings explicitly declared as localization independent.*/ \ - _f_(AppInstaller::Utility::LocIndView) \ - _f_(AppInstaller::Utility::LocIndString) \ - /* Normalized strings come from user data and should therefore already by localized - by how they are chosen (or there is no localized version).*/ \ - _f_(AppInstaller::Utility::NormalizedString) - -#define WINGET_ISAPPROVEDFOROUTPUT_ERROR_MESSAGE \ - "Only the following types are approved for output:" \ - WINGET_APPROVED_FOR_OUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_STRING_TYPE) - -namespace AppInstaller -{ - namespace Execution::details - { - // List of approved types for output, others are potentially not localized. - template - struct IsApprovedForOutput : std::false_type {}; - WINGET_APPROVED_FOR_OUTPUT_LIST(WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION); - } - - template - Utility::LocIndString StringResource::StringId::operator()(T ... args) const - { - static_assert((Execution::details::IsApprovedForOutput::value && ...), WINGET_ISAPPROVEDFOROUTPUT_ERROR_MESSAGE); - return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; - } -} diff --git a/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h b/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h new file mode 100644 index 0000000000..0c611703a1 --- /dev/null +++ b/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "Resources.h" + +namespace AppInstaller::StringResource +{ + namespace details + { + // List of approved types for output, others are potentially not localized. + template + struct IsApprovedForOutput + { + static constexpr bool value = false; + }; + +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) \ + template <> \ + struct IsApprovedForOutput<_t_> \ + { \ + static constexpr bool value = true; \ + } + + // It is assumed that single char values need not be localized, as they are matched + // ordinally or they are punctuation / other. + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(char); + // Localized strings (and from an Id for one for convenience). + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(StringId); + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Resource::LocString); + // Strings explicitly declared as localization independent. + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndView); + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndString); + // Normalized strings come from user data and should therefore already by localized + // by how they are chosen (or there is no localized version). + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::NormalizedString); + } + + template + Utility::LocIndString StringId::operator()(T ... args) const + { + static_assert((details::IsApprovedForOutput>::value && ...), "This type may not be localized, see comment for more information"); + return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; + } +} From 57afaa83fefbec3447a9a53517f064bfcbcd7dc0 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 16 Aug 2022 21:39:23 -0700 Subject: [PATCH 36/52] Addressed comments --- .../Public/winget/ExperimentalFeature.h | 4 ++-- src/AppInstallerCommonCore/Public/winget/Resources.h | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h index 3d4736abb2..7cda7be833 100644 --- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h +++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h @@ -56,13 +56,13 @@ namespace AppInstaller::Settings static std::vector GetAllFeatures(); std::string_view Name() const { return m_name; } - Utility::LocIndView JsonName() const { return Utility::LocIndView{ m_jsonName }; } + Utility::LocIndView JsonName() const { return m_jsonName; } std::string_view Link() const { return m_link; } Feature GetFeature() const { return m_feature; } private: std::string_view m_name; - std::string_view m_jsonName; + Utility::LocIndView m_jsonName; std::string_view m_link; Feature m_feature; }; diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 5c1739d411..b452d3bd0d 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -23,9 +23,12 @@ namespace AppInstaller { explicit constexpr StringId(std::wstring_view id) : std::wstring_view(id) {} + // Sets the placeholder values in the resolved string id. template Utility::LocIndString operator()(T ... args) const; private: + + // Resolve the string id. std::string Resolve() const; }; From 1c754a503949a4c8a86abba0f918591588ef83b1 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 17 Aug 2022 08:39:15 -0700 Subject: [PATCH 37/52] Addressed comments --- .../Shared/Strings/en-us/winget.resw | 11 +++++------ src/AppInstallerCommonCore/Public/winget/Resources.h | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 33eb14c0ef..628f8b47d7 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -135,11 +135,10 @@ The following commands are available: - Message displayed to inform the user about the available command line command arguments. + Message displayed to inform the user about the available commands. Available - As in "a new version is available to update to". Label displayed when a new application package is available. @@ -148,7 +147,7 @@ The following sub-commands are available: - Message displayed to inform the user about the available nested command line command arguments that runs in context of the selected command. + Message displayed to inform the user about the available nested commands that run in context of the selected command. upgrades available. @@ -163,7 +162,7 @@ Filter results by command - Description message displayed to inform the user about filtering the search results by a command line command. + Description message displayed to inform the user about filtering the search results by a command. The full command line for completion @@ -684,7 +683,7 @@ They can be configured through the settings file 'winget settings'. Unrecognized command: '{0}' - Error message displayed when the user provides an unrecognized command line command. {0} is a placeholder replaced by the user input. + Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Update all installed packages to latest if available @@ -701,7 +700,7 @@ They can be configured through the settings file 'winget settings'. usage: {0} {1} - Message displayed to provide the user with instructions on how to use a command line command. {0} is a placeholder replaced by the command usage provided by the program. + Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Validates a manifest using a strict set of guidelines. This is intended to enable you to check your manifest before submitting to a repo. diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index b452d3bd0d..9e96547370 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -5,7 +5,6 @@ #include #include -#include "LocIndependent.h" #include "AppInstallerStrings.h" using namespace std::string_view_literals; From b71b4fa30b9c4b03818548b7952a8b20ff6fef62 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 17 Aug 2022 09:25:02 -0700 Subject: [PATCH 38/52] Resolved comments --- src/AppInstallerCLICore/Commands/RootCommand.cpp | 8 +++----- .../Shared/Strings/en-us/winget.resw | 14 ++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index ae5fb58d6a..e5b19ab15c 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -178,16 +178,14 @@ namespace AppInstaller::CLI info << std::endl << "Windows: "_liv << Runtime::GetOSVersion() << std::endl; - // TODO https://task.ms/40934960 Maintain consistency for label definition - info << Resource::String::SystemArchitecture(Utility::ToString(Utility::GetSystemArchitecture())) << std::endl; + info << Resource::String::SystemArchitecture << ": "_liv << Utility::ToString(Utility::GetSystemArchitecture()) << std::endl; if (Runtime::IsRunningInPackagedContext()) { - info << Resource::String::Package(Runtime::GetPackageVersion()) << std::endl; + info << Resource::String::Package << ": "_liv << Runtime::GetPackageVersion() << std::endl; }; - auto logPath = Utility::LocIndString{ Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() }; - info << std::endl << Resource::String::Logs(logPath) << std::endl; + info << std::endl << Resource::String::Logs << ": "_liv << Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() << std::endl; info << std::endl; diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 628f8b47d7..3834b5eb21 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -135,7 +135,7 @@ The following commands are available: - Message displayed to inform the user about the available commands. + Title displayed to inform the user about the available commands. Available @@ -162,7 +162,7 @@ Filter results by command - Description message displayed to inform the user about filtering the search results by a command. + Description message displayed to inform the user about filtering the search results by a package command. The full command line for completion @@ -430,8 +430,7 @@ They can be configured through the settings file 'winget settings'. Override arguments to be passed on to the installer - Package: {0} - Label displayed for an application/software package. {0} is a placeholder replaced by an application package version. + Package Oops, we forgot to do this... @@ -756,8 +755,8 @@ They can be configured through the settings file 'winget settings'. Both local manifest and search query arguments are provided - Logs: {0} - Label displayed for a diagnostic files containing information about the application use. {0} is a placeholder replaced by the diagnostic file path. + Logs + Label displayed for a diagnostic files containing information about the application use. The installer is blocked by policy @@ -1334,8 +1333,7 @@ Please specify one of them using the `--source` option to proceed. The arguments provided can only be used with a query. - System Architecture: {0} - Label displayed for the system architecture. {0} is a placeholder replaced by the system architecture. + System Architecture Retains all files and directories created by the package (portable) From 1f5a9ea1c659e363c79797d33eef044aa3ca97d3 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Thu, 18 Aug 2022 13:48:01 -0700 Subject: [PATCH 39/52] Addressed comments --- src/AppInstallerCLICore/Core.cpp | 3 +- .../Workflows/InstallFlow.cpp | 2 +- .../Workflows/UninstallFlow.cpp | 2 +- .../Workflows/WorkflowBase.cpp | 2 +- .../Shared/Strings/en-us/winget.resw | 4 +- src/AppInstallerCLITests/Resources.cpp | 41 +++++++++++++------ .../Public/winget/LocalizationFormat.h | 2 +- .../Public/winget/Resources.h | 6 +-- src/AppInstallerCommonCore/Resources.cpp | 5 +++ 9 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/AppInstallerCLICore/Core.cpp b/src/AppInstallerCLICore/Core.cpp index 7a08639236..5a2e3f8086 100644 --- a/src/AppInstallerCLICore/Core.cpp +++ b/src/AppInstallerCLICore/Core.cpp @@ -135,9 +135,8 @@ namespace AppInstaller::CLI { // Report any action blocked by Group Policy. auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); - auto policyNameId = policy.PolicyName(); AICLI_LOG(CLI, Error, << "Operation blocked by Group Policy: " << policy.RegValueName()); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policyNameId()) << std::endl; + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policy.PolicyName()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index a9f760a1e3..bb4057cd23 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -396,7 +396,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::InstallerFailedWithCode(Utility::LocIndView{ std::to_string(installResult) }) + << Resource::String::InstallerFailedWithCode(installResult) << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp index 4e4c4e062f..a0cd1c1740 100644 --- a/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/UninstallFlow.cpp @@ -259,7 +259,7 @@ namespace AppInstaller::CLI::Workflow else { context.Reporter.Error() - << Resource::String::UninstallFailedWithCode(Utility::LocIndView{ std::to_string(uninstallResult) }) + << Resource::String::UninstallFailedWithCode(uninstallResult) << std::endl; } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index f011a97e6f..f137543fe5 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -772,7 +772,7 @@ namespace AppInstaller::CLI::Workflow if (m_onlyShowUpgrades) { - context.Reporter.Info() << availableUpgradesCount << ' ' << Resource::String::AvailableUpgrades << std::endl; + context.Reporter.Info() << Resource::String::AvailableUpgrades(availableUpgradesCount) << std::endl; } } diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 3834b5eb21..8bc2b59059 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -150,8 +150,8 @@ Message displayed to inform the user about the available nested commands that run in context of the selected command. - upgrades available. - Message displayed to inform the user about available application package upgrades. + {0} upgrades available. + Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Use the specified channel; default is general audience diff --git a/src/AppInstallerCLITests/Resources.cpp b/src/AppInstallerCLITests/Resources.cpp index b7d83b6b82..db8d0f0411 100644 --- a/src/AppInstallerCLITests/Resources.cpp +++ b/src/AppInstallerCLITests/Resources.cpp @@ -7,32 +7,49 @@ #include using namespace std::string_view_literals; -using namespace AppInstaller::CLI::Resource; using namespace AppInstaller::Utility; using namespace AppInstaller::Utility::literals; -using namespace AppInstaller::CLI::Execution; +using namespace AppInstaller::CLI; #define WINGET_TEST_OUTPUT_STREAM(_expected_, _input_) \ - std::istringstream iInput; \ - std::ostringstream oInput; \ - std::istringstream iExpected; \ - std::ostringstream oExpected; \ - Reporter(oInput, iInput).Info() << _input_; \ - Reporter(oExpected, iExpected).Info() << _expected_; \ - REQUIRE(oExpected.str()== oInput.str()); + do { \ + std::istringstream iInput; \ + std::ostringstream oInput; \ + std::istringstream iExpected; \ + std::ostringstream oExpected; \ + Execution::Reporter(oInput, iInput).Info() << _input_; \ + Execution::Reporter(oExpected, iExpected).Info() << _expected_; \ + REQUIRE(oExpected.str()== oInput.str()); \ + } while(0); TEST_CASE("Resources_StringId", "[resources]") { WINGET_TEST_OUTPUT_STREAM( "Filter results by command"_liv, - String::CommandArgumentDescription + Resource::String::CommandArgumentDescription ); } -TEST_CASE("Resources_StringIdWithPlaceholders", "[resources]") +TEST_CASE("Resources_StringIdWithPlaceholders_LocIndString", "[resources]") { WINGET_TEST_OUTPUT_STREAM( "The value provided for the `First` argument is invalid; valid values are: Second"_liv , - String::InvalidArgumentValueError("First"_liv, "Second"_liv) + Resource::String::InvalidArgumentValueError("First"_liv, "Second"_liv) + ); +} + +TEST_CASE("Resources_StringIdWithPlaceholders_StringId", "[resources]") +{ + WINGET_TEST_OUTPUT_STREAM( + "This operation is disabled by Group Policy: Enable Additional Windows App Installer Sources"_liv , + Resource::String::DisabledByGroupPolicy(AppInstaller::StringResource::String::PolicyAdditionalSources) + ); +} + +TEST_CASE("Resources_StringIdWithPlaceholders_Arithmetic", "[resources]") +{ + WINGET_TEST_OUTPUT_STREAM( + "42 upgrades available."_liv , + Resource::String::AvailableUpgrades(42) ); } diff --git a/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h b/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h index 0c611703a1..163d193644 100644 --- a/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h +++ b/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h @@ -11,7 +11,7 @@ namespace AppInstaller::StringResource template struct IsApprovedForOutput { - static constexpr bool value = false; + static constexpr bool value = std::is_arithmetic::value; }; #define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) \ diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 9e96547370..ad4f60c929 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -25,6 +25,8 @@ namespace AppInstaller // Sets the placeholder values in the resolved string id. template Utility::LocIndString operator()(T ... args) const; + + friend std::ostream& operator<<(std::ostream& out, StringId si); private: // Resolve the string id. @@ -93,7 +95,3 @@ namespace AppInstaller } } -inline std::ostream& operator<<(std::ostream& out, AppInstaller::StringResource::StringId si) -{ - return (out << AppInstaller::Resource::LocString{ si }); -} diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 9edef38ac2..9201c4589f 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -113,5 +113,10 @@ namespace AppInstaller { return Resource::Loader::Instance().ResolveString(*this); } + + std::ostream& operator<<(std::ostream& out, StringId si) + { + return (out << Resource::LocString{ si }); + } } } From adfedbd38ab01d0a4b639dac9a0bfa98d31e2254 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Sat, 20 Aug 2022 15:08:36 -0700 Subject: [PATCH 40/52] Addressed comments --- src/AppInstallerCLICore/Command.cpp | 1 - src/AppInstallerCLICore/Commands/InstallCommand.h | 1 - src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 4 ++-- src/AppInstallerCommonCore/Public/AppInstallerStrings.h | 1 - src/AppInstallerCommonCore/Public/winget/Resources.h | 1 - 5 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 41ff3dbdaa..a1a472a09d 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -244,7 +244,6 @@ namespace AppInstaller::CLI } } - // TODO https://task.ms/40934960 Change HelpLink return value to LocIndString // Finally, the link to the documentation pages auto helpLink = Utility::LocIndString{ HelpLink() }; if (!helpLink.empty()) diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.h b/src/AppInstallerCLICore/Commands/InstallCommand.h index 83d695b565..cb78ca538c 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.h +++ b/src/AppInstallerCLICore/Commands/InstallCommand.h @@ -16,7 +16,6 @@ namespace AppInstaller::CLI void Complete(Execution::Context& context, Execution::Args::Type valueType) const override; - // TODO https://task.ms/40934960 Consider changing the return type to loc independent std::string HelpLink() const override; protected: diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 8bc2b59059..0a62dd17db 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -203,7 +203,7 @@ This feature is a work in progress, and may be changed dramatically or removed altogether in the future. To enable it, edit your settings ('winget settings') to include the experimental feature: '{0}' - Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. + {Locked="winget settings"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Shows the status of experimental features. Experimental features can be turn on via 'winget settings'. @@ -1196,7 +1196,7 @@ Do you agree to the terms? This feature needs to be enabled by administrators. To enable it, run 'winget settings --enable {0}' as administrator. - Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. + {Locked="winget settings --enable"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Enables the specific administrator setting diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h index 440ba302cd..5b78455a98 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -19,7 +19,6 @@ namespace AppInstaller::Utility // Converts the given UTF8 string to UTF32 std::u32string ConvertToUTF32(std::string_view input); - // TODO https://task.ms/40934960 Consider changing the return type to loc independent // Normalizes a UTF8 string to the given form. std::string Normalize(std::string_view input, NORM_FORM form = NORM_FORM::NormalizationKC); diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index ad4f60c929..806c010826 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -77,7 +77,6 @@ namespace AppInstaller std::string m_message; }; - // TODO https://task.ms/40934960 Consider renaming or removing LocString // A localized string struct LocString : public Utility::LocIndString { From eb4b47f4d73499dcd37b78dcc7bed5aaab93dcf1 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 23 Aug 2022 08:10:22 -0700 Subject: [PATCH 41/52] Addressed comment --- src/AppInstallerCLICore/Resources.h | 1 - .../Workflows/WorkflowBase.cpp | 6 ------ .../Public/winget/Resources.h | 10 ---------- src/AppInstallerCommonCore/Resources.cpp | 18 ++++++++++-------- 4 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index f1fbd4ddc9..1b1c0cbf59 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -11,7 +11,6 @@ namespace AppInstaller::CLI::Resource { using AppInstaller::StringResource::StringId; using AppInstaller::Resource::LocString; - using AppInstaller::Resource::ResourceOpenException; // Resource string identifiers. // This list can mostly be generated by the following PowerShell: diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index f137543fe5..80fb6d9f91 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -263,12 +263,6 @@ namespace AppInstaller::CLI::Workflow context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policyNameId()) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } - catch (const Resource::ResourceOpenException& e) - { - Logging::Telemetry().LogException(Logging::FailureTypeEnum::ResourceOpen, e.what()); - context.Reporter.Error() << GetUserPresentableMessage(e) << std::endl; - return APPINSTALLER_CLI_ERROR_MISSING_RESOURCE_FILE; - } catch (const std::exception& e) { Logging::Telemetry().LogException(Logging::FailureTypeEnum::StdException, e.what()); diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 806c010826..257b035a68 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -67,16 +67,6 @@ namespace AppInstaller std::pair GetResourceAsBytes(int resourceName, int resourceType); std::pair GetResourceAsBytes(PCWSTR resourceName, PCWSTR resourceType); - struct ResourceOpenException : std::exception - { - ResourceOpenException(const winrt::hresult_error& hre); - - const char* what() const noexcept override { return m_message.c_str(); } - - private: - std::string m_message; - }; - // A localized string struct LocString : public Utility::LocIndString { diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 9201c4589f..625264ef10 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -4,7 +4,8 @@ #include "winget/Resources.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" -#include "Public//AppInstallerErrors.h" +#include "Public/AppInstallerErrors.h" +#include "Public/AppInstallerTelemetry.h" namespace AppInstaller { @@ -59,11 +60,6 @@ namespace AppInstaller return std::make_pair(reinterpret_cast(resourceData.first), resourceData.second); } - ResourceOpenException::ResourceOpenException(const winrt::hresult_error& hre) - { - m_message = "Could not open the resource file: " + GetUserPresentableMessage(hre); - } - // Utility class to load resources class Loader { @@ -78,7 +74,13 @@ namespace AppInstaller // Gets the the string resource value. std::string ResolveString(std::wstring_view resKey) const { - return Utility::ConvertToUTF8(m_wingetLoader.GetString(resKey)); + if (m_wingetLoader) + { + return Utility::ConvertToUTF8(m_wingetLoader.GetString(resKey)); + } + + // Loader failed to load resource file, print the resource key instead. + return Utility::ConvertToUTF8(resKey); } private: @@ -101,7 +103,7 @@ namespace AppInstaller { // This message cannot be localized. AICLI_LOG(CLI, Error, << "Failure loading resource file with error: " << hre.code()); - throw ResourceOpenException(hre); + m_wingetLoader = nullptr; } } }; From e7c70e6ac3fe91385cbd26708852fa40ae212761 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Tue, 23 Aug 2022 08:18:42 -0700 Subject: [PATCH 42/52] Addressed comment --- src/AppInstallerCLITests/Strings.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp index a4ba08352c..7d682e8bdd 100644 --- a/src/AppInstallerCLITests/Strings.cpp +++ b/src/AppInstallerCLITests/Strings.cpp @@ -240,10 +240,11 @@ TEST_CASE("Join", "[strings]") TEST_CASE("Format", "[strings]") { - REQUIRE("Hello World" == Format("{0} {1}", "Hello", "World")); - REQUIRE("Hello World" == Format("{1} {0}", "World", "Hello")); - REQUIRE("Hello World" == Format("{0} {1}", "Hello", "World", "(Extra", "Input", "Ignored)")); + REQUIRE("First Second" == Format("{0} {1}", "First", "Second")); + REQUIRE("First Second" == Format("{1} {0}", "Second", "First")); + REQUIRE("First Second" == Format("{0} {1}", "First", "Second", "(Extra", "Input", "Ignored)")); + REQUIRE("First Second First Second" == Format("{0} {1} {0} {1}", "First", "Second")); // Note: C++20 std::format will throw an exception for this test case - REQUIRE("Hello {1}" == Format("{0} {1}", "Hello")); + REQUIRE("First {1}" == Format("{0} {1}", "First")); } From 192830e9e178251e44b7d4924ca7aa3684759a62 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Mon, 14 Nov 2022 00:38:47 -0800 Subject: [PATCH 43/52] Fixed build --- src/AppInstallerCLICore/ChannelStreams.h | 1 - .../AppInstallerCommonCore.vcxproj | 1 - .../AppInstallerCommonCore.vcxproj.filters | 3 -- .../Public/winget/LocalizationFormat.h | 44 ------------------- .../Public/winget/Resources.h | 37 ++++++++++++++++ 5 files changed, 37 insertions(+), 49 deletions(-) delete mode 100644 src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index 7efcb44e2a..7c0bbc7493 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -4,7 +4,6 @@ #include "Resources.h" #include "VTSupport.h" #include -#include #include #include diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj index 24a8e92623..ddcdb11413 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -307,7 +307,6 @@ - diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters index 7ca8728079..860a24bc2e 100644 --- a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters +++ b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -213,9 +213,6 @@ Header Files - - Public\winget - diff --git a/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h b/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h deleted file mode 100644 index 163d193644..0000000000 --- a/src/AppInstallerCommonCore/Public/winget/LocalizationFormat.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include "Resources.h" - -namespace AppInstaller::StringResource -{ - namespace details - { - // List of approved types for output, others are potentially not localized. - template - struct IsApprovedForOutput - { - static constexpr bool value = std::is_arithmetic::value; - }; - -#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) \ - template <> \ - struct IsApprovedForOutput<_t_> \ - { \ - static constexpr bool value = true; \ - } - - // It is assumed that single char values need not be localized, as they are matched - // ordinally or they are punctuation / other. - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(char); - // Localized strings (and from an Id for one for convenience). - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(StringId); - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Resource::LocString); - // Strings explicitly declared as localization independent. - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndView); - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndString); - // Normalized strings come from user data and should therefore already by localized - // by how they are chosen (or there is no localized version). - WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::NormalizedString); - } - - template - Utility::LocIndString StringId::operator()(T ... args) const - { - static_assert((details::IsApprovedForOutput>::value && ...), "This type may not be localized, see comment for more information"); - return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; - } -} diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 257b035a68..93bb8c8a8b 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -82,5 +82,42 @@ namespace AppInstaller LocString& operator=(LocString&&) = default; }; } + + namespace details + { + // List of approved types for output, others are potentially not localized. + template + struct IsApprovedForOutput + { + static constexpr bool value = std::is_arithmetic::value; + }; + +#define WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(_t_) \ + template <> \ + struct IsApprovedForOutput<_t_> \ + { \ + static constexpr bool value = true; \ + } + + // It is assumed that single char values need not be localized, as they are matched + // ordinally or they are punctuation / other. + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(char); + // Localized strings (and from an Id for one for convenience). + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(StringResource::StringId); + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Resource::LocString); + // Strings explicitly declared as localization independent. + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndView); + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::LocIndString); + // Normalized strings come from user data and should therefore already by localized + // by how they are chosen (or there is no localized version). + WINGET_CREATE_ISAPPROVEDFOROUTPUT_SPECIALIZATION(Utility::NormalizedString); + } + + template + Utility::LocIndString StringResource::StringId::operator()(T ... args) const + { + static_assert((details::IsApprovedForOutput>::value && ...), "This type may not be localized, see comment for more information"); + return Utility::LocIndString{ Utility::Format(Resolve(), std::forward(args)...) }; + } } From a66d057ed4cc9dbe3a6a21532df7c4d28d727f46 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 18 Nov 2022 13:52:39 -0800 Subject: [PATCH 44/52] Update string resource --- src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp index 2c56cec013..83e543588b 100644 --- a/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ArchiveFlow.cpp @@ -90,7 +90,9 @@ namespace AppInstaller::CLI::Workflow else if (!std::filesystem::exists(nestedInstallerPath)) { AICLI_LOG(CLI, Error, << "Unable to locate nested installer at: " << nestedInstallerPath); - context.Reporter.Error() << Resource::String::NestedInstallerNotFound << ' ' << nestedInstallerPath << std::endl; + context.Reporter.Error() + << Resource::String::NestedInstallerNotFound(Utility::LocIndView{ nestedInstallerPath.u8string() }) + << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NESTEDINSTALLER_NOT_FOUND); } else if (!IsPortableType(installer.NestedInstallerType)) From cb9bc9f97e309ec5721b6c65ee900c3901851e87 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 18 Nov 2022 14:39:40 -0800 Subject: [PATCH 45/52] Revert minor code change --- src/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index c075813c47..10104179c9 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -855,8 +855,17 @@ namespace AppInstaller::CLI::Workflow if (!manifest) { - auto versionInfo = Utility::Format(m_channel.empty() ? "{0}{1}" : "{0}[{1}]", m_version, m_channel); - context.Reporter.Error() << Resource::String::GetManifestResultVersionNotFound(Utility::LocIndView{ versionInfo }) << std::endl; + std::ostringstream ssVersionInfo; + if (!m_version.empty()) + { + ssVersionInfo << m_version; + } + if (!m_channel.empty()) + { + ssVersionInfo << '[' << m_channel << ']'; + } + + context.Reporter.Error() << Resource::String::GetManifestResultVersionNotFound(Utility::LocIndView{ ssVersionInfo.str()}) << std::endl; AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_NO_MANIFEST_FOUND); } From 8b14f8da2cc6d49e91e365503768cdd968c096ad Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Fri, 9 Dec 2022 13:10:15 -0800 Subject: [PATCH 46/52] Comment typo --- src/AppInstallerCommonCore/Resources.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 625264ef10..98cf32cbdd 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -71,7 +71,7 @@ namespace AppInstaller return instance; } - // Gets the the string resource value. + // Gets the string resource value. std::string ResolveString(std::wstring_view resKey) const { if (m_wingetLoader) From 01b39b7bc67fbb139e4d936025fa83d7d03c4520 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 14 Dec 2022 11:20:08 -0800 Subject: [PATCH 47/52] Addressed comments --- src/AppInstallerCLICore/ChannelStreams.h | 3 ++- src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw | 4 ++-- src/AppInstallerCommonCore/Public/winget/Resources.h | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index 7c0bbc7493..a6ceb4220f 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -72,7 +72,8 @@ namespace AppInstaller::CLI::Execution // * If your string came from outside of the source code, it is best to store it in a // Utility::NormalizedString so that it has a normalized representation. This also // informs the output that there is no localized version to use. - // TODO: Convert the rest of the code base and uncomment to enforce localization. + // TODO: This assertion is currently only applied to placceholders in localized strings. + // Convert the rest of the code base and uncomment to enforce localization. //static_assert(details::IsApprovedForOutput>::value, "This type may not be localized, see comment for more information"); if (m_enabled) { diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 2d6df42a6a..c94813fea3 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -760,7 +760,7 @@ They can be configured through the settings file 'winget settings'. Logs - Label displayed for a diagnostic files containing information about the application use. + Label displayed for diagnostic files containing information about the application use. The installer is blocked by policy @@ -1167,7 +1167,7 @@ Do you agree to the terms? The optional header is not applicable without specifying a source: '{0}' - Error message displayed when the user performs an operation (e.g install) and provide the 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. + Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Release Date: diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index 146d750c95..f40d49cd32 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -27,9 +27,10 @@ namespace AppInstaller Utility::LocIndString operator()(T ... args) const; friend std::ostream& operator<<(std::ostream& out, StringId si); - private: - // Resolve the string id. + private: + // Resolve the string ID to its corresponding localized string + // without replacing placeholders. std::string Resolve() const; }; From ff660494b27fbe6b7110eff883872db38fe10477 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 14 Dec 2022 13:30:16 -0800 Subject: [PATCH 48/52] Addressed comments --- src/AppInstallerCLICore/Command.cpp | 2 +- src/AppInstallerCLICore/Workflows/WorkflowBase.cpp | 8 ++++---- src/AppInstallerCLICore/Workflows/WorkflowBase.h | 2 +- src/AppInstallerCommonCore/Public/winget/Resources.h | 6 ++++-- src/AppInstallerCommonCore/Resources.cpp | 4 ++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp index 2d8a56743e..9bf983bcc6 100644 --- a/src/AppInstallerCLICore/Command.cpp +++ b/src/AppInstallerCLICore/Command.cpp @@ -80,7 +80,7 @@ namespace AppInstaller::CLI } // Output the command preamble and command chain - infoOut << Resource::String::Usage("winget"_lis, Utility::LocIndView{ commandChain }); + infoOut << Resource::String::Usage("winget"_liv, Utility::LocIndView{ commandChain }); auto commandAliases = Aliases(); auto commands = GetVisibleCommands(); diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp index 10104179c9..cb3db051ce 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -57,7 +57,7 @@ namespace AppInstaller::CLI::Workflow out << std::endl; } - Repository::Source OpenNamedSource(Execution::Context& context, std::string_view sourceName) + Repository::Source OpenNamedSource(Execution::Context& context, Utility::LocIndView sourceName) { Repository::Source source; @@ -72,7 +72,7 @@ namespace AppInstaller::CLI::Workflow if (!sourceName.empty() && !sources.empty()) { // A bad name was given, try to help. - context.Reporter.Error() << Resource::String::OpenSourceFailedNoMatch(Utility::LocIndView{ sourceName }) << std::endl; + context.Reporter.Error() << Resource::String::OpenSourceFailedNoMatch(sourceName) << std::endl; context.Reporter.Info() << Resource::String::OpenSourceFailedNoMatchHelp << std::endl; for (const auto& details : sources) { @@ -260,7 +260,7 @@ namespace AppInstaller::CLI::Workflow { auto policy = Settings::TogglePolicy::GetPolicy(e.Policy()); auto policyNameId = policy.PolicyName(); - context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policyNameId()) << std::endl; + context.Reporter.Error() << Resource::String::DisabledByGroupPolicy(policyNameId) << std::endl; return APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY; } catch (const std::exception& e) @@ -301,7 +301,7 @@ namespace AppInstaller::CLI::Workflow } } - auto source = OpenNamedSource(context, sourceName); + auto source = OpenNamedSource(context, Utility::LocIndView{ sourceName }); if (context.IsTerminated()) { return; diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h index 1434b19dfd..7316022ebf 100644 --- a/src/AppInstallerCLICore/Workflows/WorkflowBase.h +++ b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -85,7 +85,7 @@ namespace AppInstaller::CLI::Workflow void operator()(Execution::Context& context) const override; private: - std::string_view m_sourceName; + Utility::LocIndView m_sourceName; }; // Creates a source object for a predefined source. diff --git a/src/AppInstallerCommonCore/Public/winget/Resources.h b/src/AppInstallerCommonCore/Public/winget/Resources.h index f40d49cd32..3c3c2e53d1 100644 --- a/src/AppInstallerCommonCore/Public/winget/Resources.h +++ b/src/AppInstallerCommonCore/Public/winget/Resources.h @@ -23,17 +23,19 @@ namespace AppInstaller explicit constexpr StringId(std::wstring_view id) : std::wstring_view(id) {} // Sets the placeholder values in the resolved string id. + // Example: out << myStringId(placeholderVal1, placeholderVal2, ...) template Utility::LocIndString operator()(T ... args) const; - friend std::ostream& operator<<(std::ostream& out, StringId si); - private: // Resolve the string ID to its corresponding localized string // without replacing placeholders. std::string Resolve() const; }; + // Output resource identifier as localized string. + std::ostream& operator<<(std::ostream& out, StringId si); + // Resource string identifiers. struct String { diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 98cf32cbdd..23fcc49672 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -92,9 +92,9 @@ namespace AppInstaller { // The default constructor of ResourceLoader throws a winrt::hresult_error exception // when resource.pri is not found. ResourceLoader::GetForViewIndependentUse also throws - // a winrt::hresult_error but for reasons unknown it only gets catch when running on the + // a winrt::hresult_error but for reasons unknown it only gets caught when running on the // debugger. Running without a debugger will result in a crash that not even adding a - // catch all fix. To provide a good error message we call the default constructor + // catch all will fix. To provide a good error message we call the default constructor // before calling GetForViewIndependentUse. m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader(); m_wingetLoader = winrt::Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse(L"winget"); From 55da0a381bfdbd6d4e729a04dd32d4ae292d60ad Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 14 Dec 2022 14:59:04 -0800 Subject: [PATCH 49/52] Addressed comments --- src/AppInstallerCLITests/Completion.cpp | 34 ++++++++++++------------- src/AppInstallerCLITests/WorkFlow.cpp | 4 ++- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/AppInstallerCLITests/Completion.cpp b/src/AppInstallerCLITests/Completion.cpp index e80ae27683..2af3d1d223 100644 --- a/src/AppInstallerCLITests/Completion.cpp +++ b/src/AppInstallerCLITests/Completion.cpp @@ -233,7 +233,7 @@ struct CompletionTestCommand : public Command using Command::Complete; - void Complete(CLI::Execution::Context& context, CLI::Execution::Args::Type valueType) const override + void Complete(Execution::Context& context, Execution::Args::Type valueType) const override { if (ArgumentValueCallback) { @@ -243,7 +243,7 @@ struct CompletionTestCommand : public Command std::vector SubCommandNames; std::vector Arguments; - std::function ArgumentValueCallback; + std::function ArgumentValueCallback; }; struct CompletionTestContext @@ -251,7 +251,7 @@ struct CompletionTestContext CompletionTestContext(std::string_view word, std::string_view commandLine, std::string_view position) : context(out, in) { - context.Reporter.SetChannel(CLI::Execution::Reporter::Channel::Completion); + context.Reporter.SetChannel(Execution::Reporter::Channel::Completion); context.Add(CompletionData{ word, commandLine, position }); } @@ -267,7 +267,7 @@ TEST_CASE("CommandComplete_Simple", "[complete]") CompletionTestCommand command; command.SubCommandNames = { "test1", "test2" }; command.Arguments = { Argument{ "arg1", 'a', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard } }; - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -284,7 +284,7 @@ TEST_CASE("CommandComplete_PartialCommandMatch", "[complete]") CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -301,7 +301,7 @@ TEST_CASE("CommandComplete_CommandsNotAllowed", "[complete]") CompletionTestCommand command; command.SubCommandNames = { "car", "cart", "cartesian", "carpet" }; - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -322,7 +322,7 @@ TEST_CASE("CommandComplete_Routing1", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -343,7 +343,7 @@ TEST_CASE("CommandComplete_Routing2", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -364,7 +364,7 @@ TEST_CASE("CommandComplete_PositionalRouting", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -387,7 +387,7 @@ TEST_CASE("CommandComplete_PositionalRoutingAfterArgs", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -409,7 +409,7 @@ TEST_CASE("CommandComplete_PositionalRoutingAfterDoubleDash", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -429,7 +429,7 @@ TEST_CASE("CommandComplete_ArgNamesAfterDash", "[complete]") Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -449,7 +449,7 @@ TEST_CASE("CommandComplete_AliasNames", "[complete]") Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -470,7 +470,7 @@ TEST_CASE("CommandComplete_ArgNamesFilter", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, Argument{ "foo1", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Positional }, }; - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -491,7 +491,7 @@ TEST_CASE("CommandComplete_IgnoreBadArgs", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values @@ -511,7 +511,7 @@ TEST_CASE("CommandComplete_OtherArgsParsed", "[complete]") Argument{ "arg1", '1', Args::Type::Query, CLI::Resource::String::Done, ArgumentType::Standard }, Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type) { FAIL("No argument value should be requested"); }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type) { FAIL("No argument value should be requested"); }; command.Complete(ctc.context); // Create expected values @@ -536,7 +536,7 @@ TEST_CASE("CommandComplete_Complex", "[complete]") Argument{ "arg2", '2', Args::Type::Channel, CLI::Resource::String::Done, ArgumentType::Standard }, }; Args::Type argType = static_cast(-1); - command.ArgumentValueCallback = [&](Context&, CLI::Execution::Args::Type type) { argType = type; }; + command.ArgumentValueCallback = [&](Context&, Execution::Args::Type type) { argType = type; }; command.Complete(ctc.context); // Create expected values diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index de7a61308d..21ca2d3c6b 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -1191,7 +1191,9 @@ TEST_CASE("InstallFlow_Zip_BadRelativePath", "[InstallFlow][workflow]") // Verify Installer was not called REQUIRE(!std::filesystem::exists(installResultPath.GetPath())); - REQUIRE(installOutput.str().find(Resource::LocString(Resource::String::NestedInstallerNotFound(""_liv)).get()) != std::string::npos); + auto relativePath = context.Get().parent_path() / "extracted" / "relativeFilePath"; + auto expectedMessage = Resource::String::NestedInstallerNotFound(AppInstaller::Utility::LocIndString{ relativePath.u8string()}); + REQUIRE(installOutput.str().find(Resource::LocString(expectedMessage).get()) != std::string::npos); } TEST_CASE("InstallFlow_Zip_MissingNestedInstaller", "[InstallFlow][workflow]") From a192d9fc9817bfbd3475acb51f1468119f93db34 Mon Sep 17 00:00:00 2001 From: Amir El Bawab <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 14 Dec 2022 16:04:12 -0800 Subject: [PATCH 50/52] Addressed comments --- .../Commands/SettingsCommand.cpp | 15 ++++++++++++--- .../Workflows/MSStoreInstallerHandler.cpp | 17 +++++++++-------- src/AppInstallerCommonCore/Resources.cpp | 3 +-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp index 09f0857f0e..852d437229 100644 --- a/src/AppInstallerCLICore/Commands/SettingsCommand.cpp +++ b/src/AppInstallerCLICore/Commands/SettingsCommand.cpp @@ -16,7 +16,6 @@ namespace AppInstaller::CLI constexpr Utility::LocIndView s_ArgumentName_Enable = "enable"_liv; constexpr Utility::LocIndView s_ArgumentName_Disable = "disable"_liv; constexpr Utility::LocIndView s_ArgName_EnableAndDisable = "enable|disable"_liv; - constexpr Utility::LocIndView s_ArgValue_EnableAndDisable_LocalManifestFiles = "LocalManifestFiles"_liv; static constexpr std::string_view s_SettingsCommand_HelpLink = "https://aka.ms/winget-settings"sv; } @@ -57,14 +56,24 @@ namespace AppInstaller::CLI throw CommandException(Resource::String::TooManyAdminSettingArgumentsError(s_ArgName_EnableAndDisable)); } + // Get admin setting string for all available options except Unknown + using AdminSetting_t = std::underlying_type_t; + std::vector adminSettingList; + for (AdminSetting_t i = 1 + static_cast(AdminSetting::Unknown); i < static_cast(AdminSetting::Max); ++i) + { + adminSettingList.emplace_back(AdminSettingToString(static_cast(i))); + } + + Utility::LocIndString validOptions = Join(", "_liv, adminSettingList); + if (execArgs.Contains(Execution::Args::Type::AdminSettingEnable) && AdminSetting::Unknown == StringToAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingEnable))) { - throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Enable, s_ArgValue_EnableAndDisable_LocalManifestFiles)); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Enable, validOptions)); } if (execArgs.Contains(Execution::Args::Type::AdminSettingDisable) && AdminSetting::Unknown == StringToAdminSetting(execArgs.GetArg(Execution::Args::Type::AdminSettingDisable))) { - throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Disable, s_ArgValue_EnableAndDisable_LocalManifestFiles)); + throw CommandException(Resource::String::InvalidArgumentValueError(s_ArgumentName_Disable, validOptions)); } } diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp index 5465341317..0f8f2100bd 100644 --- a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp +++ b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp @@ -112,6 +112,13 @@ namespace AppInstaller::CLI::Workflow } } + Utility::LocIndString GetErrorCodeString(const HRESULT errorCode) + { + std::ostringstream ssError; + ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); + return Utility::LocIndString{ ssError.str() }; + } + void MSStoreInstall(Execution::Context& context) { auto productId = Utility::ConvertToUTF16(context.Get()->ProductId); @@ -148,10 +155,7 @@ namespace AppInstaller::CLI::Workflow } else { - std::ostringstream ssError; - ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); - auto errorCodeString = Utility::LocIndString{ ssError.str() }; - + auto errorCodeString = GetErrorCodeString(errorCode); context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(errorCodeString) << std::endl; context.Add(errorCode); AICLI_LOG(CLI, Error, << "MSStore install failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << errorCodeString); @@ -200,10 +204,7 @@ namespace AppInstaller::CLI::Workflow } else { - std::ostringstream ssError; - ssError << WINGET_OSTREAM_FORMAT_HRESULT(errorCode); - auto errorCodeString = Utility::LocIndString{ ssError.str() }; - + auto errorCodeString = GetErrorCodeString(errorCode); context.Reporter.Info() << Resource::String::MSStoreInstallOrUpdateFailed(errorCodeString) << std::endl; context.Add(errorCode); AICLI_LOG(CLI, Error, << "MSStore execution failed. ProductId: " << Utility::ConvertToUTF8(productId) << " HResult: " << errorCodeString); diff --git a/src/AppInstallerCommonCore/Resources.cpp b/src/AppInstallerCommonCore/Resources.cpp index 23fcc49672..d4072efa90 100644 --- a/src/AppInstallerCommonCore/Resources.cpp +++ b/src/AppInstallerCommonCore/Resources.cpp @@ -61,9 +61,8 @@ namespace AppInstaller } // Utility class to load resources - class Loader + struct Loader { - public: // Gets the singleton instance of the resource loader. static const Loader& Instance() { From fcb963b96d24ac8e6c209bdce7833a4eb8ff1f16 Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 14 Dec 2022 18:42:34 -0800 Subject: [PATCH 51/52] Addressed comments --- src/AppInstallerCLICore/ChannelStreams.h | 2 +- .../Shared/Strings/en-us/winget.resw | 117 +++++++++--------- 2 files changed, 60 insertions(+), 59 deletions(-) diff --git a/src/AppInstallerCLICore/ChannelStreams.h b/src/AppInstallerCLICore/ChannelStreams.h index a6ceb4220f..64816786de 100644 --- a/src/AppInstallerCLICore/ChannelStreams.h +++ b/src/AppInstallerCLICore/ChannelStreams.h @@ -72,7 +72,7 @@ namespace AppInstaller::CLI::Execution // * If your string came from outside of the source code, it is best to store it in a // Utility::NormalizedString so that it has a normalized representation. This also // informs the output that there is no localized version to use. - // TODO: This assertion is currently only applied to placceholders in localized strings. + // TODO: This assertion is currently only applied to placeholders in localized strings. // Convert the rest of the code base and uncomment to enforce localization. //static_assert(details::IsApprovedForOutput>::value, "This type may not be localized, see comment for more information"); if (m_enabled) diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 3d6b58be22..87fa4df675 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -119,11 +119,11 @@ Adjoined alias is not a flag: '{0}' - Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). + {Locked="{0}"} Error message displayed when the user provides an adjoined alias that is not a flag argument. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). Adjoined flag alias not found: '{0}' - Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). + {Locked="{0}"} Error message displayed when the user provides an adjoined flag alias argument that was not found. {0} is a placeholder replaced by the user input argument (e.g. '-ab'). The following arguments are available: @@ -151,7 +151,7 @@ {0} upgrades available. - Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. + {Locked="{0}"} Message displayed to inform the user about available package upgrades. {0} is a placeholder replaced by the number of package upgrades. Use the specified channel; default is general audience @@ -199,11 +199,11 @@ Found a positional argument when none was expected: '{0}' - Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. + {Locked="{0}"} Error message displayed when the user provides an extra positional argument when none was expected. {0} is a placeholder replaced by the user's extra argument input. This feature is a work in progress, and may be changed dramatically or removed altogether in the future. To enable it, edit your settings ('winget settings') to include the experimental feature: '{0}' - {Locked="winget settings"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. + {Locked="winget settings","{0}"}. Error message displayed when the user uses an experimental feature that is disabled. {0} is a placeholder replaced by the experimental feature name. Shows the status of experimental features. Experimental features can be turn on via 'winget settings'. @@ -240,7 +240,7 @@ They can be configured through the settings file 'winget settings'. Flag argument cannot contain adjoined value: '{0}' - Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. + {Locked="{0}"} Error message displayed when the user provides a flag argument containing an unexpected adjoined value. {0} is a placeholder replaced by the user input. Computes the hash of a local file, appropriate for entry into a manifest. It can also compute the hash of the signature file of an MSIX package to enable streaming installations. @@ -256,7 +256,7 @@ They can be configured through the settings file 'winget settings'. More help can be found at: {0} - Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. + {Locked="{0}"} Message displayed to inform the user about a link where they can learn more about the subject context. {0} is a placeholder replaced by a website address. Filter results by id @@ -306,15 +306,15 @@ They can be configured through the settings file 'winget settings'. Argument alias was not recognized for the current command: '{0}' - Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). + {Locked="{0}"} Error message displayed when the user provides a command line argument alias that was not recognized for a selected command. {0} is a placeholder replaced by the user's argument alias input (e.g. '-a'). Invalid argument specifier: '{0}' - Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). + {Locked="{0}"} Error message displayed when the user provides an invalid argument specifier. {0} is a placeholder replaced by an argument specifier (e.g. '-'). Argument name was not recognized for the current command: '{0}' - Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). + {Locked="{0}"} Error message displayed when the user provides an unrecognized command line argument name for the selected command. {0} is a placeholder replaced by the user's argument name input (e.g. '--example'). Locale to use (BCP47 format) @@ -361,7 +361,7 @@ They can be configured through the settings file 'winget settings'. Argument value required, but none found: '{0}' - Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. + {Locked="{0}"} Error message displayed when the user does not provide a required command line argument value. {0} is a placeholder replaced by the argument name. Filter results by moniker @@ -377,7 +377,7 @@ They can be configured through the settings file 'winget settings'. Failed to install or upgrade Microsoft Store package. Error code: {0} - Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. + {Locked="{0}"} Error message displayed when a Microsoft Store application package fails to install or upgrade. {0} is a placeholder replaced by an error code. Verifying/Requesting package acquisition failed: network error @@ -434,6 +434,7 @@ They can be configured through the settings file 'winget settings'. Package + A software package Oops, we forgot to do this... @@ -452,7 +453,7 @@ They can be configured through the settings file 'winget settings'. Required argument not provided: '{0}' - Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. + {Locked="{0}"} Error message displayed when the user does not provide a required command line argument. {0} is a placeholder replaced by an argument name. Progress display as the default color @@ -514,7 +515,7 @@ They can be configured through the settings file 'winget settings'. Only the single character alias can occur after a single -: '{0}' - Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. + {Locked="{0}"} Error message displayed when the user provides more than a single character command line alias argument after an alias argument specifier '-'. {0} is a placeholder replaced by the user's argument input. A source with the given name already exists and refers to a different location: @@ -617,7 +618,7 @@ They can be configured through the settings file 'winget settings'. Removing source: {0}... - Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being removed. {0} is a placeholder replaced by the repository source name. Resetting all sources... @@ -637,7 +638,7 @@ They can be configured through the settings file 'winget settings'. Resetting source: {0}... - Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Message displayed to inform the user about a repository source that is currently being reset. {0} is a placeholder replaced by the repository source name. Type of the source @@ -653,7 +654,7 @@ They can be configured through the settings file 'winget settings'. Updating source: {0}... - Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Message displayed to inform the user about a registered repository source that is currently being updated. {0} is a placeholder replaced by the repository source name. Filter results by tag @@ -675,18 +676,18 @@ They can be configured through the settings file 'winget settings'. Argument provided more times than allowed: '{0}' - Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. + {Locked="{0}"} Error message displayed when the user provides a command line argument more times than it is allowed. {0} is a placeholder replaced by the user's argument name input. More than one execution behavior argument provided: '{0}' - Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). + {Locked="{0}"} Error message displayed when the user provides more than one execution behavior argument when installing an application package. {0} is a placeholder replaced by the user specified execution behaviors (e.g. 'silent|interactive'). An unexpected error occurred while executing the command: Unrecognized command: '{0}' - Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. + {Locked="{0}"} Error message displayed when the user provides an unrecognized command. {0} is a placeholder replaced by the user input. Upgrade all installed packages to latest if available @@ -703,7 +704,7 @@ They can be configured through the settings file 'winget settings'. usage: {0} {1} - Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. + {Locked="{0}","{1}"} Message displayed to provide the user with instructions on how to use a command. {0} is a placeholder replaced by the program name (e.g. 'winget'). {1} is a placeholder replaced by the pattern for using the selected command. Validates a manifest using a strict set of guidelines. This is intended to enable you to check your manifest before submitting to a repo. @@ -731,11 +732,11 @@ They can be configured through the settings file 'winget settings'. No version found matching: {0} - Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. + {Locked="{0}"} Error message displayed when the user attempts to upgrade an application package to a version that was not found. {0} is a placeholder replaced by the user's provided upgrade package version. No sources match the given value: {0} - Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. + {Locked="{0}"} Error message displayed when the user attempts to install or upgrade an application package from a repository source that was not found. {0} is a placeholder replaced by the user's repository source name input. The configured sources are: @@ -749,11 +750,11 @@ They can be configured through the settings file 'winget settings'. Path is a directory: {0} - Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. + {Locked="{0}"} Error message displayed when the user provides a system path that is a directory. {0} is a placeholder replaced by the provided directory path. File does not exist: {0} - Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. + {Locked="{0}"} Error message displayed when the user provides a system file that does not exist. {0} is a placeholder replaced by the provided file path. Both local manifest and search query arguments are provided @@ -773,7 +774,7 @@ They can be configured through the settings file 'winget settings'. Failed in attempting to update the source: {0} - Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Error message displayed when an attempt to update the repository source fails. {0} is a placeholder replaced by the repository source name. Uninstalls the selected package, either found by searching the installed packages list or directly from a manifest. By default, the query must case-insensitively match the id, name, or moniker of the package. Other fields can be used by passing their appropriate option. @@ -797,7 +798,7 @@ They can be configured through the settings file 'winget settings'. Uninstall failed with exit code: {0} - Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. + {Locked="{0}"} Error message displayed when an attempt to uninstall an application package fails. {0} is a placeholder replaced by an error code. Exports a list of the installed packages @@ -826,19 +827,19 @@ They can be configured through the settings file 'winget settings'. Package not found for import: {0} - Error message displayed when the user attempts to import an application package that was not found. {0} is a placeholder replaced by the import package name . + {Locked="{0}"} Error message displayed when the user attempts to import an application package that was not found. {0} is a placeholder replaced by the import package name . Source required for import is not installed: {0} - Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Error message displayed when the user attempts to import application package(s) from a repository source that is not installed. {0} is a placeholder replaced by the repository source name. Installed package is not available from any source: {0} - Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. + {Locked="{0}"} Warning message displayed when the user attempts to export an installed application package that is not available from any repository source. {0} is a placeholder replaced by the installed package name. Installed version of package is not available from any source: {0} {1} {2} - Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. + {Locked="{0}","{1}","{2}"} Warning message displayed when the user attempts to export an installed application package with a version that is not available from any repository source. {0} is a placeholder replaced by the installed package identifier. {1} is a placeholder replaced by the installed package version. {2} is a placeholder replaced by the installed package channel. No packages found in import file @@ -848,7 +849,7 @@ They can be configured through the settings file 'winget settings'. Package is already installed: {0} - Message displayed to inform the user that an import application package is already installed. {0} is a placeholder replaced by the package identifier. + {Locked="{0}"} Message displayed to inform the user that an import application package is already installed. {0} is a placeholder replaced by the package identifier. Ignore unavailable packages @@ -861,7 +862,7 @@ They can be configured through the settings file 'winget settings'. Path does not exist: {0} - Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. + {Locked="{0}"} Error message displayed when the user provides a system path argument value that does not exist. {0} is a placeholder replaced by the user's provided path. The JSON file does not specify a recognized schema. @@ -872,11 +873,11 @@ They can be configured through the settings file 'winget settings'. The value provided for the `{0}` argument is invalid; valid values are: {1} - Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. + {Locked="{0}","{1}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. {1} is a placeholder replaced by a list of valid options. This operation is disabled by Group Policy: {0} - Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. + {Locked="{0}"} Error message displayed when the user performs a command operation that is disabled by a group policy. {0} is a placeholder replaced by a group policy description. Enable Additional Windows App Installer Sources @@ -926,7 +927,7 @@ They can be configured through the settings file 'winget settings'. Field: {0} - Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. + {Locked="{0}"} Warning message displayed when a user setting field has invalid syntax or semantics. {0} is a placeholder replaced by the setting field path. Invalid field format. @@ -945,7 +946,7 @@ They can be configured through the settings file 'winget settings'. Value: {0} - Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. + {Locked="{0}"} Warning message displayed when a user setting value has invalid syntax or semantics. {0} is a placeholder replaced by the setting data value. The following experimental features are in progress. @@ -973,7 +974,7 @@ Configuration is disabled due to Group Policy. The value provided for the `{0}` argument is invalid - Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. + {Locked="{0}"} Error message displayed when the user provides an invalid command line argument value. {0} is a placeholder replaced by the argument name. Cancelled @@ -997,19 +998,19 @@ Configuration is disabled due to Group Policy. Package search yield more than one result: {0} - Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. + {Locked="{0}"} Error message displayed when application packages search yield more than one result. {0} is a placeholder replaced by the dependency package identifier. Latest version not found for package: {0} - Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. + {Locked="{0}"} Error message displayed when no suitable version found for the specific application package. {0} is a placeholder replaced by the package identifier. No installers found: {0} - Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. + {Locked="{0}"} Error message displayed when no installer found for a manifest. {0} is a placeholder replaced by the manifest identifier. Minimum required version not available for package: {0} - Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. + {Locked="{0}"} Error message displayed when the minimum required version is not available for an application package. {0} is a placeholder replaced by the package identifier. No matches @@ -1021,7 +1022,7 @@ Configuration is disabled due to Group Policy. No suitable installer found for manifest: {0} {1} - Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. + {Locked="{0}","{1}"} Error message displayed when an attempt to get a preferred installer for a manifest fails. {0} is a placeholder replaced by the manifest identifier. {1} is a placeholder replaced by the manifest version. Error processing package dependencies, do you wish to continue installation? @@ -1059,7 +1060,7 @@ Configuration is disabled due to Group Policy. Exported package requires license agreement to install: {0} - Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. + {Locked="{0}"} Warning message displayed when an exported application package requires license agreement to install. {0} is a placeholder replaced by the package name. The publisher requires that you view the above information and accept the agreements before installing. @@ -1145,7 +1146,7 @@ Do you agree to the terms? The `{0}` source requires that you view the following agreements before using. - Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Message displayed to inform the user that a repository source requires viewing agreements before using. {0} is a placeholder replaced by the repository source name. Do you agree to all the source agreements terms? @@ -1167,7 +1168,7 @@ Do you agree to the terms? The optional header is not applicable without specifying a source: '{0}' - Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. + {Locked="{0}"} Error message displayed when the user performs an operation (e.g install) and provides the HTTP 'header' argument without specifying the repository source. {0} is a placeholder replaced by the header argument name. Release Date: @@ -1198,15 +1199,15 @@ Do you agree to the terms? Failed when searching source; results will not be included: {0} - Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Warning message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. Failed when searching source: {0} - Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. + {Locked="{0}"} Error message displayed when searching a repository source fails. {0} is a placeholder replaced by the repository source name. This feature needs to be enabled by administrators. To enable it, run 'winget settings --enable {0}' as administrator. - {Locked="winget settings --enable"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. + {Locked="winget settings --enable", "{0}"}. Error message displayed when the user uses a feature that needs to be enabled by administrators. {0} is a placeholder replaced by the admin setting. Enables the specific administrator setting @@ -1216,7 +1217,7 @@ Do you agree to the terms? Too many admin setting arguments provided: '{0}' - Error message displayed when the user provides too many admin setting arguments. {0} is a placeholder replaced by the admin setting arguments (e.g. 'enable|disable'). + {Locked="{0}"} Error message displayed when the user provides too many admin setting arguments. {0} is a placeholder replaced by the admin setting arguments (e.g. 'enable|disable'). Admin setting enabled. @@ -1283,11 +1284,11 @@ Do you agree to the terms? Installer failed with exit code: {0} - Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. + {Locked="{0}"} Error message displayed when the application installer fails. {0} is a placeholder replaced by an error code. Installer log is available at: {0} - Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. + {Locked="{0}"} Message displayed to inform the user about the system path of a diagnostic files containing information about the installer. {0} is a placeholder replaced by the diagnostic file system path. The following packages were found among the working sources. @@ -1304,7 +1305,7 @@ Please specify one of them using the --source option to proceed. Windows Package Manager v{0} - Label displaying the product name and version. {0} is a placeholder replaced by the product version. + {Locked="{0}"} Label displaying the product name and version. {0} is a placeholder replaced by the product version. Ignore package versions in import file @@ -1320,7 +1321,7 @@ Please specify one of them using the --source option to proceed. Windows Package Manager (Preview) v{0} - Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. + {Locked="{0}"} Label displaying the preview product name and pre-release version. {0} is a placeholder replaced by the product version. Select the architecture to install @@ -1393,7 +1394,7 @@ Please specify one of them using the --source option to proceed. Overwriting existing file: {0} - Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. + {Locked="{0}"} Warning message displayed to inform the user that an existing file is being overwritten. {0} is a placeholder replaced by the file system path. No package selection argument was provided; see the help for details about finding a package. @@ -1418,7 +1419,7 @@ Please specify one of them using the --source option to proceed. Files remain in install directory: {0} - Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. + {Locked="{0}"} Warning message displayed when files remain in install directory. {0} is a placeholder replaced by the directory path. Purging install directory... @@ -1434,7 +1435,7 @@ Please specify one of them using the --source option to proceed. Notes: {0} - Label displayed for installation notes. {0} is a placeholder replaced by installation notes. + {Locked="{0}"} Label displayed for installation notes. {0} is a placeholder replaced by installation notes. Installation Notes: @@ -1447,7 +1448,7 @@ Please specify one of them using the --source option to proceed. Nested installer file does not exist. Ensure the specified relative path of the nested installer matches: {0} - Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. + {Locked="{0}"} Error message displayed when nested installer file does not exist. {0} is a placeholder replaced by the nested installer file path. Invalid relative file path to the nested installer; path points to a location outside of the install directory From e1eb8036f82b09c88b368d69921be3c45c55873e Mon Sep 17 00:00:00 2001 From: AmirMS <104940545+AmelBawa-msft@users.noreply.github.com> Date: Wed, 14 Dec 2022 21:43:18 -0800 Subject: [PATCH 52/52] Updated root command labels --- src/AppInstallerCLICore/Commands/RootCommand.cpp | 8 ++++---- .../Shared/Strings/en-us/winget.resw | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/AppInstallerCLICore/Commands/RootCommand.cpp b/src/AppInstallerCLICore/Commands/RootCommand.cpp index c9864bc1f6..f0cc143f8c 100644 --- a/src/AppInstallerCLICore/Commands/RootCommand.cpp +++ b/src/AppInstallerCLICore/Commands/RootCommand.cpp @@ -185,15 +185,15 @@ namespace AppInstaller::CLI info << std::endl << "Windows: "_liv << Runtime::GetOSVersion() << std::endl; - info << Resource::String::SystemArchitecture << ": "_liv << Utility::ToString(Utility::GetSystemArchitecture()) << std::endl; + info << Resource::String::SystemArchitecture(Utility::ToString(Utility::GetSystemArchitecture())) << std::endl; if (Runtime::IsRunningInPackagedContext()) { - info << Resource::String::Package << ": "_liv << Runtime::GetPackageVersion() << std::endl; + info << Resource::String::Package(Runtime::GetPackageVersion()) << std::endl; }; - info << std::endl << Resource::String::Logs << ": "_liv << Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() << std::endl; - info << std::endl << Resource::String::UserSettings << ": "_liv << Runtime::GetPathTo(Runtime::PathName::UserSettingsFileLocationForDisplay).u8string() << std::endl; + info << std::endl << Resource::String::Logs(Utility::LocIndView{ Runtime::GetPathTo(Runtime::PathName::DefaultLogLocationForDisplay).u8string() }) << std::endl; + info << std::endl << Resource::String::UserSettings(Utility::LocIndView{ Runtime::GetPathTo(Runtime::PathName::UserSettingsFileLocationForDisplay).u8string() }) << std::endl; info << std::endl; diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 87fa4df675..1b9b215a2f 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -433,8 +433,8 @@ They can be configured through the settings file 'winget settings'. Override arguments to be passed on to the installer - Package - A software package + Package: {0} + {Locked="{0}"} Label displayed for a software package. {0} is a placeholder replaced by the software package name. Oops, we forgot to do this... @@ -760,8 +760,8 @@ They can be configured through the settings file 'winget settings'. Both local manifest and search query arguments are provided - Logs - Label displayed for diagnostic files containing information about the application use. + Logs: {0} + {Locked="{0}"} Label displayed for diagnostic files containing information about the application use. {0} is a placeholder replaced by the logs directory path. The installer is blocked by policy @@ -1353,7 +1353,8 @@ Please specify one of them using the --source option to proceed. The arguments provided can only be used with a query. - System Architecture + System Architecture: {0} + {Locked="{0}"} Label displayed for the system architecture. {0} is a placeholder replaced by the value of the system architecture (e.g. X64). Retains all files and directories created by the package (portable) @@ -1588,6 +1589,7 @@ Please specify one of them using the --source option to proceed. Export settings - User Settings + User Settings: {0} + {Locked="{0}"} Label displayed for the file containing the user settings. {0} is a placeholder replaced by the user settings file path. \ No newline at end of file