Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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",
)
8 changes: 7 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 @@ -62,6 +62,11 @@ Command-line argument parser for C++20
- [Parameters Specific for Optional Arguments](/docs/tutorial.md#parameters-specific-for-optional-arguments)
- [Default Arguments](/docs/tutorial.md#default-arguments)
- [Argument Groups](/docs/tutorial.md#argument-groups)
- [Creating New Groups](/docs/tutorial.md#creating-new-groups)
- [Adding Arguments to Groups](/docs/tutorial.md#adding-arguments-to-groups)
- [Group Attributes](/docs/tutorial.md#group-attributes)
- [Complete Example](/docs/tutorial.md#complete-example)
- [Suppressing Argument Group Checks](/docs/tutorial.md#suppressing-argument-group-checks)
- [Parsing Arguments](/docs/tutorial.md#parsing-arguments)
- [Basic Argument Parsing Rules](/docs/tutorial.md#basic-argument-parsing-rules)
- [Compound Arguments](/docs/tutorial.md#compound-arguments)
Expand All @@ -72,6 +77,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)
- [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