winget-cli

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

commit 5bdc55ea4aa66877694133c96763fba46381e1dd
parent 7d86fc0a229383a131d834009084e81604024a7e
Author: Kaleb Luedtke <jluedtk@jci.com>
Date:   Thu, 16 Jun 2022 12:28:12 -0500

Enable long aliases (#2107)

This change allows for argument aliases to be more than 1 character
Diffstat:
M.github/actions/spelling/allow.txt | 1+
Msrc/AppInstallerCLICore/Argument.cpp | 8++++----
Msrc/AppInstallerCLICore/Argument.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLICore/Command.cpp | 11+++++++++--
Msrc/AppInstallerCLITests/Command.cpp | 46+++++++++++++++++++++++++++++++++++++++++++---
5 files changed, 104 insertions(+), 9 deletions(-)

diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt @@ -58,6 +58,7 @@ cla CLASSNOTAVAILABLE CLSCTX cmake +cmd cmdlet cmdlets cmp diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp @@ -28,7 +28,7 @@ namespace AppInstaller::CLI case Args::Type::Tag: return Argument{ "tag", NoAlias, Args::Type::Tag, Resource::String::TagArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Command: - return Argument{ "command", NoAlias, Args::Type::Command, Resource::String::CommandArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; + return Argument{ "command", NoAlias, "cmd", Args::Type::Command, Resource::String::CommandArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::Source: return Argument{ "source", 's', Args::Type::Source, Resource::String::SourceArgumentDescription, ArgumentType::Standard }; case Args::Type::DependencySource: @@ -54,9 +54,9 @@ namespace AppInstaller::CLI case Args::Type::InstallLocation: return Argument{ "location", 'l', Args::Type::InstallLocation, Resource::String::LocationArgumentDescription, ArgumentType::Standard }; case Args::Type::HashOverride: - return Argument{ "force", Argument::NoAlias, Args::Type::HashOverride, Resource::String::InstallForceArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::HashOverride }; + return Argument{ "force", NoAlias, Args::Type::HashOverride, Resource::String::InstallForceArgumentDescription, ArgumentType::Flag, Settings::TogglePolicy::Policy::HashOverride }; case Args::Type::AcceptPackageAgreements: - return Argument{ "accept-package-agreements", Argument::NoAlias, Args::Type::AcceptPackageAgreements, Resource::String::AcceptPackageAgreementsArgumentDescription, ArgumentType::Flag }; + return Argument{ "accept-package-agreements", NoAlias, Args::Type::AcceptPackageAgreements, Resource::String::AcceptPackageAgreementsArgumentDescription, ArgumentType::Flag }; case Args::Type::HashFile: return Argument{ "file", 'f', Args::Type::HashFile, Resource::String::FileArgumentDescription, ArgumentType::Positional, true }; case Args::Type::Msix: @@ -80,7 +80,7 @@ namespace AppInstaller::CLI case Args::Type::RetroStyle: return Argument{ "retro", NoAlias, Args::Type::RetroStyle, Resource::String::RetroArgumentDescription, ArgumentType::Flag, Argument::Visibility::Hidden }; case Args::Type::VerboseLogs: - return Argument{ "verbose-logs", NoAlias, Args::Type::VerboseLogs, Resource::String::VerboseLogsArgumentDescription, ArgumentType::Flag }; + return Argument{ "verbose-logs", NoAlias, "verbose", Args::Type::VerboseLogs, Resource::String::VerboseLogsArgumentDescription, ArgumentType::Flag}; case Args::Type::CustomHeader: return Argument{ "header", NoAlias, Args::Type::CustomHeader, Resource::String::HeaderArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help }; case Args::Type::AcceptSourceAgreements: diff --git a/src/AppInstallerCLICore/Argument.h b/src/AppInstallerCLICore/Argument.h @@ -49,6 +49,9 @@ namespace AppInstaller::CLI // Defines an argument with no alias. constexpr static char NoAlias = '\0'; + // Defines an argument with no alternate name + constexpr static std::string_view NoAlternateName = ""; + Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc) : m_name(name), m_alias(alias), m_execArgType(execArgType), m_desc(std::move(desc)) {} @@ -67,6 +70,24 @@ namespace AppInstaller::CLI Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, bool required) : m_name(name), m_alias(alias), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_required(required) {} + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, bool required) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_required(required) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, bool required) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_required(required) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, bool required) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_required(required) {} + ~Argument() = default; Argument(const Argument&) = default; @@ -89,6 +110,7 @@ namespace AppInstaller::CLI // Arguments are not localized at this time. Utility::LocIndView Name() const { return Utility::LocIndView{ m_name }; } char Alias() const { return m_alias; } + std::string_view AlternateName() const { return m_alternateName; } Execution::Args::Type ExecArgType() const { return m_execArgType; } const Resource::StringId& Description() const { return m_desc; } bool Required() const { return m_required; } @@ -128,8 +150,33 @@ namespace AppInstaller::CLI Argument(std::string_view name, char alias, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, Settings::TogglePolicy::Policy groupPolicy, Settings::AdminSetting adminSetting) : m_name(name), m_alias(alias), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, Settings::ExperimentalFeature::Feature feature) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_feature(feature) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, bool required, Settings::ExperimentalFeature::Feature feature) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_required(required), m_feature(feature) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::ExperimentalFeature::Feature feature) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_feature(feature) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, Settings::ExperimentalFeature::Feature feature) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_feature(feature) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, bool required, Settings::ExperimentalFeature::Feature feature) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_required(required), m_feature(feature) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, bool required, Settings::ExperimentalFeature::Feature feature) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_required(required), m_feature(feature) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Settings::TogglePolicy::Policy groupPolicy) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_groupPolicy(groupPolicy) {} + + Argument(std::string_view name, char alias, std::string_view alternateName, Execution::Args::Type execArgType, Resource::StringId desc, ArgumentType type, Argument::Visibility visibility, Settings::TogglePolicy::Policy groupPolicy, Settings::AdminSetting adminSetting) : + m_name(name), m_alias(alias), m_alternateName(alternateName), m_execArgType(execArgType), m_desc(std::move(desc)), m_type(type), m_visibility(visibility), m_groupPolicy(groupPolicy), m_adminSetting(adminSetting) {} + std::string_view m_name; char m_alias; + std::string_view m_alternateName; Execution::Args::Type m_execArgType; Resource::StringId m_desc; bool m_required = false; diff --git a/src/AppInstallerCLICore/Command.cpp b/src/AppInstallerCLICore/Command.cpp @@ -228,6 +228,9 @@ namespace AppInstaller::CLI { strstr << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << arg.Alias() << ','; } + if (arg.AlternateName() != Argument::NoAlternateName) { + strstr << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << arg.AlternateName() << ','; + } strstr << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR << arg.Name(); argNames.emplace_back(strstr.str()); @@ -549,7 +552,8 @@ namespace AppInstaller::CLI { // This is an arg name, find it and process its value if needed. // Skip the double arg identifier chars. - std::string_view argName = currArg.substr(2); + size_t argStart = currArg.find_first_not_of(APPINSTALLER_CLI_ARGUMENT_IDENTIFIER_CHAR); + std::string_view argName = currArg.substr(argStart); bool argFound = false; bool hasValue = false; @@ -564,7 +568,10 @@ namespace AppInstaller::CLI for (const auto& arg : m_arguments) { - if (Utility::CaseInsensitiveEquals(argName, arg.Name())) + if ( + Utility::CaseInsensitiveEquals(argName, arg.Name()) || + Utility::CaseInsensitiveEquals(argName, arg.AlternateName()) + ) { if (arg.Type() == ArgumentType::Flag) { diff --git a/src/AppInstallerCLITests/Command.cpp b/src/AppInstallerCLITests/Command.cpp @@ -23,6 +23,18 @@ std::string GetArgumentName(const Argument& arg) return std::string{ arg.Name() }; } +std::string GetArgumentAlternateName(const Argument& arg) +{ + if (arg.AlternateName() == Argument::NoAlternateName) + { + return {}; + } + else + { + return std::string{ arg.AlternateName() }; + } +} + std::string GetArgumentAlias(const Argument& arg) { if (arg.Alias() == Argument::NoAlias) @@ -36,10 +48,9 @@ std::string GetArgumentAlias(const Argument& arg) } template <typename Enumerable, typename Op> -void EnsureStringsAreLowercaseAndNoCollisions(const std::string& info, const Enumerable& e, Op& op, bool requireLower = true) +void EnsureStringsAreLowercaseAndNoCollisions(const std::string& info, const Enumerable& e, Op& op, std::unordered_set<std::string>& values, bool requireLower = true) { INFO(info); - std::unordered_set<std::string> values; for (const auto& val : e) { @@ -62,13 +73,25 @@ void EnsureStringsAreLowercaseAndNoCollisions(const std::string& info, const Enu } } +template <typename Enumerable, typename Op> +void EnsureStringsAreLowercaseAndNoCollisions(const std::string& info, const Enumerable& e, Op& op, bool requireLower = true) +{ + std::unordered_set<std::string> values; + EnsureStringsAreLowercaseAndNoCollisions(info, e, op, values, requireLower); +} + void EnsureCommandConsistency(const Command& command) { EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " commands", command.GetCommands(), GetCommandName); auto args = command.GetArguments(); Argument::GetCommon(args); - EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " argument names", args, GetArgumentName); + + // Argument names and alternate names exist in the same space, so both need to be checked as a set + std::unordered_set<std::string> allArgumentNames; + EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " argument names", args, GetArgumentName, allArgumentNames); + EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " argument alternate name", args, GetArgumentAlternateName, allArgumentNames); + // Argument aliases use a different space than the names and can be checked separately EnsureStringsAreLowercaseAndNoCollisions(command.FullName() + " argument alias", args, GetArgumentAlias, false); // No : allowed in commands @@ -414,6 +437,23 @@ TEST_CASE("ParseArguments_NameWithAdjoinedValue", "[command]") RequireValueParsedToArg(values[0].substr(7), command.m_args[0], args); } +TEST_CASE("ParseArguments_AlternateNameWithAdjoinedValue", "[command]") +{ + Args args; + TestCommand command({ + Argument{ "pos1", 'p', "p1", Args::Type::Channel, DefaultDesc, ArgumentType::Positional}, + Argument{ "std1", 's', Args::Type::Command, DefaultDesc, ArgumentType::Standard }, + Argument{ "pos2", 'q', Args::Type::Count, DefaultDesc, ArgumentType::Positional }, + }); + + std::vector<std::string> values{ "--p1=Val1" }; + Invocation inv{ std::vector<std::string>(values) }; + + command.ParseArguments(inv, args); + + RequireValueParsedToArg(values[0].substr(5), command.m_args[0], args); +} + TEST_CASE("ParseArguments_NameFlag", "[command]") { Args args;