Skip to content

Commit 4ebeaf3

Browse files
authored
YT-CPPAP-4: Extend argument description
- Added the `argument_parser::verbose` function which can be used to set the description verbosity mode - Aligned the argument description generating to: - Align the argument help messages (when verbose=false) - Print the argument parameter values (when verbose=true) - Added missing file doc comments
1 parent 5afb543 commit 4ebeaf3

26 files changed

+861
-85
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ else()
77
endif()
88

99
project(cpp-ap
10-
VERSION 2.0.2
10+
VERSION 2.1.0
1111
DESCRIPTION "Command-line argument parser for C++20"
1212
HOMEPAGE_URL "https://github.com/SpectraL519/cpp-ap"
1313
LANGUAGES CXX

Doxyfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ PROJECT_NAME = "CPP-AP"
4848
# could be handy for archiving the generated documentation or if some version
4949
# control system is used.
5050

51-
PROJECT_NUMBER = 2.0.2
51+
PROJECT_NUMBER = 2.1.0
5252

5353
# Using the PROJECT_BRIEF tag one can provide an optional one line description
5454
# for a project that appears at the top of each page and should give viewer a

include/ap/action/detail/utility.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
33
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44

5+
/**
6+
* @file utility.hpp
7+
* @brief Defines general action-related utility.
8+
*/
9+
510
#pragma once
611

712
#include "ap/action/specifiers.hpp"

include/ap/action/predefined_actions.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
33
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44

5+
/// @file predefined_actions.hpp
6+
57
#pragma once
68

79
#include "ap/error/exceptions.hpp"

include/ap/action/specifiers.hpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
33
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44

5+
/**
6+
* @file specifiers.hpp
7+
* @brief Defies the action specifier types.
8+
*/
9+
510
#pragma once
611

712
#include "ap/detail/concepts.hpp"
@@ -10,11 +15,7 @@
1015

1116
namespace ap::action_type {
1217

13-
// TODO:
14-
// * on_read_action
15-
// * on_flag_action
16-
17-
/*
18+
/**
1819
* @brief Represents a transformating action.
1920
*
2021
* Represents an argument action which transforms the parsed value and
@@ -25,7 +26,7 @@ struct transform {
2526
using type = std::function<T(const T&)>;
2627
};
2728

28-
/*
29+
/**
2930
* @brief Represents a modifying action.
3031
*
3132
* Represents an argument action which modifies the value of an

include/ap/argument/default.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
33
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44

5+
/**
6+
* @file default.hpp
7+
* @brief Defines the default argument discriminator types.
8+
*/
9+
510
#pragma once
611

712
#include <cstdint>

include/ap/argument/optional.hpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
33
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44

5+
/// @file optional.hpp
6+
57
#pragma once
68

79
#include "ap/action/detail/utility.hpp"
810
#include "ap/action/predefined_actions.hpp"
11+
#include "ap/detail/argument_descriptor.hpp"
912
#include "ap/detail/argument_interface.hpp"
1013
#include "ap/detail/concepts.hpp"
1114
#include "ap/nargs/range.hpp"
@@ -55,7 +58,7 @@ class optional : public ap::detail::argument_interface {
5558
* @param help_msg The help message to set.
5659
* @return Reference to the optional argument.
5760
*/
58-
optional& help(std::string_view help_msg) noexcept override {
61+
optional& help(std::string_view help_msg) noexcept {
5962
this->_help_msg = help_msg;
6063
return *this;
6164
}
@@ -200,6 +203,35 @@ class optional : public ap::detail::argument_interface {
200203
return this->_help_msg;
201204
}
202205

206+
/**
207+
* @param verbose The verbosity mode value.
208+
* @return An argument_descriptor instance for the argument.
209+
*/
210+
[[nodiscard]] detail::argument_descriptor desc(const bool verbose) const noexcept override {
211+
detail::argument_descriptor desc(this->_name.str(), this->_help_msg);
212+
213+
if (not verbose)
214+
return desc;
215+
216+
desc.params.reserve(6);
217+
if (this->_required)
218+
desc.add_param("required", "true");
219+
if (this->_bypass_required)
220+
desc.add_param("bypass required", "true");
221+
if (this->_nargs_range.has_value())
222+
desc.add_param("nargs", this->_nargs_range.value());
223+
if constexpr (detail::c_writable<value_type>) {
224+
if (not this->_choices.empty())
225+
desc.add_range_param("choices", this->_choices);
226+
if (this->_default_value.has_value())
227+
desc.add_param("default value", std::any_cast<value_type>(this->_default_value));
228+
if (this->_implicit_value.has_value())
229+
desc.add_param("implicit value", std::any_cast<value_type>(this->_implicit_value));
230+
}
231+
232+
return desc;
233+
}
234+
203235
/// @return True if the optional argument is required, false otherwise.
204236
[[nodiscard]] bool is_required() const noexcept override {
205237
return this->_required;

include/ap/argument/positional.hpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
33
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44

5+
/// @file positional.hpp
6+
57
#pragma once
68

79
#include "ap/action/detail/utility.hpp"
@@ -53,7 +55,7 @@ class positional : public ap::detail::argument_interface {
5355
* @param help_msg The help message to set.
5456
* @return Reference to the positional argument.
5557
*/
56-
positional& help(std::string_view help_msg) noexcept override {
58+
positional& help(std::string_view help_msg) noexcept {
5759
this->_help_msg = help_msg;
5860
return *this;
5961
}
@@ -106,7 +108,6 @@ class positional : public ap::detail::argument_interface {
106108
return this->_optional;
107109
}
108110

109-
110111
/// @brief Friend class declaration for access by argument_parser.
111112
friend class ::ap::argument_parser;
112113

@@ -128,6 +129,24 @@ class positional : public ap::detail::argument_interface {
128129
return this->_help_msg;
129130
}
130131

132+
/**
133+
* @param verbose The verbosity mode value.
134+
* @return An argument_descriptor instance for the argument.
135+
*/
136+
[[nodiscard]] detail::argument_descriptor desc(const bool verbose) const noexcept override {
137+
detail::argument_descriptor desc(this->_name.str(), this->_help_msg);
138+
139+
if (not verbose)
140+
return desc;
141+
142+
if constexpr (detail::c_writable<value_type>) {
143+
if (not this->_choices.empty())
144+
desc.add_range_param("choices", this->_choices);
145+
}
146+
147+
return desc;
148+
}
149+
131150
/// @return True if the positional argument is required, false otherwise
132151
[[nodiscard]] bool is_required() const noexcept override {
133152
return this->_required;

include/ap/argument_parser.hpp

Lines changed: 94 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
33
// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
44

5+
/**
6+
* @file argument_parser.hpp
7+
* @brief Main library header file. Defines the `argument_parser` class.
8+
*/
9+
510
#pragma once
611

712
#include "argument/default.hpp"
@@ -11,6 +16,7 @@
1116
#include "detail/concepts.hpp"
1217

1318
#include <algorithm>
19+
#include <format>
1420
#include <ranges>
1521
#include <span>
1622

@@ -68,6 +74,17 @@ class argument_parser {
6874
return *this;
6975
}
7076

77+
/**
78+
* @brief Set the verbosity mode.
79+
* @note The default verbosity mode value is `false`.
80+
* @param v The verbosity mode value.
81+
* @return Reference to the argument parser.
82+
*/
83+
argument_parser& verbose(const bool v = true) noexcept {
84+
this->_verbose = v;
85+
return *this;
86+
}
87+
7188
/**
7289
* @brief Set default positional arguments.
7390
* @tparam AR Type of the positional argument discriminator range.
@@ -87,7 +104,7 @@ class argument_parser {
87104
* @return Reference to the argument parser.
88105
*/
89106
argument_parser& default_positional_arguments(
90-
std::initializer_list<argument::default_positional> arg_discriminator_list
107+
const std::initializer_list<argument::default_positional> arg_discriminator_list
91108
) noexcept {
92109
return this->default_positional_arguments<>(arg_discriminator_list);
93110
}
@@ -111,7 +128,7 @@ class argument_parser {
111128
* @return Reference to the argument parser.
112129
*/
113130
argument_parser& default_optional_arguments(
114-
std::initializer_list<argument::default_optional> arg_discriminator_list
131+
const std::initializer_list<argument::default_optional> arg_discriminator_list
115132
) noexcept {
116133
return this->default_optional_arguments<>(arg_discriminator_list);
117134
}
@@ -418,23 +435,41 @@ class argument_parser {
418435

419436
/**
420437
* @brief Prints the argument parser's details to an output stream.
438+
* @param verbose The verbosity mode value.
421439
* @param os Output stream.
422-
* @param parser The argument parser to print.
423-
* @return The modified output stream.
424440
*/
425-
friend std::ostream& operator<<(std::ostream& os, const argument_parser& parser) noexcept {
426-
if (parser._program_name)
427-
os << parser._program_name.value() << std::endl;
441+
void print_config(const bool verbose, std::ostream& os = std::cout) const noexcept {
442+
if (this->_program_name)
443+
os << "Program: " << this->_program_name.value() << std::endl;
428444

429-
if (parser._program_description)
430-
os << parser._program_description.value() << std::endl;
445+
if (this->_program_description)
446+
os << "\n"
447+
<< std::string(this->_indent_width, ' ') << this->_program_description.value()
448+
<< std::endl;
431449

432-
for (const auto& argument : parser._positional_args)
433-
os << "\t" << *argument << std::endl;
450+
if (not this->_positional_args.empty()) {
451+
os << "\nPositional arguments:\n";
452+
this->_print(os, this->_positional_args);
453+
}
434454

435-
for (const auto& argument : parser._optional_args)
436-
os << "\t" << *argument << std::endl;
455+
if (not this->_optional_args.empty()) {
456+
os << "\nOptional arguments: [--,-]\n";
457+
this->_print(os, this->_optional_args);
458+
}
459+
}
437460

461+
/**
462+
* @brief Prints the argument parser's details to an output stream.
463+
*
464+
* An `os << parser` operation is equivalent to a `parser.print_config(_verbose, os)` call,
465+
* where `_verbose` is the inner verbosity mode, which can be set with the @ref verbose function.
466+
*
467+
* @param os Output stream.
468+
* @param parser The argument parser to print.
469+
* @return The modified output stream.
470+
*/
471+
friend std::ostream& operator<<(std::ostream& os, const argument_parser& parser) noexcept {
472+
parser.print_config(parser._verbose, os);
438473
return os;
439474
}
440475

@@ -547,10 +582,10 @@ class argument_parser {
547582
*/
548583
[[nodiscard]] bool _is_flag(const std::string& arg) const noexcept {
549584
if (arg.starts_with(this->_flag_prefix))
550-
return this->_is_arg_name_used({arg.substr(this->_flag_prefix_length)});
585+
return this->_is_arg_name_used({arg.substr(this->_primary_flag_prefxi_length)});
551586

552587
if (arg.starts_with(this->_flag_prefix_char))
553-
return this->_is_arg_name_used({arg.substr(this->_flag_prefix_char_length)});
588+
return this->_is_arg_name_used({arg.substr(this->_secondary_flag_prefix_length)});
554589

555590
return false;
556591
}
@@ -561,9 +596,9 @@ class argument_parser {
561596
*/
562597
void _strip_flag_prefix(std::string& arg_flag) const noexcept {
563598
if (arg_flag.starts_with(this->_flag_prefix))
564-
arg_flag.erase(0, this->_flag_prefix_length);
599+
arg_flag.erase(0, this->_primary_flag_prefxi_length);
565600
else
566-
arg_flag.erase(0, this->_flag_prefix_char_length);
601+
arg_flag.erase(0, this->_secondary_flag_prefix_length);
567602
}
568603

569604
/**
@@ -694,20 +729,55 @@ class argument_parser {
694729
return std::nullopt;
695730
}
696731

732+
/**
733+
* @brief Print the given argument list to an output stream.
734+
* @param os The output stream to print to.
735+
* @param args The argument list to print.
736+
*/
737+
void _print(std::ostream& os, const arg_ptr_list_t& args) const noexcept {
738+
if (this->_verbose) {
739+
for (const auto& arg : args)
740+
os << '\n' << arg->desc(this->_verbose).get(this->_indent_width) << '\n';
741+
}
742+
else {
743+
std::vector<detail::argument_descriptor> descriptors;
744+
descriptors.reserve(args.size());
745+
746+
for (const auto& arg : args)
747+
descriptors.emplace_back(arg->desc(this->_verbose));
748+
749+
std::size_t max_arg_name_length = 0ull;
750+
for (const auto& desc : descriptors)
751+
max_arg_name_length = std::max(max_arg_name_length, desc.name.length());
752+
753+
for (const auto& desc : descriptors)
754+
os << '\n' << desc.get_basic(this->_indent_width, max_arg_name_length);
755+
756+
os << '\n';
757+
}
758+
}
759+
697760
std::optional<std::string> _program_name;
698761
std::optional<std::string> _program_description;
762+
bool _verbose = false;
699763

700764
arg_ptr_list_t _positional_args;
701765
arg_ptr_list_t _optional_args;
702766

703-
static constexpr uint8_t _flag_prefix_char_length = 1u;
704-
static constexpr uint8_t _flag_prefix_length = 2u;
767+
static constexpr uint8_t _secondary_flag_prefix_length = 1u;
768+
static constexpr uint8_t _primary_flag_prefxi_length = 2u;
705769
static constexpr char _flag_prefix_char = '-';
706770
static constexpr std::string _flag_prefix = "--";
771+
static constexpr uint8_t _indent_width = 2;
707772
};
708773

709774
namespace detail {
710775

776+
/**
777+
* @brief Adds a predefined/default positional argument to the parser.
778+
* @param arg_discriminator The default argument discriminator.
779+
* @param arg_parser The argument parser to which the argument will be added.
780+
*/
711781
inline void add_default_argument(
712782
const argument::default_positional arg_discriminator, argument_parser& arg_parser
713783
) noexcept {
@@ -724,6 +794,11 @@ inline void add_default_argument(
724794
}
725795
}
726796

797+
/**
798+
* @brief Adds a predefined/default optional argument to the parser.
799+
* @param arg_discriminator The default argument discriminator.
800+
* @param arg_parser The argument parser to which the argument will be added.
801+
*/
727802
inline void add_default_argument(
728803
const argument::default_optional arg_discriminator, argument_parser& arg_parser
729804
) noexcept {

0 commit comments

Comments
 (0)