bfgroup / Lyra

A simple to use, composable, command line parser for C++ 11 and beyond
https://bfgroup.github.io/Lyra/
Boost Software License 1.0
471 stars 56 forks source link

Custom option lamba error string is dropped when nested under command parser #79

Open wadechristie opened 1 year ago

wadechristie commented 1 year ago

I encountered this problem when attempting to nest a lamba option under a sub-command. The error context returned from the lambda is dropped when unwinding the stack.

Here is an example to demonstrate the behavior:

#include <iostream>
#include <string>

#include <lyra/lyra.hpp>

static lyra::parser_result always_error_cb(std::string const&)
{
    return lyra::parser_result::error(lyra::parser_result_type::no_match, "Custom error message w/ context.");
}

struct subcommand
{
    subcommand(lyra::cli& cli)
    {
        cli.add_argument(lyra::command("subcommand", [this](lyra::group const& g) {
            exec();
        }).add_argument(lyra::opt([](std::string const& s) { return always_error_cb(s); }, "arg").name("--arg")));
    }

private:
    void exec()
    {
        std::cout << "Subcommand" << std::endl;
    }
};

int main(int argc, char const* argv[])
{
    bool show_help { false };
    auto cli
        = lyra::cli()
              .add_argument(lyra::help(show_help))
              .add_argument(lyra::opt([](std::string const& s) { return always_error_cb(s); }, "arg").name("--arg"));
    subcommand subcmd { cli };

    auto result = cli.parse({ argc, argv });

    if (!result)
    {
        std::cerr << "Error: " << result.message() << std::endl;
        return 1;
    }

    if (show_help)
    {
        std::cout << cli << std::endl;
        return 0;
    }

    return result ? 0 : 1;
}

}

Behavior with a top-level argument.

$ ./a.out --test value
Error: Custom error message w/ context.

Behavior with a sub-command argument. The error message with context for the user is lost.

$ ./a.out subcommand --test value
Error: Expected: [--arg <arg>]

The error propagates back up the stack to this point: https://github.com/bfgroup/Lyra/blob/a8bb6e22ea4dcd507292ecdb6443bad23ef7bfeb/include/lyra/arguments.hpp#L313-L316

And the error context is dropped a few lines later here: https://github.com/bfgroup/Lyra/blob/a8bb6e22ea4dcd507292ecdb6443bad23ef7bfeb/include/lyra/arguments.hpp#L351-L352

wadechristie commented 1 year ago

It seems to me the problem is at line 113 with the decision to break out of the loop just before the error would have been returned: https://github.com/bfgroup/Lyra/blob/a8bb6e22ea4dcd507292ecdb6443bad23ef7bfeb/include/lyra/arguments.hpp#L313-L321