impr: Rework command line argument parsing (#2440)

The current CLI argument parsing did not work as documented and had a
number of issues related to multi-flag (subcommand) parsing. I've
reworked the logic in such a way that should maintain full compatibility
with any existing scripts/use-cases but with added functionality.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
Zackary Newman 2025-09-12 15:07:33 -04:00 committed by GitHub
parent 6169078c04
commit dcbba9cbfc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 56 deletions

View File

@ -13,6 +13,9 @@
namespace hex::subcommands { namespace hex::subcommands {
std::optional<SubCommand> findSubCommand(const std::string &arg) { std::optional<SubCommand> findSubCommand(const std::string &arg) {
if (arg == "-" || arg == "--")
return std::nullopt;
for (auto &plugin : PluginManager::getPlugins()) { for (auto &plugin : PluginManager::getPlugins()) {
for (auto &subCommand : plugin.getSubCommands()) { for (auto &subCommand : plugin.getSubCommands()) {
if (fmt::format("--{}", subCommand.commandLong) == arg || fmt::format("-{}", subCommand.commandShort) == arg) { if (fmt::format("--{}", subCommand.commandLong) == arg || fmt::format("-{}", subCommand.commandShort) == arg) {
@ -35,46 +38,62 @@ namespace hex::subcommands {
auto argsIter = args.begin(); auto argsIter = args.begin();
// Get subcommand associated with the first argument std::optional<SubCommand> currentSubCommand;
std::optional<SubCommand> currentSubCommand = findSubCommand(*argsIter);
if (currentSubCommand) {
argsIter += 1;
// If it is a valid subcommand, remove it from the argument list
} else {
// If no (valid) subcommand was provided, the default one is --open
currentSubCommand = findSubCommand("--open");
}
// Arguments of the current subcommand
std::vector<std::string> currentSubCommandArgs; std::vector<std::string> currentSubCommandArgs;
// Compute all subcommands to run if (*argsIter == "--") {
while (argsIter != args.end()) { // Treat the rest of the args as files
const std::string &arg = *argsIter; ++argsIter;
std::vector<std::string> remainingArgs(argsIter, args.end());
subCommands.emplace_back(*findSubCommand("--open"), remainingArgs);
argsIter = args.end(); // Skip while loop
} else if (!findSubCommand(*argsIter)) {
// First argument not a subcommand, treat all args as files
subCommands.emplace_back(*findSubCommand("--open"), args);
argsIter = args.end(); // Skip while loop
}
if (!currentSubCommandArgs.empty() && arg.starts_with("--") && !(currentSubCommand.has_value() && currentSubCommand->type == SubCommand::Type::SubCommand)) { while (argsIter != args.end()) {
// Save command to run // Get subcommand associated with the argument
if (currentSubCommand) { // Guaranteed to find a match on the first argument, as the other case has been handled above
const auto newSubCommand = findSubCommand(*argsIter);
if (*argsIter == "--") {
// Treat the rest of the args as files
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
++argsIter;
std::vector<std::string> remainingArgs(argsIter, args.end());
subCommands.emplace_back(*findSubCommand("--open"), remainingArgs);
currentSubCommand = std::nullopt;
currentSubCommandArgs.clear();
break;
}
// Will always take this `if` statement on the first time through the loop
if (newSubCommand.has_value()) {
if (currentSubCommand.has_value()) {
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs); subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
} }
currentSubCommand = std::nullopt; if (newSubCommand->type == SubCommand::Type::SubCommand) {
currentSubCommandArgs = { }; ++argsIter;
} else if (currentSubCommand) { std::vector<std::string> remainingArgs(argsIter, args.end());
// Add current argument to the current command subCommands.emplace_back(*newSubCommand, remainingArgs);
currentSubCommandArgs.push_back(arg);
argsIter += 1; currentSubCommand = std::nullopt;
} else { currentSubCommandArgs.clear();
// Get next subcommand from current argument break;
currentSubCommand = findSubCommand(arg);
if (!currentSubCommand) {
log::error("No subcommand named '{}' found", arg);
exit(EXIT_FAILURE);
} }
argsIter += 1; currentSubCommand = newSubCommand;
currentSubCommandArgs.clear();
} else {
currentSubCommandArgs.push_back(*argsIter);
} }
++argsIter;
} }
// Save last command to run // Save last command to run
@ -83,7 +102,7 @@ namespace hex::subcommands {
} }
// Run the subcommands // Run the subcommands
for (auto &[subcommand, subCommandArgs] : subCommands) { for (const auto &[subcommand, subCommandArgs] : subCommands) {
subcommand.callback(subCommandArgs); subcommand.callback(subCommandArgs);
} }

View File

@ -99,29 +99,22 @@ namespace hex::plugin::builtin {
} }
std::vector<std::string> fullPaths; std::vector<std::string> fullPaths;
bool doubleDashFound = false; for (const auto &arg : args) {
for (auto &arg : args) { try {
std::fs::path path;
// Skip the first argument named `--`
if (arg == "--" && !doubleDashFound) {
doubleDashFound = true;
} else {
try { try {
std::fs::path path; path = std::fs::weakly_canonical(arg);
} catch(std::fs::filesystem_error &) {
try { path = arg;
path = std::fs::weakly_canonical(arg);
} catch(std::fs::filesystem_error &) {
path = arg;
}
if (path.empty())
continue;
fullPaths.push_back(wolv::util::toUTF8String(path));
} catch (std::exception &e) {
log::error("Failed to open file '{}'\n {}", arg, e.what());
} }
if (path.empty())
continue;
fullPaths.push_back(wolv::util::toUTF8String(path));
} catch (std::exception &e) {
log::error("Failed to open file '{}'\n {}", arg, e.what());
} }
} }
@ -541,9 +534,6 @@ namespace hex::plugin::builtin {
void registerCommandForwarders() { void registerCommandForwarders() {
hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){ hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){
for (auto &arg : args) { for (auto &arg : args) {
if (arg.starts_with("--"))
break;
RequestOpenFile::post(arg); RequestOpenFile::post(arg);
} }
}); });
@ -586,4 +576,4 @@ namespace hex::plugin::builtin {
}); });
} }
} }