diff --git a/include/vertexnova/utils/command_line_parser.h b/include/vertexnova/utils/command_line_parser.h index 4c585a0..19ea7b2 100644 --- a/include/vertexnova/utils/command_line_parser.h +++ b/include/vertexnova/utils/command_line_parser.h @@ -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 values(const std::string& name) const; @@ -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); diff --git a/src/command_line_parser.cpp b/src/command_line_parser.cpp index c00e2a8..88d768f 100644 --- a/src/command_line_parser.cpp +++ b/src/command_line_parser.cpp @@ -170,8 +170,9 @@ bool CommandLineParser::parse(const std::vector& 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); @@ -188,11 +189,11 @@ bool CommandLineParser::parse(const std::vector& 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]; } @@ -207,7 +208,7 @@ std::string CommandLineParser::value(const std::string& name) const { } std::vector 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; } @@ -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; diff --git a/tests/command_line_parser_test.cpp b/tests/command_line_parser_test.cpp index 7963abc..bb61e9e 100644 --- a/tests/command_line_parser_test.cpp +++ b/tests/command_line_parser_test.cpp @@ -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{"--window", "-w"}, + "Window size", + "size")); + parser.addOption(CommandLineOption( + std::vector{"--backend", "-b"}, + "Graphics backend", + "backend")); + + std::vector 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{"--window", "-w"}, + "Window size", + "size")); + parser.addOption(CommandLineOption( + std::vector{"--backend", "-b"}, + "Graphics backend", + "backend")); + + std::vector 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{"--window", "-w"}, + "Window size", + "size")); + + std::vector 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"));