Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ else()
endif()

project(cpp-ap
VERSION 3.0.0.8
VERSION 3.0.0
DESCRIPTION "Command-line argument parser for C++20"
HOMEPAGE_URL "https://github.com/SpectraL519/cpp-ap"
LANGUAGES CXX
Expand Down
2 changes: 1 addition & 1 deletion Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ PROJECT_NAME = CPP-AP
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER = 3.0.0.8
PROJECT_NUMBER = 3.0.0

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module(
name = "cpp-ap",
version = "3.0.0.8",
version = "3.0.0",
)
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Command-line argument parser for C++20

> [!NOTE]
>
> [v1.0](https://github.com/SpectraL519/cpp-ap/commit/9a9e5360766b732f322ae2efe3cf5ec5f9268eef) of the library has been developed for the *Team Programming* course at the *Wrocław University of Science and Technology*.
> [v1.0](https://github.com/SpectraL519/cpp-ap/releases/tag/v1.0) of the library has been developed for the *Team Programming* course at the *Wrocław University of Science and Technology*.
>
> Faculty: *W04N - Faculty of Information and Communication Technology*
>
Expand Down Expand Up @@ -72,6 +72,7 @@ Command-line argument parser for C++20
- [Using Multiple Subparsers](/docs/tutorial.md#using-multiple-subparsers)
- [Parsing Arguments with Subparsers](/docs/tutorial.md#parsing-arguments-with-subparsers)
- [Tracking Parser State](/docs/tutorial.md#tracking-parser-state)
- [Suppressing Argument Group Checks](/docs/tutorial.md#suppressing-argument-group-checks)
- [Examples](/docs/tutorial.md#examples)
- [Common Utility](/docs/tutorial.md#common-utility)
- [Dev notes](/docs/dev_notes.md#dev-notes)
Expand Down
2 changes: 1 addition & 1 deletion cpp-ap-demo
66 changes: 44 additions & 22 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,26 @@
- [Boolean Flags](#boolean-flags)
- [Argument Parameters](#argument-parameters)
- [Common Parameters](#common-parameters)
- [help](#1-help---the-arguments-description-which-will-be-printed-when-printing-the-parser-class-instance)
- [hidden](#2-hidden---if-this-option-is-set-for-an-argument-then-it-will-not-be-included-in-the-program-description)
- [required](#3-required---if-this-option-is-set-for-an-argument-and-its-value-is-not-passed-in-the-command-line-an-exception-will-be-thrown)
- [bypass required](#4-bypass_required---if-this-option-is-set-for-an-argument-the-required-option-for-other-arguments-will-be-discarded-if-the-bypassing-argument-is-used-in-the-command-line)
- [nargs](#5-nargs---sets-the-allowed-number-of-values-to-be-parsed-for-an-argument)
- [greedy](#6-greedy---if-this-option-is-set-the-argument-will-consume-all-command-line-values-until-its-upper-nargs-bound-is-reached)
- [choices](#7-choices---a-list-of-valid-argument-values)
- [value actions](#8-value-actions---functions-that-are-called-after-parsing-an-arguments-value)
- [default values](#9-default_values---a-list-of-values-which-will-be-used-if-no-values-for-an-argument-have-been-parsed)
- [help](#1-help---the-arguments-description-which-will-be-printed-when-printing-the-parser-class-instance) - the text shown in the help message to describe an argument
- [hidden](#2-hidden---if-this-option-is-set-for-an-argument-then-it-will-not-be-included-in-the-program-description) - hides the argument from the generated program description and help output
- [required](#3-required---if-this-option-is-set-for-an-argument-and-its-value-is-not-passed-in-the-command-line-an-exception-will-be-thrown) - marks the argument as mandatory; not using it will cause an error
- [suppress arg checks](#4-suppress_arg_checks---using-a-suppressing-argument-results-in-suppressing-requirement-checks-for-other-arguments) - if a suppressing argument is used, other requirement validation will be skipped for other arguments
- [nargs](#5-nargs---sets-the-allowed-number-of-values-to-be-parsed-for-an-argument) - defines how many values an argument can or must accept
- [greedy](#6-greedy---if-this-option-is-set-the-argument-will-consume-all-command-line-values-until-its-upper-nargs-bound-is-reached) - makes the argument consume all following values until its limit is reached
- [choices](#7-choices---a-list-of-valid-argument-values) - restricts the valid inputs to a predefined set of values
- [value actions](#8-value-actions---functions-that-are-called-after-parsing-an-arguments-value) - allows you to run custom code after the argument’s value is parsed
- [default values](#9-default_values---a-list-of-values-which-will-be-used-if-no-values-for-an-argument-have-been-parsed) - specifies fallback values to use if none are provided
- [Parameters Specific for Optional Arguments](#parameters-specific-for-optional-arguments)
- [on-flag actions](#1-on-flag-actions---functions-that-are-called-immediately-after-parsing-an-arguments-flag)
- [implicit values](#2-implicit_values---a-list-of-values-which-will-be-set-for-an-argument-if-only-its-flag-but-no-values-are-parsed-from-the-command-line)
- [on-flag actions](#1-on-flag-actions---functions-that-are-called-immediately-after-parsing-an-arguments-flag) - executes custom code immediately when the argument’s flag is present
- [implicit values](#2-implicit_values---a-list-of-values-which-will-be-set-for-an-argument-if-only-its-flag-but-no-values-are-parsed-from-the-command-line) - automatically assigns a value if an argument flag is used without an explicit value
- [Predefined Parameter Values](#predefined-parameter-values)
- [Default Arguments](#default-arguments)
- [Argument Groups](#argument-groups)
Comment thread
SpectraL519 marked this conversation as resolved.
- [Creating New Groups](#creating-new-groups)
- [Adding Arguments to Groups](#adding-arguments-to-groups)
- [Group Attributes](#group-attributes)
- [Complete Example](#complete-example)
- [Suppressing Argument Group Checks](#suppressing-argument-group-checks)
- [Parsing Arguments](#parsing-arguments)
- [Basic Argument Parsing Rules](#basic-argument-parsing-rules)
- [Compound Arguments](#compound-arguments)
Expand Down Expand Up @@ -318,9 +319,7 @@ Optional arguments:
> [!WARNING]
>
> - If a positional argument is defined as non-required, then no required positional argument can be defined after (only other non-required positional arguments and optional arguments will be allowed).
> - For both positional and optional arguments:
> - enabling the `required` option disables the `bypass_required` option
> - disabling the `required` option has no effect on the `bypass_required` option.
> - If an argument is suppressing (see [suppress arg checks](#4-suppress_arg_checks---using-a-suppressing-argument-results-in-suppressing-requirement-checks-for-other-arguments) and [Suppressing Argument Group Checks](#suppressing-argument-group-checks)), then it cannot be required (an exception will be thrown).

```cpp
// example: positional arguments
Expand Down Expand Up @@ -377,24 +376,27 @@ Command Result

<br />

#### 4. `bypass_required` - If this option is set for an argument, the `required` option for other arguments will be discarded if the bypassing argument is used in the command-line.
#### 4. `suppress_arg_checks` - Using a suppressing argument results in suppressing requirement checks for other arguments.

If an argument is defined with the `suppress_arg_checks` option enabled and such argument is explicitly used in the command-line, then requirement validation will be suppressed/skipped for other arguments. This includes validating whether:
- a required argument has been parsed
- the number of values parsed for an argument matches the specified [nargs](#5-nargs---sets-the-allowed-number-of-values-to-be-parsed-for-an-argument) range.

> [!NOTE]
>
> - Both all arguments have the `bypass_required` option disabled.
> - The default value of the value parameter of the `argument::bypass_required(bool)` method is `true` for all arguments.
> - All arguments have the `suppress_arg_checks` option disabled by default.
> - The default value of the value parameter of the `argument::suppress_arg_checks(bool)` method is `true` for all arguments.

> [!WARNING]
>
> For both positional and optional arguments:
> - enabling the `bypass_required` option disables the `required` option
> - disabling the `bypass_required` option has no effect on the `required` option.
> - Enabling the `suppress_arg_checks` option has no effect on [argument group](#argument-groups) requirements validation.
> - Enabling argument checks suppressing is not possible for required arguments (an exception will be thrown).

```cpp
// example: optional arguments
parser.add_positional_argument("input");
parser.add_optional_argument("output", "o").required();
parser.add_optional_argument("version", "v").bypass_required();
parser.add_optional_argument("version", "v").suppress_arg_checks();

parser.parse_args(argc, argv);

Expand Down Expand Up @@ -522,7 +524,7 @@ Actions are represented as functions, which take the argument's value as an argu
```cpp
void is_valid_user_tag(const std::string& tag) {
if (tag.empty() or tag.front() != '@')
throw std::runtime_error(std::format("Invalid user tag: `{}` must start with '@'", tag));
throw std::runtime_error(std::format("Invalid user tag: `{}` - must start with '@'", tag));
}

parser.add_optional_argument<std::string>("user", "u")
Expand Down Expand Up @@ -934,6 +936,26 @@ Output Options: (required, mutually exclusive)
--print, -p : Print output to the console
```

### Suppressing Argument Group Checks

Similarly to [suppressing argument checks](#4-suppress_arg_checks---using-a-suppressing-argument-results-in-suppressing-requirement-checks-for-other-arguments), an argument can suppress the requirement checks of argument groups:

```c++
argument.suppress_group_checks();
```

If such argument is used the requirement checks associated with the [group attributes](#group-attributes) will not be validated.

> [!NOTE]
>
> - All arguments have the `suppress_group_checks` option disabled by default.
> - The default value of the value parameter of the `argument::suppress_group_checks(bool)` method is `true` for all arguments.

> [!WARNING]
>
> - Enabling the `suppress_group_checks` option has no effect on argument requirements validation.
> - Enabling argument group checks suppressing is not possible for required arguments (an exception will be thrown).

<br/>
<br/>
<br/>
Expand Down
82 changes: 57 additions & 25 deletions include/ap/argument.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,14 @@ class argument : public detail::argument_base {
return this->_required;
}

/// @return `true` if required argument bypassing is enabled for the argument, `false` otherwise.
/// @note Required argument bypassing is enabled only if the argument is not required.
[[nodiscard]] bool is_bypass_required_enabled() const noexcept override {
return not this->_required and this->_bypass_required;
/// @return `true` if argument checks suppressing is enabled for the argument, `false` otherwise.
[[nodiscard]] bool suppresses_arg_checks() const noexcept override {
return this->_suppress_arg_checks;
}

/// @return `true` if argument group checks suppressing is enabled for the argument, `false` otherwise.
[[nodiscard]] bool suppresses_group_checks() const noexcept override {
return this->_suppress_group_checks;
}

/// @return `true` if the argument is greedy, `false` otherwise.
Expand Down Expand Up @@ -152,40 +156,59 @@ class argument : public detail::argument_base {

/**
* @brief Set the `required` attribute of the argument
* @param r The attribute value.
* @param value The attribute value (default: `true`).
* @return Reference to the argument instance.
* @attention Setting the `required` attribute to `true` disables the `bypass_required` attribute.
*/
argument& required(const bool r = true) noexcept {
this->_required = r;
if (this->_required)
this->_bypass_required = false;
argument& required(const bool value = true) {
if (value and (this->_suppress_arg_checks or this->_suppress_group_checks))
throw invalid_configuration(
std::format("A suppressing argument [{}] cannot be required!", this->_name.str())
);

this->_required = value;
return *this;
}

/**
* @brief Enable/disable bypassing the `required` attribute for the argument.
* @param br The attribute value.
* @brief Enable/disable suppressing argument checks for other arguments.
* @param value The attribute value (default: `true`).
* @return Reference to the argument instance.
* @attention Setting the `bypass_required` option to `true` disables the `required` attribute.
*/
argument& bypass_required(const bool br = true) noexcept {
this->_bypass_required = br;
if (this->_bypass_required)
this->_required = false;
argument& suppress_arg_checks(const bool value = true) {
if (value and this->_required)
throw invalid_configuration(std::format(
"A required argument [{}] cannot suppress argument checks!", this->_name.str()
));

this->_suppress_arg_checks = value;
return *this;
}

/**
* @brief Enable/disable suppressing argument group checks.
* @param value The attribute value (default: `true`).
* @return Reference to the argument instance.
*/
argument& suppress_group_checks(const bool value = true) {
if (value and this->_required)
throw invalid_configuration(std::format(
"A required argument [{}] cannot suppress argument group checks!", this->_name.str()
));

this->_suppress_group_checks = value;
return *this;
}

/**
* @brief Set the `greedy` attribute of the argument.
* @param g The attribute value.
* @param value The attribute value (default: `true`).
* @return Reference to the argument instance.
* @note The method is enabled only if `value_type` is not `none_type`.
*/
argument& greedy(const bool g = true) noexcept
argument& greedy(const bool value = true) noexcept
requires(not util::c_is_none<value_type>)
{
this->_greedy = g;
this->_greedy = value;
return *this;
}

Expand Down Expand Up @@ -438,8 +461,10 @@ class argument : public detail::argument_base {
bld.params.reserve(6ull);
if (this->_required != _default_required)
bld.add_param("required", std::format("{}", this->_required));
if (this->is_bypass_required_enabled())
bld.add_param("bypass required", "true");
if (this->_suppress_arg_checks)
bld.add_param("suppress arg checks", "true");
if (this->_suppress_group_checks)
bld.add_param("suppress group checks", "true");
if (this->_nargs_range != _default_nargs_range)
bld.add_param("nargs", this->_nargs_range);
if constexpr (util::c_writable<value_type>) {
Expand Down Expand Up @@ -670,11 +695,15 @@ class argument : public detail::argument_base {
}
else {
if (not (std::istringstream(str_value) >> value))
throw parsing_failure::invalid_value(this->_name, str_value);
throw parsing_failure(std::format(
"Cannot parse value `{}` for argument [{}].", str_value, this->_name.str()
));
}

if (not this->_is_valid_choice(value))
throw parsing_failure::invalid_choice(this->_name, str_value);
throw parsing_failure(std::format(
"Value `{}` is not a valid choice for argument [{}].", str_value, this->_name.str()
));

const auto apply_visitor = action::util::apply_visitor<value_type>{value};
for (const auto& action : this->_value_actions)
Expand All @@ -700,7 +729,10 @@ class argument : public detail::argument_base {
_value_actions; ///< The argument's value actions collection.

bool _required : 1; ///< The argument's `required` attribute value.
bool _bypass_required : 1 = false; ///< The argument's `bypass_required` attribute value.
bool _suppress_arg_checks : 1 =
false; ///< The argument's `suppress_arg_checks` attribute value.
bool _suppress_group_checks : 1 =
false; ///< The argument's `suppress_group_checks` attribute value.
bool _greedy : 1 = false; ///< The argument's `greedy` attribute value.
bool _hidden : 1 = false; ///< The argument's `hidden` attribute value.

Expand Down
Loading