CPP-AP 2.7.0
Command-line argument parser for C++20
Loading...
Searching...
No Matches
positional.hpp
Go to the documentation of this file.
1// Copyright (c) 2023-2025 Jakub MusiaƂ
2// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
3// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
4
6
7#pragma once
8
13
14#ifdef AP_TESTING
15
16namespace ap_testing {
17struct positional_argument_test_fixture;
18} // namespace ap_testing
19
20#endif
21
22namespace ap::argument {
23
28template <detail::c_argument_value_type T = std::string>
30public:
31 using value_type = T;
32
33 positional() = delete;
34
40
41 ~positional() = default;
42
48 bool operator==(const positional& other) const noexcept {
49 return this->_name == other._name;
50 }
51
57 positional& help(std::string_view help_msg) noexcept {
58 this->_help_msg = help_msg;
59 return *this;
60 }
61
67 positional& hidden(const bool h = true) noexcept {
68 this->_hidden = h;
69 return *this;
70 }
71
78 positional& required(const bool r = true) noexcept {
79 this->_required = r;
80 if (this->_required)
81 this->_bypass_required = false;
82 return *this;
83 }
84
91 positional& bypass_required(const bool br = true) noexcept {
92 this->_bypass_required = br;
93 if (this->_bypass_required)
94 this->_required = false;
95 return *this;
96 }
97
106 template <detail::c_range_of<value_type, detail::type_validator::convertible> CR>
107 positional& choices(const CR& choices) noexcept
108 requires(std::equality_comparable<value_type>)
109 {
110 for (const auto& choice : choices)
111 this->_choices.emplace_back(choice);
112 return *this;
113 }
114
121 positional& choices(std::initializer_list<value_type> choices) noexcept
122 requires(std::equality_comparable<value_type>)
123 {
124 return this->choices<>(choices);
125 }
126
133 positional& default_value(const std::convertible_to<value_type> auto& default_value) noexcept {
134 this->_default_value = std::make_any<value_type>(default_value);
135 this->_required = false;
136 return *this;
137 }
138
146 template <action::detail::c_value_action_specifier AS, std::invocable<value_type&> F>
147 positional& action(F&& action) noexcept {
149 this->_value_actions.emplace_back(std::forward<callable_type>(action));
150 return *this;
151 }
152
154 friend class ::ap::argument_parser;
155
156#ifdef AP_TESTING
160 friend struct ::ap_testing::positional_argument_test_fixture;
161#endif
162
163private:
166
171 [[nodiscard]] detail::argument_descriptor desc(const bool verbose) const noexcept override {
172 detail::argument_descriptor desc(this->_name.str(), this->_help_msg);
173
174 if (not verbose)
175 return desc;
176
177 if (not this->_required)
178 desc.add_param("required", "false");
179 if (this->bypass_required_enabled())
180 desc.add_param("bypass required", "true");
181 if constexpr (detail::c_writable<value_type>) {
182 if (not this->_choices.empty())
183 desc.add_range_param("choices", this->_choices);
184 if (this->_default_value.has_value())
185 desc.add_param("default value", std::any_cast<value_type>(this->_default_value));
186 }
187
188 return desc;
189 }
190
195 bool mark_used() override {
196 return false;
197 }
198
200 [[nodiscard]] bool is_used() const noexcept override {
201 return this->_value.has_value();
202 }
203
205 [[nodiscard]] std::size_t count() const noexcept override {
206 return static_cast<std::size_t>(this->has_parsed_values());
207 }
208
215 bool set_value(const std::string& str_value) override {
216 if (this->_value.has_value())
218
221 value = value_type(str_value);
222 }
223 else {
224 if (not (std::istringstream(str_value) >> value))
225 throw parsing_failure::invalid_value(this->_name, str_value);
226 }
227
228 if (not detail::is_valid_choice(value, this->_choices))
229 throw parsing_failure::invalid_choice(this->_name, str_value);
230
231 const auto apply_visitor = action::detail::apply_visitor<value_type>{value};
232 for (const auto& action : this->_value_actions)
233 std::visit(apply_visitor, action);
234
235 this->_value = value;
236 return false;
237 }
238
240 [[nodiscard]] bool has_value() const noexcept override {
241 return this->has_parsed_values() or this->_default_value.has_value();
242 }
243
245 [[nodiscard]] bool has_parsed_values() const noexcept override {
246 return this->_value.has_value();
247 }
248
250 [[nodiscard]] std::weak_ordering nvalues_ordering() const noexcept override {
251 if (not this->_required)
252 return std::weak_ordering::equivalent;
253
254 return this->has_value() ? std::weak_ordering::equivalent : std::weak_ordering::less;
255 }
256
261 [[nodiscard]] const std::any& value() const override {
262 if (this->has_parsed_values())
263 return this->_value;
264
265 if (this->_default_value.has_value())
266 return this->_default_value;
267
268 throw std::logic_error(
269 std::format("No value parsed for the `{}` positional argument.", this->_name.str())
270 );
271 }
272
277 [[nodiscard]] const std::vector<std::any>& values() const override {
278 throw std::logic_error(
279 std::format("Positional argument `{}` has only 1 value.", this->_name.str())
280 );
281 }
282
284 std::vector<value_type> _choices;
285 std::vector<value_action_type> _value_actions;
286
287 std::any _value;
288};
289
290} // namespace ap::argument
Defines the base argument class and common utility.
The positional argument class.
bool set_value(const std::string &str_value) override
Set the value for the positional argument.
std::any _value
Stored value of the positional argument.
positional & bypass_required(const bool br=true) noexcept
Enable/disable bypassing the required attributeattribute for the positional argument.
positional & required(const bool r=true) noexcept
Set the required attribute of the positional argument.
bool operator==(const positional &other) const noexcept
Equality operator for positional argument.
std::weak_ordering nvalues_ordering() const noexcept override
positional & default_value(const std::convertible_to< value_type > auto &default_value) noexcept
Set the default value for the positional argument.
positional & action(F &&action) noexcept
Set the action for the positional argument.
positional & choices(const CR &choices) noexcept
Set the choices for the positional argument.
bool is_used() const noexcept override
bool mark_used() override
Mark the positional argument as used.
action::detail::value_action_variant_type< T > value_action_type
The argument's value action type.
detail::argument_descriptor desc(const bool verbose) const noexcept override
const std::vector< std::any > & values() const override
positional & help(std::string_view help_msg) noexcept
Set the help message for the positional argument.
positional & hidden(const bool h=true) noexcept
Set the hidden attribute for the positional argument.
std::vector< value_action_type > _value_actions
T value_type
The argument's value type.
positional & choices(std::initializer_list< value_type > choices) noexcept
Set the choices for the positional argument.
positional(const detail::argument_name &name)
Constructor for positional argument with the name identifier.
const std::any & value() const override
Get the stored value of the positional argument.
std::size_t count() const noexcept override
bool has_value() const noexcept override
bool has_parsed_values() const noexcept override
std::vector< value_type > _choices
Argument class interface.
const ap::detail::argument_name _name
const ap::detail::argument_name & name() const noexcept
bool bypass_required_enabled() const noexcept
std::optional< std::string > _help_msg
argument_base(const argument_name &name, const bool required=false)
A structure used to represent an argument's description.
void add_param(const std::string &param_name, const std::string &value)
Adds a parameter descriptor with the given string value.
void add_range_param(const std::string &param_name, const R &range, const std::string_view delimiter=default_delimiter)
Adds a range parameter descriptor with the given value.
The concept is satisfied when T can be constructed from const std::string&.
Definition concepts.hpp:29
The concept is satisfied when T overloads the std::ostream operator <<.
Definition concepts.hpp:36
Provides the general concept definitions.
typename AS::template type< T > callable_type
Template argument action callable type alias.
Definition utility.hpp:36
std::variant< callable_type< action_type::observe, T >, callable_type< action_type::transform, T >, callable_type< action_type::modify, T > > value_action_variant_type
Template argument action callabla variant type alias.
Definition utility.hpp:43
bool is_valid_choice(const T &value, const std::vector< T > &choices) noexcept
Checks if the provided choice is valid for the given set of choices.
A visitor structure used to apply value actions.
Definition utility.hpp:50
Structure holding the argument's name.
std::string str() const noexcept
Get a string representation of the argument_name.
static parsing_failure invalid_value(const detail::argument_name &arg_name, const std::string &value) noexcept
static parsing_failure invalid_choice(const detail::argument_name &arg_name, const std::string &value) noexcept
static parsing_failure value_already_set(const detail::argument_name &arg_name) noexcept
Defines general action-related utility.