Lector v1.0.0
C++ library for parsing command line arguments.
arguments.hpp
1// Copyright © 2026, Alexandre Coderre-Chabot.
2
3// This file is part of Lector (https://github.com/acodcha/lector), a C++ library for parsing
4// command line arguments. Lector is licensed under the MIT License (https://mit-license.org).
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
7// associated documentation files (the "Software"), to deal in the Software without restriction,
8// including without limitation the rights to use, copy, modify, merge, publish, distribute,
9// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11// - The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13// - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
17// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
19#ifndef LECTOR_ARGUMENTS_HPP
20#define LECTOR_ARGUMENTS_HPP
21
22#include <algorithm>
23#include <array>
24#include <cstddef>
25#include <cstdint>
26#include <filesystem>
27#include <limits>
28#include <optional>
29#include <stdexcept>
30#include <string>
31#include <string_view>
32#include <tuple>
33#include <type_traits>
34#include <unordered_set>
35#include <utility>
36#include <vector>
37
38#include "lector/parse.hpp"
39#include "lector/print.hpp"
40#include "lector/text.hpp"
41
42/// @brief The Lector library's namespace.
43namespace lector {
44
45/// @brief Arity of a command line argument.
46enum class Arity : std::int8_t {
47 /// @brief Unknown, unspecified, or invalid command line argument arity.
48 Unknown = 0,
49
50 /// @brief The command line argument has singular arity; it can only appear once on the command
51 /// line.
52 Singular = 1,
53
54 /// @brief The command line argument has repeatable arity; it can appear multiple times on the
55 /// command line. If the argument is a named repeatable argument, each appearance must include
56 /// both its key and its value. If the argument is a positional repeatable argument, its multiple
57 /// values must appear in an uninterrupted sequence.
58 Repeatable = 2,
59};
60
61/// @brief Specialization of the lector::Names constant for the lector::Arity enumeration.
62template <>
63inline constexpr std::array<lector::Name<lector::Arity>, 3> Names<lector::Arity>{
64 {
65 {lector::Arity::Unknown, "Unknown"},
66 {lector::Arity::Singular, "Singular"},
67 {lector::Arity::Repeatable, "Repeatable"},
68 }
69};
70
71/// @brief Specialization of the lector::Spellings constant for the lector::Arity enumeration.
72template <>
73inline constexpr std::array<lector::Spelling<lector::Arity>, 9> Spellings<lector::Arity>{
74 {
75 {"Unknown", lector::Arity::Unknown},
76 {"Singular", lector::Arity::Singular},
77 {"Repeatable", lector::Arity::Repeatable},
78 {"unknown", lector::Arity::Unknown},
79 {"singular", lector::Arity::Singular},
80 {"repeatable", lector::Arity::Repeatable},
81 {"UNKNOWN", lector::Arity::Unknown},
82 {"SINGULAR", lector::Arity::Singular},
83 {"REPEATABLE", lector::Arity::Repeatable},
84 }
85};
86
87/// @brief Form of a command line argument.
88enum class Form : std::int8_t {
89 /// @brief Unknown, unspecified, or invalid command line argument form.
90 Unknown = 0,
91
92 /// @brief The command line argument is a positional argument; it does not define any keys and
93 /// must be specified in a specific order on the command line.
94 Positional = 1,
95
96 /// @brief The command line argument is a named argument; it defines one or more keys and is
97 /// specified on the command line by one of its keys. Named arguments can be specified in any
98 /// order on the command line.
99 Named = 2,
100};
101
102/// @brief Specialization of the lector::Names constant for the lector::Form enumeration.
103template <>
104inline constexpr std::array<lector::Name<lector::Form>, 3> Names<lector::Form>{
105 {
106 {lector::Form::Unknown, "Unknown"},
107 {lector::Form::Positional, "Positional"},
108 {lector::Form::Named, "Named"},
109 }
110};
111
112/// @brief Specialization of the lector::Spellings constant for the lector::Form enumeration.
113template <>
114inline constexpr std::array<lector::Spelling<lector::Form>, 9> Spellings<lector::Form>{
115 {
116 {"Unknown", lector::Form::Unknown},
117 {"Positional", lector::Form::Positional},
118 {"Named", lector::Form::Named},
119 {"unknown", lector::Form::Unknown},
120 {"positional", lector::Form::Positional},
121 {"named", lector::Form::Named},
122 {"UNKNOWN", lector::Form::Unknown},
123 {"POSITIONAL", lector::Form::Positional},
124 {"NAMED", lector::Form::Named},
125 }
126};
127
128/// @brief Importance of a command line argument.
129enum class Importance : std::int8_t {
130 /// @brief Unknown, unspecified, or invalid command line argument importance.
131 Unknown = 0,
132
133 /// @brief The command line argument is optional; it may or may not be provided by the user.
134 Optional = 1,
135
136 /// @brief The command line argument is required; it must be provided by the user.
137 Required = 2,
138};
139
140/// @brief Specialization of the lector::Names constant for the lector::Importance enumeration.
141template <>
142inline constexpr std::array<lector::Name<lector::Importance>, 3> Names<lector::Importance>{
143 {
144 {lector::Importance::Unknown, "Unknown"},
145 {lector::Importance::Optional, "Optional"},
146 {lector::Importance::Required, "Required"},
147 }
148};
149
150/// @brief Specialization of the lector::Spellings constant for the lector::Importance enumeration.
151template <>
152inline constexpr std::array<lector::Spelling<lector::Importance>, 9> Spellings<lector::Importance>{
153 {
154 {"Unknown", lector::Importance::Unknown},
155 {"Optional", lector::Importance::Optional},
156 {"Required", lector::Importance::Required},
157 {"unknown", lector::Importance::Unknown},
158 {"optional", lector::Importance::Optional},
159 {"required", lector::Importance::Required},
160 {"UNKNOWN", lector::Importance::Unknown},
161 {"OPTIONAL", lector::Importance::Optional},
162 {"REQUIRED", lector::Importance::Required},
163 }
164};
165
166/// @brief A singular command line argument.
167/// @tparam LabelValue Value of this command line argument's label. The label is used to uniquely
168/// identify this command line argument in a collection of command line arguments.
169/// @tparam Type The type of the value stored in this command line argument.
170template <auto LabelValue, typename Type>
171class SingularArgument final {
172public:
173 using ValueType = Type;
174
175 /// @brief Default constructor. Initializes the singular command line argument with no keys, an
176 /// empty description, required importance, and no default value.
177 SingularArgument() noexcept = default;
178
179 /// @brief Constructor for a singular positional required command line argument. No default value
180 /// is needed.
181 /// @param[in] description The description of the command line argument.
182 /// @throws std::invalid_argument if this argument's type is boolean or if its description is
183 /// empty.
184 explicit SingularArgument(const std::string_view description) : description_{description} {
185 validate_non_boolean_positional();
186 validate_description();
187 }
188
189 /// @brief Constructor for a singular named required command line argument or a singular named
190 /// boolean command line argument. No default value is needed. Singular named boolean command line
191 /// arguments are always optional and always default to false.
192 /// @param[in] keys The keys used to specify the command line argument.
193 /// @param[in] description The description of the command line argument.
194 /// @throws std::invalid_argument if the keys are invalid or if the description is empty.
195 SingularArgument(const std::vector<std::string>& keys, const std::string_view description)
196 : keys_{keys}, description_{description},
197 importance_{
198 std::is_same_v<Type, bool> ? lector::Importance::Optional : lector::Importance::Required} {
199 set_default_value_to_false_if_boolean();
200 validate_keys();
201 validate_description();
202 }
203
204 /// @brief Constructor for a singular positional optional non-boolean command line argument. A
205 /// default value must be provided.
206 /// @param[in] description The description of the command line argument.
207 /// @param[in] default_value The default value of the command line argument.
208 /// @throws std::invalid_argument if this argument's type is boolean or if its description is
209 /// empty.
210 SingularArgument(const std::string_view description, const Type& default_value)
211 : description_{description}, default_value_{default_value},
212 importance_{lector::Importance::Optional} {
213 validate_non_boolean_default_value();
214 validate_description();
215 }
216
217 /// @brief Constructor for a singular named optional non-boolean command line argument. A default
218 /// value must be provided.
219 /// @param[in] keys The keys used to specify the command line argument.
220 /// @param[in] description The description of the command line argument.
221 /// @param[in] default_value The default value of the command line argument.
222 /// @throws std::invalid_argument if this argument's type is boolean, if its keys are invalid, or
223 /// if its description is empty.
224 SingularArgument(const std::vector<std::string>& keys, const std::string_view description,
225 const Type& default_value)
226 : keys_{keys}, description_{description}, default_value_{default_value},
227 importance_{lector::Importance::Optional} {
228 validate_non_boolean_default_value();
229 validate_keys();
230 validate_description();
231 }
232
233 /// @brief Destructor. Destroys this singular command line argument.
234 ~SingularArgument() noexcept = default;
235
236 /// @brief Copy constructor. Constructs a singular command line argument by copying another one.
237 SingularArgument(const lector::SingularArgument<LabelValue, Type>&) = default;
238
239 /// @brief Copy assignment operator. Assigns this singular command line argument by copying
240 /// another one.
241 /// @return This singular command line argument after the assignment.
242 lector::SingularArgument<LabelValue, Type>& operator=(
243 const lector::SingularArgument<LabelValue, Type>&) = default;
244
245 /// @brief Move constructor. Constructs a singular command line argument by moving another one.
246 SingularArgument(lector::SingularArgument<LabelValue, Type>&&) noexcept = default;
247
248 /// @brief Move assignment operator. Assigns this singular command line argument by moving another
249 /// one.
250 /// @return This singular command line argument after the assignment.
251 lector::SingularArgument<LabelValue, Type>& operator=(
252 lector::SingularArgument<LabelValue, Type>&&) noexcept = default;
253
254 /// @brief Label of this command line argument. Used to uniquely identify this command line
255 /// argument in a collection of command line arguments. Set at construction.
256 /// @return The label of this command line argument.
257 [[nodiscard]] static constexpr auto label() noexcept {
258 return LabelValue;
259 }
260
261 /// @brief Keys that can be used to specify this argument on the command line if it is a named
262 /// argument, or an empty collection if this argument is a positional argument. Set at
263 /// construction.
264 /// @return The keys that can be used to specify this command line argument.
265 [[nodiscard]] const std::vector<std::string>& keys() const noexcept {
266 return keys_;
267 }
268
269 /// @brief Description of this command line argument. Set at construction.
270 /// @return The description of this command line argument.
271 [[nodiscard]] std::string_view description() const noexcept {
272 return description_;
273 }
274
275 /// @brief Returns whether this singular command line argument has a default value.
276 /// @return True if this singular command line argument has a default value; false if it does not.
277 [[nodiscard]] bool has_default() const noexcept {
278 return default_value_.has_value();
279 }
280
281 /// @brief Default value of this singular command line argument if it is optional and non-boolean,
282 /// or std::nullopt otherwise. Set at construction.
283 /// @return The default value of this singular command line argument.
284 [[nodiscard]] const std::optional<Type>& default_value() const noexcept {
285 return default_value_;
286 }
287
288 /// @brief Returns whether this singular command line argument has a parsed value.
289 /// @return True if this singular command line argument has a parsed value; false if it does not.
290 [[nodiscard]] bool has_parsed() const noexcept {
291 return parsed_value_.has_value();
292 }
293
294 /// @brief Parsed value of this singular command line argument. Set when this argument is parsed
295 /// from the command line.
296 /// @return The parsed value of this singular command line argument.
297 [[nodiscard]] const std::optional<Type>& parsed_value() const noexcept {
298 return parsed_value_;
299 }
300
301 /// @brief Importance of this command line argument. A required argument must be provided by the
302 /// user on the command line, whereas an optional argument may or may not be provided by the user
303 /// on the command line. Set at construction.
304 /// @return The importance of this command line argument.
305 [[nodiscard]] lector::Importance importance() const noexcept {
306 return importance_;
307 }
308
309 /// @brief Form of this command line argument. A positional argument does not define any keys and
310 /// must be specified in a specific order on the command line, whereas a named argument defines
311 /// one or more keys and is specified on the command line by one of its keys. Named arguments can
312 /// be specified in any order on the command line.
313 /// @return The form of this command line argument.
314 [[nodiscard]] lector::Form form() const noexcept {
315 if (keys_.empty()) {
317 }
318 return lector::Form::Named;
319 }
320
321 /// @brief Arity of this command line argument. A singular argument can only appear once on the
322 /// command line, whereas a repeatable argument can appear multiple times.
323 /// @return The arity of this command line argument.
324 [[nodiscard]] static lector::Arity arity() noexcept {
326 }
327
328 /// @brief Value of this singular command line argument. Returns the parsed value if it exists;
329 /// otherwise, returns the default value.
330 /// @return The value of this singular command line argument.
331 /// @throws std::logic_error if this singular command line argument is missing both its parsed
332 /// value and its default value. Cannot occur in practice due to checks done at construction.
333 [[nodiscard]] const Type& parsed_or_default_value() const {
334 if (parsed_value_.has_value()) {
335 return parsed_value_.value();
336 }
337 if (default_value_.has_value()) {
338 return default_value_.value();
339 }
340 throw std::logic_error("No parsed or default value for argument '" + options() + "'.");
341 }
342
343 /// @brief Sets the parsed value of this singular command line argument.
344 /// @param[in] value The parsed value to set.
345 /// @throws std::logic_error if this singular command line argument is default-constructed or if a
346 /// parsed value has already been set for this singular command line argument.
347 /// @throws std::invalid_argument if this singular command line argument is boolean and the parsed
348 /// value is false.
349 void set_parsed_value(const Type& value) {
350 if (description_.empty()) {
351 throw std::logic_error("Default-constructed arguments cannot parse values.");
352 }
353 if constexpr (std::is_same_v<Type, bool>) {
354 if (!value) {
355 throw std::invalid_argument("Boolean arguments can only be parsed as true.");
356 }
357 }
358 if (parsed_value_.has_value()) {
359 throw std::logic_error("A singular argument cannot have more than one parsed value.");
360 }
361 parsed_value_ = value;
362 }
363
364 /// @brief Prints the longest key of this command line argument with its associated value type as
365 /// a string of text.
366 /// @return The string of text that contains the longest key of this command line argument with
367 /// its associated value type.
368 [[nodiscard]] std::string longest_key_with_value_type() const {
369 if (keys_.empty()) {
370 return std::string{value_type()};
371 }
372 std::string result{longest_key()};
373 const std::string type{value_type()};
374 if (!type.empty()) {
375 result.push_back(' ');
376 result.append(type);
377 }
378 return result;
379 }
380
381 /// @brief Prints the keys and value type of this command line argument as a string of text.
382 /// @return The string of text that contains the keys and value type of this command line
383 /// argument.
384 [[nodiscard]] std::string keys_with_value_type() const {
385 if (keys_.empty()) {
386 return std::string{value_type()};
387 }
388 std::string result;
389 for (std::size_t index{0UL}; index < keys_.size(); ++index) {
390 const std::string type{value_type()};
391 result.append(keys_.at(index));
392 if (!type.empty()) {
393 result.push_back(' ');
394 result.append(type);
395 }
396 if (index + 1 < keys_.size()) {
397 result.append(", ");
398 }
399 }
400 return result;
401 }
402
403 /// @brief Prints the usage information of this command line argument as a string of text. The
404 /// usage information consists of this command line argument's longest key and value type,
405 /// enclosed in square braces if this command line argument is optional.
406 /// @return The string of text that contains the usage information of this command line argument.
407 [[nodiscard]] std::string usage() const {
409 return "[" + longest_key_with_value_type() + "]";
410 }
412 }
413
414 /// @brief Prints the options information of this command line argument as a string of text. The
415 /// options information consists of this command line argument's keys, value type, and
416 /// description.
417 /// @return The string of text that contains the options information of this command line
418 /// argument.
419 [[nodiscard]] std::string options() const {
420 std::string keys_with_value_type_{keys_with_value_type()};
421 if (keys_with_value_type_.empty()) {
422 return description_;
423 }
424 if (description_.empty()) {
425 return keys_with_value_type_;
426 }
427 return keys_with_value_type_ + " " + description_;
428 }
429
430 /// @brief Prints the execution of this command line argument as a string of text. The execution
431 /// consists of this command line argument's longest key and parsed value, if any.
432 /// @return The string of text that contains the execution of this command line argument.
433 [[nodiscard]] std::string execution() const {
434 if constexpr (std::is_same_v<Type, bool>) {
435 return execution_boolean();
436 } else {
437 return execution_non_boolean();
438 }
439 }
440
441private:
442 /// @brief If this command line argument is boolean, sets its default value to false. Boolean
443 /// command line arguments are always optional and always default to false.
444 void set_default_value_to_false_if_boolean() {
445 if constexpr (std::is_same_v<Type, bool>) {
446 default_value_ = false;
447 }
448 }
449
450 /// @brief Validates that this positional command line argument is not boolean. Boolean command
451 /// line arguments must always specify one or more keys and therefore cannot be positional.
452 void validate_non_boolean_positional() const {
453 if constexpr (std::is_same_v<Type, bool>) {
454 throw std::invalid_argument("Boolean arguments must specify one or more keys.");
455 }
456 }
457
458 /// @brief Validates that this command line argument that specifies a default value is not
459 /// boolean. Boolean command line arguments are always optional and always default to false, so
460 /// they cannot specify default values.
461 void validate_non_boolean_default_value() const {
462 if constexpr (std::is_same_v<Type, bool>) {
463 throw std::invalid_argument(
464 "Boolean arguments cannot specify default values; they are always false by default.");
465 }
466 }
467
468 /// @brief Validates the keys of this command line argument.
469 /// @throws std::logic_error if the keys are missing or invalid.
470 void validate_keys() const {
471 if (keys_.empty()) {
472 throw std::logic_error("All named arguments must each have at least one key.");
473 }
474 for (const std::string& key : keys_) {
475 if (key.empty()) {
476 throw std::logic_error("Empty key in named argument '" + options()
477 + "'. Named arguments cannot have empty keys.");
478 }
479 }
480 const std::size_t keys_size{keys_.size()};
481 for (std::size_t first{0UL}; first < keys_size; ++first) {
482 for (std::size_t second{first + static_cast<std::size_t>(1UL)}; second < keys_size;
483 ++second) {
484 if (keys_.at(first) == keys_.at(second)) {
485 throw std::logic_error("Duplicate key '" + keys_.at(first) + "' in named argument '"
486 + options() + "'. Named arguments cannot have duplicate keys.");
487 }
488 }
489 }
490 }
491
492 /// @brief Validates the description of this command line argument.
493 /// @throws std::logic_error if the description of this command line argument is empty.
494 void validate_description() const {
495 if (description_.empty()) {
496 throw std::logic_error("Empty description in argument '" + options()
497 + "'. All arguments must have descriptions.");
498 }
499 }
500
501 /// @brief Prints the value type of this command line argument as a string of text.
502 /// @return The string of text that contains the value type of this command line argument.
503 [[nodiscard]] constexpr std::string_view value_type() const {
504 if constexpr (std::is_same_v<Type, bool>) {
505 return "";
506 } else if constexpr (std::numeric_limits<Type>::is_integer) {
507 return "<number>";
508 } else if constexpr (std::is_floating_point_v<Type>) {
509 return "<value>";
510 } else if constexpr (
511 std::is_same_v<Type, std::string> || std::is_same_v<Type, std::string_view>) {
512 return "<text>";
513 } else if constexpr (std::is_same_v<Type, std::filesystem::path>) {
514 return "<path>";
515 } else {
516 return "<value>";
517 }
518 }
519
520 /// @brief Returns the longest key of this command line argument.
521 /// @return The longest key of this command line argument.
522 /// @throws std::out_of_range if this command line argument has no keys.
523 [[nodiscard]] const std::string& longest_key() const {
524 std::size_t longest_key_index{0UL};
525 std::size_t longest_key_length{0UL};
526 for (std::size_t index{0UL}; index < keys_.size(); ++index) {
527 const std::size_t length{lector::count_code_points(keys_.at(index))};
528 if (length > longest_key_length) {
529 longest_key_index = index;
530 longest_key_length = length;
531 }
532 }
533 return keys_.at(longest_key_index);
534 }
535
536 /// @brief Prints the execution of this boolean command line argument as a string of text. The
537 /// execution consists of this boolean command line argument's longest key.
538 /// @return The string of text that contains the execution of this boolean command line argument.
539 [[nodiscard]] std::string execution_boolean() const {
540 if (parsed_value_.has_value() && parsed_value_.value()) {
541 return longest_key();
542 }
543 return std::string{};
544 }
545
546 /// @brief Prints the execution of this non-boolean command line argument as a string of text. The
547 /// execution consists of this non-boolean command line argument's longest key and parsed value,
548 /// if any.
549 /// @return The string of text that contains the execution of this non-boolean command line
550 /// argument.
551 [[nodiscard]] std::string execution_non_boolean() const {
552 if (parsed_value_.has_value()) {
553 if (keys_.empty()) {
554 return lector::quote_if_contains_whitespace(lector::print<Type>(parsed_value_.value()));
555 }
556 return longest_key() + " "
557 + lector::quote_if_contains_whitespace(lector::print<Type>(parsed_value_.value()));
558 }
559 return std::string{};
560 }
561
562 /// @brief Keys that can be used to specify this argument on the command line if it is a named
563 /// argument, or an empty collection if this argument is a positional argument. Set at
564 /// construction.
565 std::vector<std::string> keys_;
566
567 /// @brief Description of this command line argument. Set at construction.
568 std::string description_;
569
570 /// @brief Default value of this singular command line argument if it is optional and non-boolean,
571 /// or std::nullopt otherwise. Set at construction.
572 std::optional<Type> default_value_;
573
574 /// @brief Parsed value of this singular command line argument. Set when this argument is parsed
575 /// from the command line.
576 std::optional<Type> parsed_value_;
577
578 /// @brief Importance of this command line argument. A required argument must be provided by the
579 /// user on the command line, whereas an optional argument may or may not be provided by the user
580 /// on the command line. Set at construction.
582};
583
584/// @brief A repeatable command line argument.
585/// @tparam LabelValue Value of this command line argument's label. The label is used to uniquely
586/// identify this command line argument in a collection of command line arguments.
587/// @tparam Type The type of the value stored in this command line argument.
588template <auto LabelValue, typename Type>
590public:
591 using ValueType = Type;
592
593 /// @brief Default constructor. Initializes the repeatable command line argument with no keys, an
594 /// empty description, required importance, and no default values.
595 RepeatableArgument() noexcept = default;
596
597 /// @brief Constructor for a repeatable positional required command line argument. No default
598 /// values are needed.
599 /// @param[in] description The description of the command line argument.
600 /// @throws std::invalid_argument if the type is boolean or if the description is empty.
601 explicit RepeatableArgument(const std::string_view description) : description_{description} {
602 validate_non_boolean_positional();
603 validate_description();
604 }
605
606 /// @brief Constructor for a repeatable named required command line argument or a repeatable named
607 /// optional boolean command line argument. No default value is needed. Repeatable named boolean
608 /// command line arguments are always optional and always default to false.
609 /// @param[in] keys The keys used to specify the command line argument.
610 /// @param[in] description The description of the command line argument.
611 /// @throws std::invalid_argument if the keys are invalid or if the description is empty.
612 RepeatableArgument(const std::vector<std::string>& keys, const std::string_view description)
613 : keys_{keys}, description_{description},
614 importance_{
615 std::is_same_v<Type, bool> ? lector::Importance::Optional : lector::Importance::Required} {
616 validate_keys();
617 validate_description();
618 }
619
620 /// @brief Constructor for a repeatable positional optional non-boolean command line argument.
621 /// @param[in] description The description of the command line argument.
622 /// @param[in] default_values The default values of the command line argument, if any.
623 /// @throws std::invalid_argument if the type is boolean or if the description is empty.
624 RepeatableArgument(const std::string_view description, const std::vector<Type>& default_values)
625 : description_{description}, default_values_{default_values},
626 importance_{lector::Importance::Optional} {
627 validate_non_boolean_default_values();
628 validate_description();
629 }
630
631 /// @brief Constructor for a repeatable named optional non-boolean command line argument.
632 /// @param[in] keys The keys used to specify the command line argument.
633 /// @param[in] description The description of the command line argument.
634 /// @param[in] default_values The default values of the command line argument, if any.
635 /// @throws std::invalid_argument if the type is boolean, if the keys are invalid, or if the
636 /// description is empty.
637 RepeatableArgument(const std::vector<std::string>& keys, const std::string_view description,
638 const std::vector<Type>& default_values)
639 : keys_{keys}, description_{description}, default_values_{default_values},
640 importance_{lector::Importance::Optional} {
641 validate_non_boolean_default_values();
642 validate_keys();
643 validate_description();
644 }
645
646 /// @brief Destructor. Destroys this repeatable command line argument.
647 ~RepeatableArgument() noexcept = default;
648
649 /// @brief Copy constructor. Constructs a repeatable command line argument by copying another one.
650 RepeatableArgument(const lector::RepeatableArgument<LabelValue, Type>&) = default;
651
652 /// @brief Copy assignment operator. Assigns this repeatable command line argument by copying
653 /// another one.
654 /// @return This repeatable command line argument after the assignment.
655 lector::RepeatableArgument<LabelValue, Type>& operator=(
656 const lector::RepeatableArgument<LabelValue, Type>&) = default;
657
658 /// @brief Move constructor. Constructs a repeatable command line argument by moving another one.
659 RepeatableArgument(lector::RepeatableArgument<LabelValue, Type>&&) noexcept = default;
660
661 /// @brief Move assignment operator. Assigns this repeatable command line argument by moving
662 /// another one.
663 /// @return This repeatable command line argument after the assignment.
664 lector::RepeatableArgument<LabelValue, Type>& operator=(
665 lector::RepeatableArgument<LabelValue, Type>&&) noexcept = default;
666
667 /// @brief Label of this command line argument. Used to uniquely identify this command line
668 /// argument in a collection of command line arguments. Set at construction.
669 /// @return The label of this command line argument.
670 [[nodiscard]] static constexpr auto label() noexcept {
671 return LabelValue;
672 }
673
674 /// @brief Keys that can be used to specify this argument on the command line if it is a named
675 /// argument, or an empty collection if this argument is a positional argument. Set at
676 /// construction.
677 /// @return The keys that can be used to specify this command line argument.
678 [[nodiscard]] const std::vector<std::string>& keys() const noexcept {
679 return keys_;
680 }
681
682 /// @brief Description of this command line argument. Set at construction.
683 /// @return The description of this command line argument.
684 [[nodiscard]] std::string_view description() const noexcept {
685 return description_;
686 }
687
688 /// @brief Returns whether this repeatable command line argument has one or more default values.
689 /// @return True if this repeatable command line argument has one or more default values; false if
690 /// it has none.
691 [[nodiscard]] bool has_default() const noexcept {
692 return !default_values_.empty();
693 }
694
695 /// @brief Default values of this repeatable command line argument. Set at construction.
696 /// @return The default values of this repeatable command line argument.
697 [[nodiscard]] const std::vector<Type>& default_values() const noexcept {
698 return default_values_;
699 }
700
701 /// @brief Returns whether this repeatable command line argument has one or more parsed values.
702 /// @return True if this repeatable command line argument has one or more parsed values; false if
703 /// it has none.
704 [[nodiscard]] bool has_parsed() const noexcept {
705 return !parsed_values_.empty();
706 }
707
708 /// @brief Parsed values of this repeatable command line argument. Set when this argument is
709 /// parsed from the command line.
710 /// @return The parsed values of this repeatable command line argument.
711 [[nodiscard]] const std::vector<Type>& parsed_values() const noexcept {
712 return parsed_values_;
713 }
714
715 /// @brief Importance of this command line argument. A required argument must be provided by the
716 /// user on the command line, whereas an optional argument may or may not be provided by the user
717 /// on the command line. Set at construction.
718 /// @return The importance of this command line argument.
719 [[nodiscard]] lector::Importance importance() const noexcept {
720 return importance_;
721 }
722
723 /// @brief Form of this command line argument. A positional argument does not define any keys and
724 /// must be specified in a specific order on the command line, whereas a named argument defines
725 /// one or more keys and is specified on the command line by one of its keys. Named arguments can
726 /// be specified in any order on the command line.
727 /// @return The form of this command line argument.
728 [[nodiscard]] lector::Form form() const noexcept {
729 if (keys_.empty()) {
731 }
732 return lector::Form::Named;
733 }
734
735 /// @brief Arity of this command line argument. A repeatable argument can only appear once on the
736 /// command line, whereas a repeatable argument can appear multiple times.
737 /// @return The arity of this command line argument.
738 [[nodiscard]] static lector::Arity arity() noexcept {
740 }
741
742 /// @brief Values of this repeatable command line argument. Returns the parsed values if they
743 /// exist; otherwise, returns the default values.
744 /// @return The values of this repeatable command line argument.
745 [[nodiscard]] const std::vector<Type>& parsed_or_default_values() const {
746 if (!parsed_values_.empty()) {
747 return parsed_values_;
748 }
749 return default_values_;
750 }
751
752 /// @brief Inserts an additional parsed value into this repeatable command line argument.
753 /// @param[in] value The parsed value to insert.
754 /// @throws std::logic_error if this repeatable command line argument is default-constructed.
755 /// @throws std::invalid_argument if this repeatable command line argument is boolean and the
756 /// parsed value is false.
757 void set_parsed_value(const Type& value) {
758 if (description_.empty()) {
759 throw std::logic_error("Default-constructed arguments cannot parse values.");
760 }
761 if constexpr (std::is_same_v<Type, bool>) {
762 if (!value) {
763 throw std::invalid_argument("Boolean arguments can only be parsed as true.");
764 }
765 }
766 parsed_values_.push_back(value);
767 }
768
769 /// @brief Prints the longest key of this command line argument with its associated value type as
770 /// a string of text.
771 /// @return The string of text that contains the longest key of this command line argument with
772 /// its associated value type.
773 [[nodiscard]] std::string longest_key_with_value_type() const {
774 if (keys_.empty()) {
775 return std::string{value_type()};
776 }
777 std::string result{longest_key()};
778 const std::string type{value_type()};
779 if (!type.empty()) {
780 result.push_back(' ');
781 result.append(type);
782 }
783 return result;
784 }
785
786 /// @brief Prints the keys and value type of this command line argument as a string of text.
787 /// @return The string of text that contains the keys and value type of this command line
788 /// argument.
789 [[nodiscard]] std::string keys_with_value_type() const {
790 if (keys_.empty()) {
791 return std::string{value_type()};
792 }
793 std::string result;
794 for (std::size_t index{0UL}; index < keys_.size(); ++index) {
795 const std::string type{value_type()};
796 result.append(keys_.at(index));
797 if (!type.empty()) {
798 result.push_back(' ');
799 result.append(type);
800 }
801 if (index + 1 < keys_.size()) {
802 result.append(", ");
803 }
804 }
805 return result;
806 }
807
808 /// @brief Prints the usage information of this command line argument as a string of text. The
809 /// usage information consists of this command line argument's longest key and value type,
810 /// enclosed in square braces if this command line argument is optional.
811 /// @return The string of text that contains the usage information of this command line argument.
812 [[nodiscard]] std::string usage() const {
813 const std::string longest_key_with_value_type_{longest_key_with_value_type()};
815 return "[" + longest_key_with_value_type_ + "] ...";
816 }
817 if (longest_key_with_value_type_.empty()) {
818 return "...";
819 }
820 return longest_key_with_value_type_ + " ...";
821 }
822
823 /// @brief Prints the options information of this command line argument as a string of text. The
824 /// options information consists of this command line argument's keys, value type, and
825 /// description.
826 /// @return The string of text that contains the options information of this command line
827 /// argument.
828 [[nodiscard]] std::string options() const {
829 std::string keys_with_value_type_{keys_with_value_type()};
830 if (keys_with_value_type_.empty()) {
831 return description_;
832 }
833 if (description_.empty()) {
834 return keys_with_value_type_;
835 }
836 return keys_with_value_type_ + " " + description_;
837 }
838
839 /// @brief Prints the execution of this command line argument as a string of text. The execution
840 /// consists of this command line argument's longest key and parsed values, if any.
841 /// @return The string of text that contains the execution of this command line argument.
842 [[nodiscard]] std::string execution() const {
843 if constexpr (std::is_same_v<Type, bool>) {
844 return execution_boolean();
845 } else {
846 return execution_non_boolean();
847 }
848 }
849
850private:
851 /// @brief Validates that this positional command line argument is not boolean. Boolean command
852 /// line arguments must always specify one or more keys and therefore cannot be positional.
853 void constexpr validate_non_boolean_positional() const {
854 if constexpr (std::is_same_v<Type, bool>) {
855 throw std::invalid_argument("Boolean arguments must specify one or more keys.");
856 }
857 }
858
859 /// @brief Validates that this command line argument that specifies one or more default values is
860 /// not boolean. Boolean command line arguments are always optional and always default to false,
861 /// so they cannot specify default values.
862 void constexpr validate_non_boolean_default_values() const {
863 if constexpr (std::is_same_v<Type, bool>) {
864 throw std::invalid_argument(
865 "Boolean arguments cannot specify default values; they are always false by default.");
866 }
867 }
868
869 /// @brief Validates the keys of this command line argument.
870 /// @throws std::logic_error if the keys are missing or invalid.
871 void validate_keys() const {
872 if (keys_.empty()) {
873 throw std::logic_error("All named arguments must each have at least one key.");
874 }
875 for (const std::string& key : keys_) {
876 if (key.empty()) {
877 throw std::logic_error("Empty key in named argument '" + options()
878 + "'. Named arguments cannot have empty keys.");
879 }
880 }
881 const std::size_t keys_size{keys_.size()};
882 for (std::size_t first{0UL}; first < keys_size; ++first) {
883 for (std::size_t second{first + static_cast<std::size_t>(1UL)}; second < keys_size;
884 ++second) {
885 if (keys_.at(first) == keys_.at(second)) {
886 throw std::logic_error("Duplicate key '" + keys_.at(first) + "' in named argument '"
887 + options() + "'. Named arguments cannot have duplicate keys.");
888 }
889 }
890 }
891 }
892
893 /// @brief Validates the description of this command line argument.
894 /// @throws std::logic_error if the description of this command line argument is empty.
895 void validate_description() const {
896 if (description_.empty()) {
897 throw std::logic_error("Empty description in argument '" + options()
898 + "'. All arguments must have descriptions.");
899 }
900 }
901
902 /// @brief Prints the value type of this command line argument as a string of text.
903 /// @return The string of text that contains the value type of this command line argument.
904 [[nodiscard]] constexpr std::string_view value_type() const {
905 if constexpr (std::is_same_v<Type, bool>) {
906 return "";
907 } else if constexpr (std::numeric_limits<Type>::is_integer) {
908 return "<number>";
909 } else if constexpr (std::is_floating_point_v<Type>) {
910 return "<value>";
911 } else if constexpr (
912 std::is_same_v<Type, std::string> || std::is_same_v<Type, std::string_view>) {
913 return "<text>";
914 } else if constexpr (std::is_same_v<Type, std::filesystem::path>) {
915 return "<path>";
916 } else {
917 return "<value>";
918 }
919 }
920
921 /// @brief Returns the longest key of this command line argument.
922 /// @return The longest key of this command line argument.
923 /// @throws std::out_of_range if this command line argument has no keys.
924 [[nodiscard]] const std::string& longest_key() const {
925 std::size_t longest_key_index{0UL};
926 std::size_t longest_key_length{0UL};
927 for (std::size_t index{0UL}; index < keys_.size(); ++index) {
928 const std::size_t length{lector::count_code_points(keys_.at(index))};
929 if (length > longest_key_length) {
930 longest_key_index = index;
931 longest_key_length = length;
932 }
933 }
934 return keys_.at(longest_key_index);
935 }
936
937 /// @brief Prints the execution of this boolean command line argument as a string of text. The
938 /// execution consists of this boolean command line argument's longest key for each parsed value.
939 /// @return The string of text that contains the execution of this boolean command line argument.
940 [[nodiscard]] std::string execution_boolean() const {
941 std::string result;
942 for (const Type& parsed_value : parsed_values_) {
943 if (parsed_value) {
944 if (!result.empty()) {
945 result.push_back(' ');
946 }
947 result.append(longest_key());
948 }
949 }
950 return result;
951 }
952
953 /// @brief Prints the execution of this non-boolean command line argument as a string of text. The
954 /// execution consists of this non-boolean command line argument's longest key and parsed values,
955 /// if any.
956 /// @return The string of text that contains the execution of this non-boolean command line
957 /// argument.
958 [[nodiscard]] std::string execution_non_boolean() const {
959 if (keys_.empty()) {
960 std::string result;
961 for (const Type& parsed_value : parsed_values_) {
962 if (!result.empty()) {
963 result.push_back(' ');
964 }
965 result.append(lector::quote_if_contains_whitespace(lector::print<Type>(parsed_value)));
966 }
967 return result;
968 }
969 std::string result;
970 for (const Type& parsed_value : parsed_values_) {
971 if (!result.empty()) {
972 result.push_back(' ');
973 }
974 result.append(longest_key());
975 result.push_back(' ');
976 result.append(lector::quote_if_contains_whitespace(lector::print<Type>(parsed_value)));
977 }
978 return result;
979 }
980
981 /// @brief Keys that can be used to specify this argument on the command line if it is a named
982 /// argument, or an empty collection if this argument is a positional argument. Set at
983 /// construction.
984 std::vector<std::string> keys_;
985
986 /// @brief Description of this command line argument. Set at construction.
987 std::string description_;
988
989 /// @brief Default values of this repeatable command line argument. Set at construction.
990 std::vector<Type> default_values_;
991
992 /// @brief Parsed values of this repeatable command line argument. Set when this argument is
993 /// parsed from the command line.
994 std::vector<Type> parsed_values_;
995
996 /// @brief Importance of this command line argument. A required argument must be provided by the
997 /// user on the command line, whereas an optional argument may or may not be provided by the user
998 /// on the command line. Set at construction.
1000};
1001
1002/// @brief Configuration of the help information of a collection of command line arguments.
1003struct Configuration final {
1004public:
1005 /// @brief Title of the application whose command line arguments are to be parsed. When the
1006 /// collection of command line arguments' help information is printed, this title appears first,
1007 /// before its usage information. Optional and empty by default, in which case no title is
1008 /// printed.
1009 std::optional<std::string> title{std::nullopt};
1010
1011 /// @brief Description of the application whose command line arguments are to be parsed. When the
1012 /// collection of command line arguments' help information is printed, this description appears
1013 /// between its usage information and its options information. Optional and empty by default, in
1014 /// which case no description is printed.
1015 std::optional<std::string> description{std::nullopt};
1016
1017 /// @brief Additional notes pertaining to the application whose command line arguments are to be
1018 /// parsed. When the collection of command line arguments' help information is printed, these
1019 /// notes appear last, after its options information. Optional and empty by default, in which case
1020 /// no notes are printed.
1021 std::optional<std::string> notes{std::nullopt};
1022};
1023
1024/// @brief Type trait used to extract a command line argument from a collection of command line
1025/// arguments, using only its Label.
1026/// @tparam Label The label of the command line argument to extract.
1027/// @tparam ...ArgumentTypes The variadic list of argument types in the collection of command line
1028/// arguments.
1029template <auto Label, typename... ArgumentTypes>
1030struct FindArgumentByLabel;
1031
1032/// @brief Type trait to provide short-circuit evaluation for lector::FindArgumentByLabel.
1033/// @tparam Label The label of the command line argument to extract.
1034/// @tparam Match Whether the command line argument was found or not.
1035/// @tparam FirstArgument The type of the command line argument to extract.
1036/// @tparam ...RemainingArgumentTypes The variadic list of argument types in the collection of
1037/// command line arguments, excluding the command line argument to extract.
1038template <auto Label, bool Match, typename FirstArgument, typename... RemainingArgumentTypes>
1040
1041/// @brief True branch of the short-circuit evaluation type trait. The command line argument has
1042/// been found and will now be returned; the remaining command line arguments do not need to be
1043/// searched.
1044/// @tparam Label The label of the command line argument to extract.
1045/// @tparam FirstArgument The type of the command line argument to extract.
1046/// @tparam ...RemainingArgumentTypes The variadic list of argument types in the collection of
1047/// command line arguments, excluding the command line argument to extract.
1048template <auto Label, typename FirstArgument, typename... RemainingArgumentTypes>
1049struct FindArgument<Label, true, FirstArgument, RemainingArgumentTypes...> {
1050 using type = FirstArgument;
1051};
1052
1053/// @brief False branch of the short-circuit evaluation type trait. The command line argument has
1054/// not yet been found and the remaining command line arguments should be searched.
1055/// @tparam Label The label of the command line argument to extract.
1056/// @tparam FirstArgument The type of the command line argument to extract.
1057/// @tparam ...RemainingArgumentTypes The variadic list of argument types in the collection of
1058/// command line arguments, excluding the command line argument to extract.
1059template <auto Label, typename FirstArgument, typename... RemainingArgumentTypes>
1060struct FindArgument<Label, false, FirstArgument, RemainingArgumentTypes...> {
1061 using type = typename FindArgumentByLabel<Label, RemainingArgumentTypes...>::type;
1062};
1063
1064/// @brief Type trait specialization used to extract a command line argument from a collection of
1065/// command line arguments, using its Label and the types of the remaining command line arguments in
1066/// the collection.
1067/// @tparam Label The label of the command line argument to extract.
1068/// @tparam FirstArgument The type of the command line argument to extract.
1069/// @tparam ...RemainingArgumentTypes The variadic list of argument types in the collection of
1070/// command line arguments, excluding the command line argument to extract.
1071template <auto Label, typename FirstArgument, typename... RemainingArgumentTypes>
1072struct FindArgumentByLabel<Label, FirstArgument, RemainingArgumentTypes...> {
1073 using type = typename FindArgument<Label, (FirstArgument::label() == Label), FirstArgument,
1074 RemainingArgumentTypes...>::type;
1075};
1076
1077/// @brief Type trait that validates at compilation time that a specified variadic list of types are
1078/// unique. Base type trait that contains an empty list of types and returns true.
1079/// @tparam ...Types Variadic list of types to check for uniqueness.
1080template <auto... Types>
1081struct AreUnique : std::true_type {};
1082
1083/// @brief Type trait that validates at compilation time that a specified variadic list of types are
1084/// unique. Recursively compares a first type against the remaining variadic list of types.
1085/// @tparam FirstType The first type to compare.
1086/// @tparam ...RemainingTypes The remaining types in the variadic list of types to compare.
1087template <auto FirstType, auto... RemainingTypes>
1088struct AreUnique<FirstType, RemainingTypes...>
1089 : std::bool_constant<((FirstType != RemainingTypes) && ...)
1090 && AreUnique<RemainingTypes...>::value> {};
1091
1092/// @brief A collection of command line arguments that can be parsed from argc and argv.
1093/// @tparam ...ArgumentTypes Variadic list of the types of the command line arguments in this
1094/// collection.
1095template <typename... ArgumentTypes>
1096class Arguments final {
1097public:
1098 /// @brief Compile-time check that all arguments have unique labels.
1099 static_assert(AreUnique<ArgumentTypes::label()...>::value,
1100 "Duplicate argument labels detected. Each argument must have a unique label.");
1101
1102 /// @brief Constructor. Constructs a collection of command line arguments from a configuration
1103 /// data structure and a variadic list of command line arguments.
1104 /// @param[in] configuration The configuration data structure.
1105 /// @param[in] arguments... The variadic list of command line arguments.
1106 /// @throws std::logic_error if the command line arguments are invalid.
1107 explicit Arguments(const lector::Configuration& configuration, const ArgumentTypes&... arguments)
1108 : configuration_{configuration}, arguments_{arguments...} {
1109 validate_arguments();
1110 }
1111
1112 /// @brief Constructor. Constructs a collection of command line arguments from a variadic list of
1113 /// command line arguments.
1114 /// @param[in] arguments... The variadic list of command line arguments.
1115 /// @throws std::logic_error if the command line arguments are invalid.
1116 explicit Arguments(const ArgumentTypes&... arguments) : arguments_{arguments...} {
1117 validate_arguments();
1118 }
1119
1120 /// @brief Destructor. Destroys this collection of command line arguments.
1121 ~Arguments() noexcept = default;
1122
1123 /// @brief Copy constructor. Constructs a collection of command line argument by copying another
1124 /// one.
1125 Arguments(const lector::Arguments<ArgumentTypes...>&) = default;
1126
1127 /// @brief Copy assignment operator. Assigns this collection of command line argument by copying
1128 /// another one.
1129 /// @return This collection of command line argument after the assignment.
1130 lector::Arguments<ArgumentTypes...>& operator=(
1131 const lector::Arguments<ArgumentTypes...>&) = default;
1132
1133 /// @brief Move constructor. Constructs a collection of command line argument by moving another
1134 /// one.
1135 Arguments(lector::Arguments<ArgumentTypes...>&&) noexcept = default;
1136
1137 /// @brief Move assignment operator. Assigns this collection of command line argument by moving
1138 /// another one.
1139 /// @return This collection of command line argument after the assignment.
1140 lector::Arguments<ArgumentTypes...>& operator=(
1141 lector::Arguments<ArgumentTypes...>&&) noexcept = default;
1142
1143 /// @brief Parses argc and argv to populate the parsed values of the command line arguments in
1144 /// this collection. This method should be called before calling the validate() method.
1145 /// @param[in] argc The number of command line arguments, including the executable path.
1146 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
1147 /// with the executable path.
1148 /// @throws std::invalid_argument if an invalid, unknown, duplicate, or missing argument is
1149 /// encountered.
1150 void parse(const int argc, char* argv[]) {
1151 parse_executable_path(argc, argv);
1152 const std::vector<std::string_view> positional_tokens{parse_named_arguments(argc, argv)};
1153 parse_positional_arguments(positional_tokens);
1154 }
1155
1156 /// @brief Validates that all required arguments have each successfully parsed a value from the
1157 /// command line. Should only be called after the lector::Arguments::parse() method has been
1158 /// called. If any command line arguments require special consideration, such as --version or
1159 /// --help flags, they should be handled before calling this method.
1160 /// @throws std::invalid_argument if any required arguments are lacking parsed values.
1161 void validate() const {
1162 std::apply(
1163 [&](const auto&... argument) {
1164 (..., [&] {
1165 if (argument.importance() == lector::Importance::Required && !argument.has_parsed()) {
1166 throw std::invalid_argument(
1167 "Missing required argument '" + argument.options() + "'.");
1168 }
1169 }());
1170 },
1171 arguments_);
1172 }
1173
1174 /// @brief Returns the configuration of the help information of this collection of command line
1175 /// arguments.
1176 /// @return The configuration of the help information of this collection of command line
1177 /// arguments.
1178 [[nodiscard]] const lector::Configuration& configuration() const {
1179 return configuration_;
1180 }
1181
1182 /// @brief Returns the executable path of this collection of command line arguments. If the
1183 /// command line arguments have not yet been parsed from argc and argv, this path is empty.
1184 /// @return The executable path of this collection of command line arguments.
1185 [[nodiscard]] const std::filesystem::path& executable_path() const {
1186 return executable_path_;
1187 }
1188
1189 /// @brief Returns a specified command line argument from this collection.
1190 /// @tparam Label The label of the command line argument to return.
1191 /// @return The specified command line argument.
1192 template <auto Label>
1193 [[nodiscard]] const auto& get() const {
1194 using Type = typename lector::FindArgumentByLabel<Label, ArgumentTypes...>::type;
1195 return std::get<Type>(arguments_);
1196 }
1197
1198 /// @brief Prints the usage information of this collection of command line arguments as a string
1199 /// of text. The usage information consists of each argument's longest key and value type,
1200 /// enclosed in square braces for optional command line arguments.
1201 /// @return The string of text that contains the usage information of this collection of command
1202 /// line arguments.
1203 [[nodiscard]] std::string usage() const {
1204 return usage(std::numeric_limits<std::size_t>::max());
1205 }
1206
1207 /// @brief Prints the usage information of this collection of command line arguments as a string
1208 /// of text. The usage information consists of each argument's longest key and value type,
1209 /// enclosed in square braces for optional command line arguments. Lines are wrapped.
1210 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
1211 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
1212 /// text contains very long words whose lengths exceed the desired line length.
1213 /// @return The string of text that contains the usage information of this collection of command
1214 /// line arguments.
1215 /// @throws std::invalid_argument if the desired line length is zero.
1216 [[nodiscard]] std::string usage(const std::size_t line_length) const {
1217 validate_line_length(line_length);
1218 std::string result;
1219 result.append(executable_path_.filename().string());
1220 std::apply(
1221 [&](const auto&... argument) {
1222 (..., [&] {
1223 result.push_back(' ');
1224 result.append(argument.usage());
1225 }());
1226 },
1227 arguments_);
1228 return result;
1229 }
1230
1231 /// @brief Prints the options information of this collection of command line argument as a string
1232 /// of text. The options information consists of the list of each command line argument's keys,
1233 /// value type, and description.
1234 /// @return The string of text that contains the options information of this collection of command
1235 /// line arguments.
1236 [[nodiscard]] std::string options() const {
1237 return options(std::numeric_limits<std::size_t>::max());
1238 }
1239
1240 /// @brief Prints the options information of this collection of command line argument as a string
1241 /// of text. The options information consists of the list of each command line argument's keys,
1242 /// value type, and description. Lines are wrapped.
1243 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
1244 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
1245 /// text contains very long words whose lengths exceed the desired line length.
1246 /// @return The string of text that contains the options information of this collection of command
1247 /// line arguments.
1248 /// @throws std::invalid_argument if the desired line length is zero.
1249 [[nodiscard]] std::string options(const std::size_t line_length) const {
1250 validate_line_length(line_length);
1251 // Compute the text formatting dimensions.
1252 constexpr std::size_t gutter_width{2UL};
1253 const std::size_t maximum_first_column_width{
1254 (line_length - gutter_width) / static_cast<std::size_t>(2UL)};
1255 const std::size_t maximum_length_of_keys_with_value_types_{
1256 maximum_length_of_keys_with_value_type()};
1257 const std::size_t first_column_width{
1258 std::min(maximum_length_of_keys_with_value_types_, maximum_first_column_width)};
1259 const std::size_t second_column_width{line_length - gutter_width - first_column_width};
1260 // Obtain the number of command line arguments.
1261 constexpr std::size_t argument_count{std::tuple_size_v<decltype(arguments_)>};
1262 // Iterate through the command line arguments and update the resulting string of text.
1263 std::string result;
1264 std::size_t argument_index{0UL};
1265 std::apply(
1266 [&](const auto&... argument) {
1267 (..., [&] {
1268 result.append(lector::collate_and_align_left(
1269 argument.keys_with_value_type(), first_column_width, argument.description(),
1270 second_column_width));
1271 ++argument_index;
1272 if (argument_index < argument_count) {
1273 result.push_back('\n');
1274 }
1275 }());
1276 },
1277 arguments_);
1278 return result;
1279 }
1280
1281 /// @brief Prints the help information of this collection of command line arguments as a string of
1282 /// text. The help information consists of this collection of command line arguments' title, usage
1283 /// information, description, options information, and notes.
1284 /// @return The string of text that contains the help information of this collection of command
1285 /// line arguments.
1286 [[nodiscard]] std::string help() const {
1287 return help(std::numeric_limits<std::size_t>::max());
1288 }
1289
1290 /// @brief Prints the help information of this collection of command line arguments as a string of
1291 /// text. The help information consists of this collection of command line arguments' title, usage
1292 /// information, description, options information, and notes. Lines are wrapped.
1293 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
1294 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
1295 /// text contains very long words whose lengths exceed the desired line length.
1296 /// @return The string of text that contains the help information of this collection of command
1297 /// line arguments.
1298 /// @throws std::invalid_argument if the desired line length is zero.
1299 [[nodiscard]] std::string help(const std::size_t line_length) const {
1300 validate_line_length(line_length);
1301 std::string result;
1302 if (configuration_.title.has_value() && !configuration_.title.value().empty()) {
1303 result.append(lector::wrap_and_align_left(configuration_.title.value(), line_length));
1304 }
1305 const std::string usage_{lector::wrap_and_align_left(usage(), line_length)};
1306 if (!usage_.empty()) {
1307 if (!result.empty()) {
1308 result.push_back('\n');
1309 result.push_back('\n');
1310 }
1311 result.append("Usage:\n");
1312 result.append(usage_);
1313 }
1314 if (configuration_.description.has_value() && !configuration_.description.value().empty()) {
1315 if (!result.empty()) {
1316 result.push_back('\n');
1317 result.push_back('\n');
1318 }
1319 result.append(lector::wrap_and_align_left(configuration_.description.value(), line_length));
1320 }
1321 const std::string options_{options(line_length)};
1322 if (!options_.empty()) {
1323 if (!result.empty()) {
1324 result.push_back('\n');
1325 result.push_back('\n');
1326 }
1327 result.append("Options:\n");
1328 result.append(options_);
1329 }
1330 if (configuration_.notes.has_value() && !configuration_.notes.value().empty()) {
1331 if (!result.empty()) {
1332 result.push_back('\n');
1333 result.push_back('\n');
1334 }
1335 result.append(lector::wrap_and_align_left(configuration_.notes.value(), line_length));
1336 }
1337 return result;
1338 }
1339
1340 /// @brief Prints the execution of this collection of command line argument as a string of text.
1341 /// The execution consists of the executable path followed by each argument's longest key and
1342 /// corresponding parsed value, if any.
1343 /// @return The string of text that contains the execution of this collection of command line
1344 /// argument.
1345 [[nodiscard]] std::string execution() const {
1346 return execution(std::numeric_limits<std::size_t>::max());
1347 }
1348
1349 /// @brief Prints the execution of this collection of command line argument as a string of text.
1350 /// The execution consists of the executable path followed by each argument's longest key and
1351 /// corresponding parsed value, if any. Lines are wrapped.
1352 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
1353 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
1354 /// text contains very long words whose lengths exceed the desired line length.
1355 /// @return The string of text that contains the execution of this collection of command line
1356 /// argument.
1357 /// @throws std::invalid_argument if the desired line length is zero.
1358 [[nodiscard]] std::string execution(const std::size_t line_length) const {
1359 validate_line_length(line_length);
1360 std::string printed_execution_arguments;
1361 std::apply(
1362 [&](const auto&... argument) {
1363 (..., [&] {
1364 const std::string argument_execution{argument.execution()};
1365 if (!printed_execution_arguments.empty() && !argument_execution.empty()) {
1366 printed_execution_arguments.push_back(' ');
1367 }
1368 printed_execution_arguments.append(argument_execution);
1369 }());
1370 },
1371 arguments_);
1372 if (printed_execution_arguments.empty()) {
1373 return executable_path_.string();
1374 }
1376 executable_path_.string() + " " + printed_execution_arguments, line_length);
1377 }
1378
1379private:
1380 /// @brief Default length to use when wrapping lines when printing usage information, options
1381 /// information, or help information as a string of text. The actual line length may be longer if
1382 /// the string of text contains very long words whose lengths exceed the desired line length.
1383 static constexpr std::size_t default_line_length_{80UL};
1384
1385 /// @brief Data structure that contains the best matching argument for a command line argument
1386 /// token during parsing. The best matching argument is the argument with the longest matching
1387 /// key, and if there are multiple arguments with keys of the same length that match, then the
1388 /// best matching argument is the one that is matched by a non-inline key rather than an inline
1389 /// key. Used to avoid shadowing when multiple arguments have keys that are prefixes of each
1390 /// other, and to prefer non-inline matches over inline matches when the key lengths are equal.
1391 struct BestArgument final {
1392 /// @brief Index of this argument in the tuple of arguments. Used to identify this argument
1393 /// during parsing.
1394 std::size_t index{0UL};
1395
1396 /// @brief Length of the longest matching key for this argument. Used to avoid shadowing when
1397 /// multiple arguments have keys that are prefixes of each other. For example, if one argument
1398 /// has the key "key" and another argument has the key "key_long", then the argument with the
1399 /// key "key_long" should be matched for the command line argument "key_long=value", not the
1400 /// argument with the key "key".
1401 std::size_t key_length{0UL};
1402
1403 /// @brief Whether this argument was matched by an inline key of the form "key=value" rather
1404 /// than a whitespace-separated key-value pair of the form "key value". Used to prefer
1405 /// non-inline matches over inline matches when the key lengths are equal. For example, if one
1406 /// argument has the key "key" and another argument has the key "key_long", then the argument
1407 /// with the key "key" should be matched for the command line argument "key=value", not the
1408 /// argument with the key "key_long".
1409 bool is_inline{false};
1410 };
1411
1412 /// @brief Validates that the arguments in this collection of command line arguments are
1413 /// consistent.
1414 /// @throws std::logic_error if a repeated positional argument is mixed with other positional
1415 /// arguments, or if the same key is duplicated across two or more arguments.
1416 void validate_arguments() const {
1417 validate_positional_arguments();
1418 validate_keys();
1419 }
1420
1421 /// @brief Validates that this collection of command line arguments does not mix a repeated
1422 /// positional command line argument with other positional arguments.
1423 /// @throws std::logic_error if a repeated positional argument is mixed with other positional
1424 /// arguments.
1425 void validate_positional_arguments() const {
1426 bool has_repeated_positional_argument{false};
1427 std::size_t positional_argument_count{0UL};
1428 std::apply(
1429 [&](const auto&... argument) {
1430 (..., [&] {
1431 if (argument.form() == lector::Form::Positional) {
1432 ++positional_argument_count;
1433 if (argument.arity() == lector::Arity::Repeatable) {
1434 has_repeated_positional_argument = true;
1435 }
1436 }
1437 }());
1438 },
1439 arguments_);
1440 if (has_repeated_positional_argument
1441 && positional_argument_count >= static_cast<std::size_t>(2UL)) {
1442 throw std::logic_error(
1443 "A repeated positional argument cannot be mixed with any other positional arguments.");
1444 }
1445 }
1446
1447 /// @brief Validates that the same key is never duplicated across two or more arguments.
1448 /// @throws std::logic_error if the same key is duplicated across two or more arguments.
1449 void validate_keys() const {
1450 std::unordered_set<std::string> unique_keys;
1451 std::apply(
1452 [&](const auto&... argument) {
1453 (..., [&] {
1454 for (const std::string& key : argument.keys()) {
1455 const std::pair<std::unordered_set<std::string>::const_iterator, bool> result{
1456 unique_keys.insert(key)};
1457 if (!result.second) {
1458 throw std::logic_error("Duplicate key '" + key + "' across two arguments.");
1459 }
1460 }
1461 }());
1462 },
1463 arguments_);
1464 }
1465
1466 /// @brief Validates that a specified line length is strictly greater than zero.
1467 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
1468 /// than zero.
1469 /// @throws std::invalid_argument if the desired line length is zero.
1470 static void validate_line_length(const std::size_t line_length) {
1471 if (line_length <= static_cast<std::size_t>(0UL)) {
1472 throw std::invalid_argument("Invalid line length. Must be strictly greater than zero.");
1473 }
1474 }
1475
1476 /// @brief Parses the executable path from argc and argv.
1477 /// @param[in] argc The number of command line arguments, including the executable path.
1478 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
1479 /// with the executable path.
1480 void parse_executable_path(const int argc, char* argv[]) {
1481 if (argc > 0) {
1482 executable_path_ = argv[0];
1483 }
1484 }
1485
1486 /// @brief Parses argc and argv, except for the executable path, and attempts to match them to the
1487 /// named arguments. Starts at the second argument in argv. Remaining arguments that could not be
1488 /// matched to named arguments are treated as positional arguments and returned.
1489 /// @param[in] argc The number of command line arguments, including the executable path.
1490 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
1491 /// with the executable path.
1492 /// @return Collection of the remaining command line arguments that could not be matched to named
1493 /// arguments, which are treated as positional arguments.
1494 /// @throws std::invalid_argument if an invalid, unknown, duplicate, or missing argument is
1495 /// encountered.
1496 [[nodiscard]] std::vector<std::string_view> parse_named_arguments(const int argc, char* argv[]) {
1497 std::vector<std::string_view> positional_tokens;
1498 const std::size_t count{static_cast<std::size_t>(argc)};
1499 for (std::size_t argv_index{1UL}; argv_index < count; ++argv_index) {
1500 const std::string_view token{argv[argv_index]};
1501 const std::optional<BestArgument> best_argument{find_best_argument(token)};
1502 if (best_argument.has_value()) {
1503 std::size_t current_index{0UL};
1504 std::apply(
1505 [&](auto&... argument) {
1506 (..., [&] {
1507 if (current_index == best_argument->index) {
1508 populate_argument(argument, best_argument.value(), argc, argv, argv_index);
1509 }
1510 ++current_index;
1511 }());
1512 },
1513 arguments_);
1514 } else {
1515 // If no named argument key matches the current argv token, treat it as the value of a
1516 // positional argument.
1517 positional_tokens.push_back(token);
1518 }
1519 }
1520 return positional_tokens;
1521 }
1522
1523 /// @brief Parses the remaining command line arguments that could not be matched to named
1524 /// arguments as positional arguments.
1525 /// @param[in] positional_tokens Collection of the remaining command line arguments that could not
1526 /// be matched to named arguments, which are treated as positional arguments.
1527 /// @throws std::invalid_argument if too many positional arguments are provided or if a positional
1528 /// argument cannot be parsed.
1529 void parse_positional_arguments(const std::vector<std::string_view>& positional_tokens) {
1530 std::size_t positional_token_index{0UL};
1531 std::apply(
1532 [&](auto&... argument) {
1533 (..., [&] {
1534 if (argument.form() == lector::Form::Positional) {
1535 using Type = typename std::decay_t<decltype(argument)>::ValueType;
1536 if (argument.arity() == lector::Arity::Singular) {
1537 if (positional_token_index < positional_tokens.size()) {
1538 const std::string raw_value{positional_tokens.at(positional_token_index)};
1539 const std::optional<Type> parsed_value{lector::parse<Type>(raw_value)};
1540 if (parsed_value.has_value()) {
1541 argument.set_parsed_value(parsed_value.value());
1542 } else {
1543 throw std::invalid_argument("Invalid value '" + raw_value + "' for argument '"
1544 + argument.options() + "'.");
1545 }
1546 ++positional_token_index;
1547 }
1548 } else {
1549 // Repeatable arity: consume all remaining tokens.
1550 while (positional_token_index < positional_tokens.size()) {
1551 const std::string raw_value{positional_tokens.at(positional_token_index)};
1552 const std::optional<Type> parsed_value{lector::parse<Type>(raw_value)};
1553 if (parsed_value.has_value()) {
1554 argument.set_parsed_value(parsed_value.value());
1555 } else {
1556 throw std::invalid_argument("Invalid value '" + raw_value + "' for argument '"
1557 + argument.options() + "'.");
1558 }
1559 ++positional_token_index;
1560 }
1561 }
1562 }
1563 }());
1564 },
1565 arguments_);
1566 validate_all_positional_tokens_matched(positional_tokens, positional_token_index);
1567 }
1568
1569 /// @brief Finds the best matching argument for a command line argument token.
1570 /// @param[in] token The command line argument token to match.
1571 /// @return The best matching argument.
1572 /// @throws std::invalid_argument if an unknown argument is encountered.
1573 [[nodiscard]] std::optional<BestArgument> find_best_argument(const std::string_view token) const {
1574 std::optional<BestArgument> best;
1575 std::size_t argument_index{0UL};
1576 std::apply(
1577 [&](const auto&... argument) {
1578 (..., [&] {
1579 for (const std::string& argument_key : argument.keys()) {
1580 const std::optional<BestArgument> exact_match{
1581 try_exact_match(token, argument_index, argument_key)};
1582 if (exact_match.has_value()) {
1583 if (!best.has_value() || exact_match->key_length > best->key_length
1584 || (exact_match->key_length == best->key_length && best->is_inline)) {
1585 best = exact_match;
1586 }
1587 continue;
1588 }
1589 const std::optional<BestArgument> inline_match{
1590 try_inline_match<decltype(argument)>(token, argument_index, argument_key)};
1591 if (inline_match.has_value()
1592 && (!best.has_value() || inline_match->key_length > best->key_length)) {
1593 best = inline_match;
1594 }
1595 }
1596 ++argument_index;
1597 }());
1598 },
1599 arguments_);
1600 return best;
1601 }
1602
1603 /// @brief Checks whether a token is the exact match of an argument's key.
1604 /// @param[in] token The token to check.
1605 /// @param[in] argument_index The index of the argument that has the key.
1606 /// @param[in] argument_key The argument key against which to compare.
1607 /// @return A populated lector::Arguments::BestArgument data structure if the specified token is
1608 /// an exact match for the key, or std::nullopt otherwise.
1609 [[nodiscard]] static std::optional<BestArgument> try_exact_match(
1610 const std::string_view token, const std::size_t argument_index,
1611 const std::string_view argument_key) noexcept {
1612 if (token == argument_key) {
1613 return BestArgument{argument_index, argument_key.size(), false};
1614 }
1615 return std::nullopt;
1616 }
1617
1618 /// @brief Checks whether a token contains an inline match of an argument's key of the form
1619 /// "key=value".
1620 /// @tparam SingularArgument The type of the argument that has the key to be used in the
1621 /// comparison.
1622 /// @param[in] token The token to check.
1623 /// @param[in] argument_index The index of the argument that has the key to be used in the
1624 /// comparison.
1625 /// @param[in] argument_key The argument key against which to compare.
1626 /// @return A populated lector::Arguments::BestArgument data structure if the specified token
1627 /// contains an inline match for the key, or std::nullopt otherwise.
1628 template <typename SingularArgument>
1629 [[nodiscard]] static std::optional<BestArgument> try_inline_match(
1630 const std::string_view token, const std::size_t argument_index,
1631 const std::string_view argument_key) noexcept {
1632 using ArgumentType = typename std::decay_t<SingularArgument>::ValueType;
1633 // Inline matching is strictly disabled for boolean arguments because they are key-only flags
1634 // that do not have values.
1635 if constexpr (!std::is_same_v<ArgumentType, bool>) {
1636 if (token.size() > argument_key.size()
1637 && token.compare(0, argument_key.size(), argument_key) == 0
1638 && token[argument_key.size()] == '=') {
1639 return BestArgument{argument_index, argument_key.size(), true};
1640 }
1641 }
1642 return std::nullopt;
1643 }
1644
1645 /// @brief Populates an argument with its parsed value.
1646 /// @tparam ArgumentType The type of the argument to be populated.
1647 /// @param[in,out] argument The argument to be populated.
1648 /// @param[in] best_argument The lector::Arguments::BestArgument data structure that corresponds
1649 /// to the argument to be populated.
1650 /// @param[in] argc The number of command line arguments, including the executable path.
1651 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
1652 /// with the executable path.
1653 /// @param[in,out] argv_index The index of the argument in the array of C-string command line
1654 /// arguments whose value is to be extracted and used to populate the argument.
1655 /// @throws std::invalid_argument if the parsed value is invalid for this argument type.
1656 template <typename ArgumentType>
1657 static void populate_argument(ArgumentType& argument, const BestArgument& best_argument,
1658 const int argc, char* argv[], std::size_t& argv_index) {
1659 using Type = typename std::decay_t<ArgumentType>::ValueType;
1660 if constexpr (std::is_same_v<Type, bool>) {
1661 // Boolean arguments are key-only flags; their presence implies true.
1662 argument.set_parsed_value(true);
1663 } else {
1664 // This is a non-boolean argument. Extract and parse its value.
1665 const std::string raw_value{
1666 extract_raw_value(best_argument, argument.options(), argc, argv, argv_index)};
1667 const std::optional<Type> parsed_value{lector::parse<Type>(raw_value)};
1668 if (parsed_value.has_value()) {
1669 argument.set_parsed_value(parsed_value.value());
1670 } else {
1671 throw std::invalid_argument(
1672 "Invalid value '" + raw_value + "' for argument '" + argument.options() + "'.");
1673 }
1674 }
1675 }
1676
1677 /// @brief Extracts the raw string value from an argv token for a non-boolean best argument.
1678 /// @param[in] best_argument The best argument.
1679 /// @param[in] best_argument_options The options string of the best argument, for printing to an
1680 /// error message if needed.
1681 /// @param[in] argc The number of command line arguments, including the executable path.
1682 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
1683 /// with the executable path.
1684 /// @param[in,out] argv_index The index of the argument in the array of C-string command line
1685 /// arguments whose raw string value is to be extracted.
1686 /// @return The extracted raw string value.
1687 /// @throws std::invalid_argument if the command line arguments are missing the value for this
1688 /// best argument.
1689 [[nodiscard]] static std::string extract_raw_value(
1690 const BestArgument& best_argument, const std::string& best_argument_options, const int argc,
1691 char* argv[], std::size_t& argv_index) {
1692 const std::string_view token{argv[argv_index]};
1693 if (best_argument.is_inline) {
1694 // The token contains an inline value of the form "key=value". Extract the last portion of the
1695 // token.
1696 return std::string{token.substr(best_argument.key_length + 1)};
1697 }
1698 if (argv_index + 1 < static_cast<std::size_t>(argc)) {
1699 // The token is a standalone value of the form "key", and the next token is of the form
1700 // "value". Advance the argv index to consume the next argv element that contains the value.
1701 ++argv_index;
1702 return std::string{argv[argv_index]};
1703 }
1704 throw std::invalid_argument("Missing value for argument '" + best_argument_options + "'.");
1705 }
1706
1707 /// @brief Validates that all raw positional tokens have been consumed by positional command line
1708 /// arguments.
1709 /// @param[in] positional_tokens The raw positional command line tokens.
1710 /// @param[in] positional_token_index The parsed index in the collection of raw positional command
1711 /// line tokens.
1712 /// @throws std::invalid_argument if any raw positional command line tokens were not consumed by
1713 /// positional command line arguments.
1714 void validate_all_positional_tokens_matched(
1715 const std::vector<std::string_view>& positional_tokens,
1716 const std::size_t positional_token_index) {
1717 if (positional_token_index < positional_tokens.size()) {
1718 std::string unexpected_tokens;
1719 for (std::size_t unexpected_token_index{positional_token_index};
1720 unexpected_token_index < positional_tokens.size(); ++unexpected_token_index) {
1721 if (!unexpected_tokens.empty()) {
1722 unexpected_tokens.append(", ");
1723 }
1724 unexpected_tokens.push_back('\'');
1725 unexpected_tokens.append(std::string{positional_tokens.at(unexpected_token_index)});
1726 unexpected_tokens.push_back('\'');
1727 }
1728 throw std::invalid_argument("Unexpected command line tokens: " + unexpected_tokens + ".");
1729 }
1730 }
1731
1732 /// @brief Computes and returns the maximum length of the printed keys and value type across all
1733 /// arguments in this collection of command line arguments.
1734 /// @return The maximum length of the printed keys and value type across all arguments in this
1735 /// collection.
1736 [[nodiscard]] std::size_t maximum_length_of_keys_with_value_type() const {
1737 std::size_t maximum_length{0UL};
1738 std::apply(
1739 [&](const auto&... argument) {
1740 (..., [&] {
1741 const std::size_t length{lector::count_code_points(argument.keys_with_value_type())};
1742 maximum_length = std::max(maximum_length, length);
1743 }());
1744 },
1745 arguments_);
1746 return maximum_length;
1747 }
1748
1749 /// @brief Configuration of the help information of this collection of command line arguments.
1750 lector::Configuration configuration_{};
1751
1752 /// @brief Variadic collection of command line arguments.
1753 std::tuple<ArgumentTypes...> arguments_;
1754
1755 /// @brief Executable path of this collection of command line arguments. If the command line
1756 /// arguments have not yet been parsed from argc and argv, this path is empty.
1757 std::filesystem::path executable_path_;
1758};
1759
1760} // namespace lector
1761
1762#endif // LECTOR_ARGUMENTS_HPP
A collection of command line arguments that can be parsed from argc and argv.
Definition arguments.hpp:1096
std::string usage(const std::size_t line_length) const
Prints the usage information of this collection of command line arguments as a string of text....
Definition arguments.hpp:1216
std::string options() const
Prints the options information of this collection of command line argument as a string of text....
Definition arguments.hpp:1236
const std::filesystem::path & executable_path() const
Returns the executable path of this collection of command line arguments. If the command line argumen...
Definition arguments.hpp:1185
std::string execution(const std::size_t line_length) const
Prints the execution of this collection of command line argument as a string of text....
Definition arguments.hpp:1358
const lector::Configuration & configuration() const
Returns the configuration of the help information of this collection of command line arguments.
Definition arguments.hpp:1178
Arguments(const ArgumentTypes &... arguments)
Constructor. Constructs a collection of command line arguments from a variadic list of command line a...
Definition arguments.hpp:1116
std::string help() const
Prints the help information of this collection of command line arguments as a string of text....
Definition arguments.hpp:1286
std::string execution() const
Prints the execution of this collection of command line argument as a string of text....
Definition arguments.hpp:1345
void validate() const
Validates that all required arguments have each successfully parsed a value from the command line....
Definition arguments.hpp:1161
~Arguments() noexcept=default
Destructor. Destroys this collection of command line arguments.
const auto & get() const
Returns a specified command line argument from this collection.
Definition arguments.hpp:1193
void parse(const int argc, char *argv[])
Parses argc and argv to populate the parsed values of the command line arguments in this collection....
Definition arguments.hpp:1150
std::string usage() const
Prints the usage information of this collection of command line arguments as a string of text....
Definition arguments.hpp:1203
std::string help(const std::size_t line_length) const
Prints the help information of this collection of command line arguments as a string of text....
Definition arguments.hpp:1299
Arguments(const lector::Configuration &configuration, const ArgumentTypes &... arguments)
Compile-time check that all arguments have unique labels.
Definition arguments.hpp:1107
std::string options(const std::size_t line_length) const
Prints the options information of this collection of command line argument as a string of text....
Definition arguments.hpp:1249
A repeatable command line argument.
Definition arguments.hpp:589
const std::vector< Type > & default_values() const noexcept
Default values of this repeatable command line argument. Set at construction.
Definition arguments.hpp:697
static lector::Arity arity() noexcept
Arity of this command line argument. A repeatable argument can only appear once on the command line,...
Definition arguments.hpp:738
~RepeatableArgument() noexcept=default
Destructor. Destroys this repeatable command line argument.
const std::vector< std::string > & keys() const noexcept
Keys that can be used to specify this argument on the command line if it is a named argument,...
Definition arguments.hpp:678
const std::vector< Type > & parsed_or_default_values() const
Values of this repeatable command line argument. Returns the parsed values if they exist; otherwise,...
Definition arguments.hpp:745
std::string keys_with_value_type() const
Prints the keys and value type of this command line argument as a string of text.
Definition arguments.hpp:789
const std::vector< Type > & parsed_values() const noexcept
Parsed values of this repeatable command line argument. Set when this argument is parsed from the com...
Definition arguments.hpp:711
std::string_view description() const noexcept
Description of this command line argument. Set at construction.
Definition arguments.hpp:684
RepeatableArgument() noexcept=default
Default constructor. Initializes the repeatable command line argument with no keys,...
bool has_default() const noexcept
Returns whether this repeatable command line argument has one or more default values.
Definition arguments.hpp:691
lector::Form form() const noexcept
Form of this command line argument. A positional argument does not define any keys and must be specif...
Definition arguments.hpp:728
std::string longest_key_with_value_type() const
Prints the longest key of this command line argument with its associated value type as a string of te...
Definition arguments.hpp:773
static constexpr auto label() noexcept
Label of this command line argument. Used to uniquely identify this command line argument in a collec...
Definition arguments.hpp:670
RepeatableArgument(const std::vector< std::string > &keys, const std::string_view description)
Constructor for a repeatable named required command line argument or a repeatable named optional bool...
Definition arguments.hpp:612
std::string execution() const
Prints the execution of this command line argument as a string of text. The execution consists of thi...
Definition arguments.hpp:842
lector::Importance importance() const noexcept
Importance of this command line argument. A required argument must be provided by the user on the com...
Definition arguments.hpp:719
std::string options() const
Prints the options information of this command line argument as a string of text. The options informa...
Definition arguments.hpp:828
void set_parsed_value(const Type &value)
Inserts an additional parsed value into this repeatable command line argument.
Definition arguments.hpp:757
bool has_parsed() const noexcept
Returns whether this repeatable command line argument has one or more parsed values.
Definition arguments.hpp:704
RepeatableArgument(const std::string_view description, const std::vector< Type > &default_values)
Constructor for a repeatable positional optional non-boolean command line argument.
Definition arguments.hpp:624
std::string usage() const
Prints the usage information of this command line argument as a string of text. The usage information...
Definition arguments.hpp:812
RepeatableArgument(const std::vector< std::string > &keys, const std::string_view description, const std::vector< Type > &default_values)
Constructor for a repeatable named optional non-boolean command line argument.
Definition arguments.hpp:637
A singular command line argument.
Definition arguments.hpp:171
std::string longest_key_with_value_type() const
Prints the longest key of this command line argument with its associated value type as a string of te...
Definition arguments.hpp:368
bool has_parsed() const noexcept
Returns whether this singular command line argument has a parsed value.
Definition arguments.hpp:290
SingularArgument() noexcept=default
Default constructor. Initializes the singular command line argument with no keys, an empty descriptio...
std::string keys_with_value_type() const
Prints the keys and value type of this command line argument as a string of text.
Definition arguments.hpp:384
bool has_default() const noexcept
Returns whether this singular command line argument has a default value.
Definition arguments.hpp:277
static lector::Arity arity() noexcept
Arity of this command line argument. A singular argument can only appear once on the command line,...
Definition arguments.hpp:324
const std::optional< Type > & parsed_value() const noexcept
Parsed value of this singular command line argument. Set when this argument is parsed from the comman...
Definition arguments.hpp:297
std::string options() const
Prints the options information of this command line argument as a string of text. The options informa...
Definition arguments.hpp:419
const std::vector< std::string > & keys() const noexcept
Keys that can be used to specify this argument on the command line if it is a named argument,...
Definition arguments.hpp:265
const Type & parsed_or_default_value() const
Value of this singular command line argument. Returns the parsed value if it exists; otherwise,...
Definition arguments.hpp:333
static constexpr auto label() noexcept
Label of this command line argument. Used to uniquely identify this command line argument in a collec...
Definition arguments.hpp:257
SingularArgument(const std::string_view description, const Type &default_value)
Constructor for a singular positional optional non-boolean command line argument. A default value mus...
Definition arguments.hpp:210
const std::optional< Type > & default_value() const noexcept
Default value of this singular command line argument if it is optional and non-boolean,...
Definition arguments.hpp:284
std::string usage() const
Prints the usage information of this command line argument as a string of text. The usage information...
Definition arguments.hpp:407
~SingularArgument() noexcept=default
Destructor. Destroys this singular command line argument.
lector::Importance importance() const noexcept
Importance of this command line argument. A required argument must be provided by the user on the com...
Definition arguments.hpp:305
std::string_view description() const noexcept
Description of this command line argument. Set at construction.
Definition arguments.hpp:271
SingularArgument(const std::vector< std::string > &keys, const std::string_view description, const Type &default_value)
Constructor for a singular named optional non-boolean command line argument. A default value must be ...
Definition arguments.hpp:224
SingularArgument(const std::vector< std::string > &keys, const std::string_view description)
Constructor for a singular named required command line argument or a singular named boolean command l...
Definition arguments.hpp:195
void set_parsed_value(const Type &value)
Sets the parsed value of this singular command line argument.
Definition arguments.hpp:349
std::string execution() const
Prints the execution of this command line argument as a string of text. The execution consists of thi...
Definition arguments.hpp:433
lector::Form form() const noexcept
Form of this command line argument. A positional argument does not define any keys and must be specif...
Definition arguments.hpp:314
The Lector library's namespace.
Definition arguments.hpp:43
std::string quote_if_contains_whitespace(const std::string_view text)
Encloses a string of text in quotes if it contains any whitespace. Either single or double quotes are...
Definition text.hpp:238
std::string wrap_and_align_left(const std::string_view text, const std::size_t line_length)
Wraps and left-aligns a string of text to a line length.
Definition text.hpp:714
Form
Form of a command line argument.
Definition arguments.hpp:88
@ Named
The command line argument is a named argument; it defines one or more keys and is specified on the co...
@ Unknown
Unknown, unspecified, or invalid command line argument form.
@ Positional
The command line argument is a positional argument; it does not define any keys and must be specified...
Importance
Importance of a command line argument.
Definition arguments.hpp:129
@ Unknown
Unknown, unspecified, or invalid command line argument importance.
@ Required
The command line argument is required; it must be provided by the user.
@ Optional
The command line argument is optional; it may or may not be provided by the user.
std::size_t count_code_points(const std::string_view text)
Counts and returns the number of UTF-8 code points in a string of text. The number of UTF-8 code poin...
Definition text.hpp:102
Arity
Arity of a command line argument.
Definition arguments.hpp:46
@ Repeatable
The command line argument has repeatable arity; it can appear multiple times on the command line....
@ Unknown
Unknown, unspecified, or invalid command line argument arity.
@ Singular
The command line argument has singular arity; it can only appear once on the command line.
std::string collate_and_align_left(const std::string_view first_column_text, const std::size_t first_column_width, const std::string_view second_column_text, const std::size_t second_column_width)
Collates two strings of text, each representing a column, into a single string that contains newline-...
Definition text.hpp:765
Configuration of the help information of a collection of command line arguments.
Definition arguments.hpp:1003
std::optional< std::string > title
Title of the application whose command line arguments are to be parsed. When the collection of comman...
Definition arguments.hpp:1009
std::optional< std::string > description
Description of the application whose command line arguments are to be parsed. When the collection of ...
Definition arguments.hpp:1015
std::optional< std::string > notes
Additional notes pertaining to the application whose command line arguments are to be parsed....
Definition arguments.hpp:1021
Type trait to provide short-circuit evaluation for lector::FindArgumentByLabel.
Definition arguments.hpp:1039