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:
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;