Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions include/vertexnova/utils/command_line_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,21 @@ class CommandLineParser {

/**
* @brief Check if an option was specified.
* @param name Option name
* @param name Canonical option name or any registered alias (e.g. "--window" or "-w")
* @return true if the option was found
*/
bool isSet(const std::string& name) const;

/**
* @brief Get the value of an option.
* @param name Option name
* @param name Canonical option name or any registered alias (e.g. "--window" or "-w")
* @return The value of the option, or empty string if not found
*/
std::string value(const std::string& name) const;

/**
* @brief Get all values for an option.
* @param name Option name
* @param name Canonical option name or any registered alias
* @return Vector of all values for the option
*/
std::vector<std::string> values(const std::string& name) const;
Expand Down Expand Up @@ -224,6 +224,7 @@ class CommandLineParser {

private:
const CommandLineOption* findOption(const std::string& name) const;
std::string canonicalOptionName(const std::string& name) const;
std::string generateHelpText() const;
void setError(const std::string& message);

Expand Down
19 changes: 14 additions & 5 deletions src/command_line_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,9 @@ bool CommandLineParser::parse(const std::vector<std::string>& args) {
return false;
}

// Store the option value
option_values_[option_name].push_back(option_value);
// Store under the primary (canonical) name so isSet("--window") works
// whether the user passed --window or the short alias -w.
option_values_[option->getName()].push_back(option_value);

// Call callback if registered
auto callback = callbacks_.find(option_name);
Expand All @@ -188,11 +189,11 @@ bool CommandLineParser::parse(const std::vector<std::string>& args) {
}

bool CommandLineParser::isSet(const std::string& name) const {
return option_values_.find(name) != option_values_.end();
return option_values_.find(canonicalOptionName(name)) != option_values_.end();
}

std::string CommandLineParser::value(const std::string& name) const {
auto it = option_values_.find(name);
auto it = option_values_.find(canonicalOptionName(name));
if (it != option_values_.end() && !it->second.empty()) {
return it->second[0];
}
Expand All @@ -207,7 +208,7 @@ std::string CommandLineParser::value(const std::string& name) const {
}

std::vector<std::string> CommandLineParser::values(const std::string& name) const {
auto it = option_values_.find(name);
auto it = option_values_.find(canonicalOptionName(name));
if (it != option_values_.end()) {
return it->second;
}
Expand Down Expand Up @@ -274,6 +275,14 @@ const CommandLineOption* CommandLineParser::findOption(const std::string& name)
return nullptr;
}

std::string CommandLineParser::canonicalOptionName(const std::string& name) const {
const CommandLineOption* option = findOption(name);
if (option && !option->getName().empty()) {
return option->getName();
}
return name;
}

std::string CommandLineParser::generateHelpText() const {
std::ostringstream oss;

Expand Down
68 changes: 68 additions & 0 deletions tests/command_line_parser_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,74 @@ TEST(CommandLineParserStandaloneTest, PositionalArgumentsRef) {
EXPECT_EQ(positional[2], "arg3");
}

TEST(CommandLineParserStandaloneTest, ShortAliasNormalizesToCanonicalName) {
CommandLineParser parser("Test", "1.0.0");
parser.addOption(CommandLineOption(
std::vector<std::string>{"--window", "-w"},
"Window size",
"size"));
parser.addOption(CommandLineOption(
std::vector<std::string>{"--backend", "-b"},
"Graphics backend",
"backend"));

std::vector<std::string> args = {"program", "-w", "1920x1080", "-b", "metal"};
EXPECT_TRUE(parser.parse(args));

EXPECT_TRUE(parser.isSet("--window"));
EXPECT_TRUE(parser.isSet("-w"));
EXPECT_EQ(parser.value("--window"), "1920x1080");
EXPECT_EQ(parser.value("-w"), "1920x1080");

EXPECT_TRUE(parser.isSet("--backend"));
EXPECT_TRUE(parser.isSet("-b"));
EXPECT_EQ(parser.value("--backend"), "metal");
EXPECT_EQ(parser.value("-b"), "metal");

auto window_vals = parser.values("--window");
EXPECT_EQ(window_vals.size(), 1);
EXPECT_EQ(window_vals[0], "1920x1080");
}

TEST(CommandLineParserStandaloneTest, LongOptionCanonicalLookup) {
CommandLineParser parser("Test", "1.0.0");
parser.addOption(CommandLineOption(
std::vector<std::string>{"--window", "-w"},
"Window size",
"size"));
parser.addOption(CommandLineOption(
std::vector<std::string>{"--backend", "-b"},
"Graphics backend",
"backend"));

std::vector<std::string> args = {"program", "--window", "1280x720", "--backend", "vulkan"};
EXPECT_TRUE(parser.parse(args));

EXPECT_TRUE(parser.isSet("--window"));
EXPECT_TRUE(parser.isSet("-w"));
EXPECT_EQ(parser.value("--window"), "1280x720");
EXPECT_EQ(parser.value("-w"), "1280x720");

EXPECT_TRUE(parser.isSet("--backend"));
EXPECT_TRUE(parser.isSet("-b"));
EXPECT_EQ(parser.value("--backend"), "vulkan");
EXPECT_EQ(parser.value("-b"), "vulkan");
}

TEST(CommandLineParserStandaloneTest, ShortAliasEqualsFormNormalizesToCanonicalName) {
CommandLineParser parser("Test", "1.0.0");
parser.addOption(CommandLineOption(
std::vector<std::string>{"--window", "-w"},
"Window size",
"size"));

std::vector<std::string> args = {"program", "-w=800x600"};
EXPECT_TRUE(parser.parse(args));

EXPECT_TRUE(parser.isSet("--window"));
EXPECT_EQ(parser.value("--window"), "800x600");
}

TEST(CommandLineParserStandaloneTest, SlashOptionPrefix) {
CommandLineParser parser("Test", "1.0.0");
parser.addOption(CommandLineOption("/debug", "Enable debug mode"));
Expand Down
Loading