diff --git a/DOCS.MD b/DOCS.MD index 4398615..41284e3 100644 --- a/DOCS.MD +++ b/DOCS.MD @@ -120,6 +120,72 @@ namespace argument_parser::helpers { Factory helpers for creating action objects used by `base_parser` and v2. +### `parser_settings` + +```cpp +struct parser_settings { + bool should_exit_on_error = true; + bool should_exit_on_missing_required = true; + bool should_exit_on_unknown_argument = true; + bool should_exit_on_help = true; + + bool ignore_unknown_arguments = false; + bool ignore_errors = false; +}; + +constexpr static inline parser_settings no_exit{ + false, + false, + false, + false +}; +``` + +`parser_settings` configures how the parser reacts to help, parse errors, +unknown arguments, and missing required arguments. The default settings preserve +CLI-style behavior: help exits after printing, unknown arguments and parse +errors exit with status `1`, and missing required arguments print diagnostics +and help before exiting. + +Use `argument_parser::no_exit` when embedding the parser in tests, tools, or +larger applications that should receive exceptions instead of process exits: + +```cpp +argument_parser::v2::parser parser(argument_parser::no_exit); +``` + +For targeted behavior, pass an explicit settings object to the v2 platform +parser constructor: + +```cpp +argument_parser::parser_settings settings; +settings.should_exit_on_help = false; +settings.should_exit_on_error = false; +settings.should_exit_on_unknown_argument = false; +settings.should_exit_on_missing_required = false; +settings.ignore_unknown_arguments = true; + +argument_parser::v2::parser parser(settings); +``` + +The fields have these effects: + +- `should_exit_on_help`: controls whether the automatically registered v2 + `-h` / `--help` action calls `std::exit(0)` after printing help. +- `should_exit_on_error`: when `true`, non-unknown parse errors caught by + `handle_arguments(...)` call `std::exit(1)`; when `false`, the exception is + rethrown. +- `should_exit_on_unknown_argument`: when `true`, unknown arguments caught by + `handle_arguments(...)` call `std::exit(1)`; when `false`, the unknown + argument exception is rethrown. +- `should_exit_on_missing_required`: when `true`, missing required arguments + print diagnostics/help and call `std::exit(1)`; when `false`, they throw + `std::runtime_error`. +- `ignore_unknown_arguments`: when `true`, tokens that match no convention and + cannot be consumed as positionals are skipped. +- `ignore_errors`: reserved in the public settings struct in the current + worktree; no runtime branch currently reads it. + ### `argument` ```cpp @@ -397,7 +463,7 @@ Typical use is through the platform alias from ``: using namespace argument_parser::v2::flags; int main() { - argument_parser::v2::parser parser; + argument_parser::v2::parser parser(argument_parser::no_exit); parser.add_argument({ {LongArgument, "count"}, @@ -463,7 +529,11 @@ only when `T` is exactly a `std::vector` specialization. `v2::base_parser` is the v2 facade. Concrete platform parsers such as `argument_parser::v2::linux_parser`, `macos_parser`, `windows_parser`, and the -portable `` alias `argument_parser::v2::parser` derive from it. +portable `` alias `argument_parser::v2::parser` derive from it. The +v2 platform parser constructors accept `parser_settings const& settings = {}`. +Use the default constructor for normal command-line tools, or pass +`argument_parser::no_exit` / a custom `parser_settings` object when the caller +should handle errors without automatic process exits. The class privately inherits from the v1 `argument_parser::base_parser` and re-exposes selected operations. @@ -808,7 +878,7 @@ configuration. using argument_parser::builder::new_argument; int main() { - argument_parser::v2::parser parser(false); + argument_parser::v2::parser parser(argument_parser::no_exit); int threshold = 0; std::vector ids; diff --git a/README.md b/README.md index e41d82e..b841b75 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ A lightweight, modern, and highly customizable C++17 argument parser with native - `build_and_get(parser)` for storable builder modes, returning a small container that is populated after parsing completes. - Positional arguments with optional explicit ordering and support for `--` as a positional separator. - Trait-driven `format_hint` and `purpose_hint` metadata used in generated help text and parse errors. -- Automatic help flag on `argument_parser::v2::parser` (`-h`, `--help`) with configurable exit behavior. +- Automatic help flag on `argument_parser::v2::parser` (`-h`, `--help`) with configurable exit behavior through `parser_settings`. +- `parser_settings` for choosing whether help, unknown arguments, parse errors, and missing required arguments exit or throw. - Auto-formatted help output. - Completion hooks via `parser.on_complete(...)`. - Pluggable conventions for GNU next-token, GNU equal-style, Windows next-token, and Windows inline `=` / `:` parsing, or bring your own! @@ -35,7 +36,7 @@ A lightweight, modern, and highly customizable C++17 argument parser with native using argument_parser::builder::new_argument; int main() { - argument_parser::v2::parser parser(false); // --help prints without exiting immediately + argument_parser::v2::parser parser(argument_parser::no_exit); // throw instead of exiting int threshold = 0; std::vector ids; @@ -157,8 +158,8 @@ If you omit `help_text()`, `v2` uses the trait hints to generate help such as `A `argument_parser::v2::parser` automatically registers `-h` and `--help`. ```cpp -argument_parser::v2::parser parser; // help prints and exits -argument_parser::v2::parser parser(false); // help prints without immediate exit +argument_parser::v2::parser parser; // help prints and exits +argument_parser::v2::parser parser(argument_parser::no_exit); // help prints without exiting ``` You can also display help manually: @@ -167,6 +168,39 @@ You can also display help manually: parser.display_help(conventions); ``` +## Parser Settings + +Use `argument_parser::parser_settings` when the parser is embedded in a larger +application, test, or REPL-like tool where parse failures should be handled by +the caller instead of ending the process. + +```cpp +argument_parser::parser_settings settings; +settings.should_exit_on_help = false; +settings.should_exit_on_error = false; +settings.should_exit_on_unknown_argument = false; +settings.should_exit_on_missing_required = false; +settings.ignore_unknown_arguments = true; + +argument_parser::v2::parser parser(settings); +``` + +The convenience constant `argument_parser::no_exit` disables the four automatic +exit paths while leaving unknown-argument handling enabled: + +```cpp +argument_parser::v2::parser parser(argument_parser::no_exit); +``` + +Settings fields: + +- `should_exit_on_help`: exit with status `0` after automatic `-h` / `--help`. +- `should_exit_on_error`: exit with status `1` for non-unknown parse errors; otherwise rethrow. +- `should_exit_on_unknown_argument`: exit with status `1` for unknown arguments; otherwise rethrow. +- `should_exit_on_missing_required`: exit with status `1` after reporting missing required arguments; otherwise throw. +- `ignore_unknown_arguments`: skip unknown arguments that cannot be consumed as positionals. +- `ignore_errors`: present in the public settings struct, but not currently read by the parser runtime. + ## Supported Conventions - GNU next-token: `-o value`, `--output value` diff --git a/TODO.md b/TODO.md index 46ec87e..ec5024d 100644 --- a/TODO.md +++ b/TODO.md @@ -19,8 +19,6 @@ After handle_parse(...) ends, we can call the on_parse_done callbacks. But this 1. Storable arguments. Those would be not actions, but stored values. So you could get them later on in the callback or somewhere else. 2. The on_parse_done callbacks should be called after all actions are invoked. But we should make this a toggle, so the callback only gets executed if the user explicitly wants it to be called. By default we would enable it. -## Self reminder, implement this feature. It'd be helpful. - ### Update: 29 March. 2026 Help text is clear, supports multi convention print, also provides hints for types that provides the hints in the traits. Positional argument support is here. Also we now have some tests to ensure the library works as intended. @@ -39,9 +37,8 @@ Configure CMAKE to ensure the library can be built as a library and can be insta # TODO 5: Display help | DONE Display help doesn't reflect the conventions right now. Also it should come automatically, and should be allowed to overriden by user. -# TODO 6: Accumulate repeated calls -Add support to letting users accumulate repeated calls to a flag. If the flag is called x times, the result should be x items stored in a vector, -instead of an action doing it. +# TODO 6: Accumulate repeated calls | DONE +Add support to let users accumulate repeated calls to a flag. If the flag is called x times, the result should be x items stored in a vector, instead of an action doing it. # TODO 7: Defaults/Implicits If given, an arguments default store value could be changed. If nothing was given use that value instead. diff --git a/examples/test/main.cpp b/examples/test/main.cpp index a0e0236..c96217c 100644 --- a/examples/test/main.cpp +++ b/examples/test/main.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -42,7 +43,7 @@ template struct parser_trait> { using namespace argument_parser::v2::flags; auto main() -> int { - argument_parser::v2::parser parser({.should_exit_on_help = false}); + argument_parser::v2::parser parser(argument_parser::no_exit); new_argument() .positional("count") diff --git a/src/headers/parser/argument_parser.hpp b/src/headers/parser/argument_parser.hpp index c2cb228..7ccc7f1 100644 --- a/src/headers/parser/argument_parser.hpp +++ b/src/headers/parser/argument_parser.hpp @@ -249,7 +249,15 @@ namespace argument_parser { bool ignore_errors = false; }; - constexpr static inline parser_settings no_exit{false, false, false, false}; + constexpr static inline parser_settings no_exit{ + .should_exit_on_error = false, + .should_exit_on_missing_required = false, + .should_exit_on_unknown_argument = false, + .should_exit_on_help = false, + + .ignore_unknown_arguments = false, + .ignore_errors = false, + }; /** * @brief Base class for parsing arguments from the command line.