1//! @file argument.hpp
2//! @author ryftchen
3//! @brief The declarations (argument) in the utility module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#pragma once
8
9#include <algorithm>
10#include <any>
11#include <cstdint>
12#include <functional>
13#include <list>
14#include <map>
15#include <optional>
16#include <sstream>
17#include <variant>
18
19//! @brief The utility module.
20namespace utility // NOLINT(modernize-concat-nested-namespaces)
21{
22//! @brief Argument-parsing-related functions in the utility module.
23namespace argument
24{
25//! @brief Brief function description.
26//! @return function description (module_function)
27inline static const char* description() noexcept
28{
29 return "UTIL_ARGUMENT";
30}
31extern const char* version() noexcept;
32
33//! @brief Confirm container traits. Value is false.
34//! @tparam Var - type of variety to be confirmed
35template <typename Var, typename = void>
36struct HasContainerTraits : public std::false_type
37{
38};
39//! @brief Confirm container traits (std::string). Value is false.
40template <>
41struct HasContainerTraits<std::string> : public std::false_type
42{
43};
44//! @brief Confirm container traits (std::string_view). Value is false.
45template <>
46struct HasContainerTraits<std::string_view> : public std::false_type
47{
48};
49//! @brief Confirm container traits. Value is true.
50//! @tparam Var - type of variety to be confirmed
51template <typename Var>
52struct HasContainerTraits<
53 Var,
54 std::void_t<
55 typename Var::value_type,
56 decltype(std::declval<Var>().begin()),
57 decltype(std::declval<Var>().end()),
58 decltype(std::declval<Var>().size())>> : public std::true_type
59{
60};
61//! @brief Confirm whether it is a container.
62//! @tparam Var - type of variety to be confirmed
63template <typename Var>
64static constexpr bool isContainer = HasContainerTraits<Var>::value;
65
66//! @brief Confirm streamable traits. Value is false.
67//! @tparam Var - type of variety to be confirmed
68template <typename Var, typename = void>
69struct HasStreamableTraits : public std::false_type
70{
71};
72//! @brief Confirm streamable traits. Value is true.
73//! @tparam Var - type of variety to be confirmed
74template <typename Var>
75struct HasStreamableTraits<Var, std::void_t<decltype(std::declval<std::ostream&>() << std::declval<Var>())>>
76 : public std::true_type
77{
78};
79//! @brief Confirm whether it is streamable.
80//! @tparam Var - type of variety to be confirmed
81template <typename Var>
82static constexpr bool isStreamable = HasStreamableTraits<Var>::value;
83
84//! @brief Enumerate specific argument patterns.
85enum class ArgsNumPattern : std::uint8_t
86{
87 //! @brief Optional.
88 optional,
89 //! @brief Any.
90 any,
91 //! @brief At least one.
92 atLeastOne
93};
94//! @brief Indicate the range for the number of arguments.
95class ArgsNumRange
96{
97public:
98 //! @brief Construct a new ArgsNumRange object.
99 //! @param minimum - minimum of range
100 //! @param maximum - maximum of range
101 ArgsNumRange(const std::size_t minimum, const std::size_t maximum);
102
103 //! @brief The operator (==) overloading of ArgTrait class.
104 //! @param rhs - right-hand side
105 //! @return be equal or not
106 bool operator==(const ArgsNumRange& rhs) const;
107 //! @brief The operator (!=) overloading of ArgTrait class.
108 //! @param rhs - right-hand side
109 //! @return be unequal or not
110 bool operator!=(const ArgsNumRange& rhs) const;
111
112 friend class ArgTrait;
113 //! @brief Check whether the number of arguments is within the range.
114 //! @param value - number of arguments
115 //! @return within or not
116 [[nodiscard]] bool within(const std::size_t value) const;
117 //! @brief Check whether the number of arguments is exact.
118 //! @return be exact or not
119 [[nodiscard]] bool isExact() const;
120 //! @brief Check whether the maximum of the range is set.
121 //! @return exist or not
122 [[nodiscard]] bool existRightBound() const;
123
124private:
125 //! @brief Minimum of range.
126 std::size_t min{0};
127 //! @brief Maximum of range.
128 std::size_t max{0};
129
130protected:
131 friend std::ostream& operator<<(std::ostream& os, const ArgsNumRange& range);
132};
133
134class Argument;
135
136//! @brief Argument trait.
137class ArgTrait
138{
139public:
140 //! @brief Construct a new ArgTrait object.
141 //! @tparam Num - number of arguments
142 //! @tparam Is - indices of sequence related to arguments
143 //! @param prefix - prefix characters
144 //! @param collection - collection of arguments to be registered
145 //! @param sequence - sequence related to arguments
146 template <std::size_t Num, std::size_t... Is>
147 explicit ArgTrait(
148 const std::string_view prefix,
149 std::array<std::string_view, Num>&& collection,
150 const std::index_sequence<Is...>& sequence);
151 //! @brief Construct a new ArgTrait object.
152 //! @tparam Num - number of arguments
153 //! @param prefix - prefix characters
154 //! @param collection - collection of arguments to be registered
155 template <std::size_t Num>
156 explicit ArgTrait(const std::string_view prefix, std::array<std::string_view, Num>&& collection) :
157 ArgTrait(prefix, std::move(collection), std::make_index_sequence<Num>{})
158 {
159 }
160
161 //! @brief The operator (==) overloading of ArgTrait class.
162 //! @tparam RHS - type of right-hand side
163 //! @param rhs - right-hand side
164 //! @return be equal or not
165 template <typename RHS>
166 bool operator==(const RHS& rhs) const;
167 //! @brief The operator (!=) overloading of ArgTrait class.
168 //! @tparam RHS - type of right-hand side
169 //! @param rhs - right-hand side
170 //! @return be unequal or not
171 template <typename RHS>
172 bool operator!=(const RHS& rhs) const;
173
174 //! @brief Set help message.
175 //! @param message - help message
176 //! @return reference of the ArgTrait object
177 ArgTrait& help(const std::string_view message);
178 //! @brief Set meta variable.
179 //! @param variable - meta variable
180 //! @return reference of the ArgTrait object
181 ArgTrait& metaVariable(const std::string_view variable);
182 //! @brief Set default value.
183 //! @tparam Value - type of default value
184 //! @param value - default value
185 //! @return reference of the ArgTrait object
186 template <typename Value>
187 ArgTrait& defaultValue(Value&& value);
188 //! @brief Set default value.
189 //! @param value - default value
190 //! @return reference of the ArgTrait object
191 ArgTrait& defaultValue(const std::string_view value);
192 //! @brief Set implicit value.
193 //! @param value - implicit value
194 //! @return reference of the ArgTrait object
195 ArgTrait& implicitValue(std::any value);
196 //! @brief Set the argument property to be required.
197 //! @return reference of the ArgTrait object
198 ArgTrait& required();
199 //! @brief Set the argument property to be appending.
200 //! @return reference of the ArgTrait object
201 ArgTrait& appending();
202 //! @brief Set the argument property to be remaining.
203 //! @return reference of the ArgTrait object
204 ArgTrait& remaining();
205 //! @brief The action of specific arguments.
206 //! @tparam Func - type of callable function
207 //! @tparam Args - type of bound arguments
208 //! @param callable - callable function
209 //! @param boundArgs - bound arguments
210 //! @return reference of the ArgTrait object
211 template <typename Func, typename... Args>
212 auto action(Func&& callable, Args&&... boundArgs)
213 -> std::enable_if_t<std::is_invocable_v<Func, Args..., const std::string&>, ArgTrait&>;
214 //! @brief Set number of arguments.
215 //! @param num - number of arguments
216 //! @return reference of the ArgTrait object
217 ArgTrait& argsNum(const std::size_t num);
218 //! @brief Set minimum number and maximum number of arguments.
219 //! @param numMin - minimum number
220 //! @param numMax - maximum number
221 //! @return reference of the ArgTrait object
222 ArgTrait& argsNum(const std::size_t numMin, const std::size_t numMax);
223 //! @brief Set number of arguments with pattern.
224 //! @param pattern - argument pattern
225 //! @return reference of the ArgTrait object
226 ArgTrait& argsNum(const ArgsNumPattern pattern);
227 //! @brief Consume arguments.
228 //! @tparam Iterator - type of argument iterator
229 //! @param start - start argument iterator
230 //! @param end - end argument iterator
231 //! @param argName - target argument name
232 //! @return argument iterator between start and end
233 template <typename Iterator>
234 Iterator consume(const Iterator start, Iterator end, const std::string_view argName = {});
235 //! @brief Validate all arguments.
236 void validate() const;
237 //! @brief Get the inline usage.
238 //! @return inline usage
239 [[nodiscard]] std::string getInlineUsage() const;
240 //! @brief Get the length of all arguments.
241 //! @return length of all arguments
242 [[nodiscard]] std::size_t getArgumentsLength() const;
243 friend class Argument;
244
245private:
246 //! @brief Alias for the function which has valued return.
247 using ValuedAction = std::function<std::any(const std::string&)>;
248 //! @brief Alias for the function which has void return.
249 using VoidAction = std::function<void(const std::string&)>;
250 //! @brief All argument names.
251 std::vector<std::string> names;
252 //! @brief Used argument name.
253 std::string usedName;
254 //! @brief Help message.
255 std::string helpMsg;
256 //! @brief Meta variable.
257 std::string metaVar;
258 //! @brief Default value.
259 std::any defaultVal;
260 //! @brief Default value content to be represented.
261 std::string representedDefVal;
262 //! @brief Implicit value.
263 std::any implicitVal;
264 //! @brief All actions of arguments.
265 std::variant<ValuedAction, VoidAction> actions{
266 std::in_place_type<ValuedAction>, [](const std::string& value) { return value; }};
267 //! @brief Values from all arguments.
268 std::vector<std::any> values;
269 //! @brief Flag to indicate whether to accept optional like value.
270 bool optionalAsValue{false};
271 //! @brief Flag to indicate whether to be optional.
272 bool isOptional{true};
273 //! @brief Flag to indicate whether to be required.
274 bool isRequired{true};
275 //! @brief Flag to indicate whether to be repeatable.
276 bool isRepeatable{true};
277 //! @brief Flag to indicate whether to be used.
278 bool isUsed{true};
279 //! @brief End of file in arguments.
280 static constexpr int eof{std::char_traits<char>::eof()};
281 //! @brief Prefix characters.
282 std::string prefixChars;
283 //! @brief Maximum size for representing.
284 static constexpr std::size_t maxRepresentSize{5};
285 //! @brief The range for the number of arguments.
286 ArgsNumRange argsNumRange{1, 1};
287
288 //! @brief Represent target value.
289 //! @tparam Value - type of target value
290 //! @param value - target value
291 //! @return content to be represented
292 template <typename Value>
293 static std::string represent(const Value& value);
294 //! @brief Implementation of wrapping function calls that have scope.
295 //! @tparam Func - type of callable function
296 //! @tparam Tuple - type of bound arguments tuple
297 //! @tparam Extra - type of extra option
298 //! @tparam Is - number of sequence which converted from bound arguments tuple
299 //! @param func - callable function
300 //! @param tup - bound arguments tuple
301 //! @param ext - extra option
302 //! @param seq - sequence which converted from bound arguments tuple
303 //! @return wrapping of calls
304 template <typename Func, typename Tuple, typename Extra, std::size_t... Is>
305 static constexpr decltype(auto) applyScopedOneImpl(
306 Func&& func, Tuple&& tup, Extra&& ext, const std::index_sequence<Is...>& seq);
307 //! @brief Wrap function calls that have scope.
308 //! @tparam Func - type of callable function
309 //! @tparam Tuple - type of bound arguments tuple
310 //! @tparam Extra - type of extra option
311 //! @param func - callable function
312 //! @param tup - bound arguments tuple
313 //! @param ext - extra option
314 //! @return wrapping of calls
315 template <typename Func, typename Tuple, typename Extra>
316 static constexpr decltype(auto) applyScopedOne(Func&& func, Tuple&& tup, Extra&& ext);
317 //! @brief Throw an exception when the range for the number of arguments is invalid.
318 [[noreturn]] void throwInvalidArgsNumRange() const;
319 //! @brief Find the character in the argument.
320 //! @param name - name of argument
321 //! @return character
322 static int lookAhead(const std::string_view name);
323 //! @brief Check whether the argument is optional.
324 //! @param name - name of argument
325 //! @param prefix - prefix characters
326 //! @return be optional or not
327 static bool checkIfOptional(const std::string_view name, const std::string_view prefix);
328 //! @brief Check whether the argument is positional.
329 //! @param name - name of argument
330 //! @param prefix - prefix characters
331 //! @return be positional or not
332 static bool checkIfPositional(const std::string_view name, const std::string_view prefix);
333 //! @brief Get the value.
334 //! @tparam Value - type of value
335 //! @return value corresponding to the specific type
336 template <typename Value>
337 Value get() const;
338 //! @brief Retrieves the value of the argument, if any.
339 //! @tparam Value - type of value
340 //! @return an optional that contains the value of the argument if it exists, otherwise an empty optional
341 template <typename Value>
342 std::optional<Value> present() const;
343 //! @brief Convert the container type.
344 //! @tparam Container - type of container
345 //! @param operand - container to be converted
346 //! @return container after converting type
347 template <typename Container>
348 static Container anyCastContainer(const std::vector<std::any>& operand);
349 //! @brief Wrap for applying actions.
350 //! @tparam Iterator - type of argument iterator
351 template <typename Iterator>
352 struct ApplyAction
353 {
354 //! @brief The first argument iterator.
355 const Iterator first{};
356 //! @brief The last argument iterator.
357 const Iterator last{};
358 //! @brief The ArgTrait instance.
359 ArgTrait& self;
360
361 //! @brief The operator (()) overloading of ApplyAction struct.
362 //! @param func - function which has valued return
363 void operator()(const ValuedAction& func) const
364 {
365 std::transform(first, last, std::back_inserter(x&: self.values), func);
366 }
367 //! @brief The operator (()) overloading of ApplyAction struct.
368 //! @param func - function which has void return
369 void operator()(const VoidAction& func) const
370 {
371 std::for_each(first, last, func);
372 if (!self.defaultVal.has_value() && !self.optionalAsValue)
373 {
374 self.values.resize(new_size: static_cast<std::size_t>(std::distance(first, last)));
375 }
376 }
377 };
378
379protected:
380 friend std::ostream& operator<<(std::ostream& os, const ArgTrait& tra);
381 friend std::ostream& operator<<(std::ostream& os, const Argument& arg);
382};
383
384template <std::size_t Num, std::size_t... Is>
385ArgTrait::ArgTrait(
386 const std::string_view prefix,
387 std::array<std::string_view, Num>&& collection,
388 const std::index_sequence<Is...>& /*sequence*/) :
389 isOptional{(checkIfOptional(name: collection.at(Is), prefix) || ...)},
390 isRequired{false},
391 isRepeatable{false},
392 isUsed{false},
393 prefixChars{prefix}
394{
395 (names.emplace_back(std::move(collection).at(Is)), ...);
396 std::ranges::sort(
397 names,
398 [](const auto& lhs, const auto& rhs)
399 { return (lhs.size() == rhs.size()) ? (lhs < rhs) : (lhs.size() < rhs.size()); });
400}
401
402template <typename RHS>
403bool ArgTrait::operator==(const RHS& rhs) const
404{
405 if constexpr (!isContainer<RHS>)
406 {
407 return rhs == get<RHS>();
408 }
409 else
410 {
411 const auto lhs = get<RHS>();
412 return std::ranges::equal(
413 lhs,
414 rhs,
415 [](const auto& l, const auto& r) { return r == std::any_cast<const typename RHS::value_type&>(l); });
416 }
417}
418
419template <typename RHS>
420bool ArgTrait::operator!=(const RHS& rhs) const
421{
422 return !(rhs == *this);
423}
424
425template <typename Value>
426ArgTrait& ArgTrait::defaultValue(Value&& value)
427{
428 representedDefVal = represent(value);
429 defaultVal = std::forward<Value>(value);
430 return *this;
431}
432
433template <typename Func, typename... Args>
434auto ArgTrait::action(Func&& callable, Args&&... boundArgs)
435 -> std::enable_if_t<std::is_invocable_v<Func, Args..., const std::string&>, ArgTrait&>
436{
437 using ActionType = std::conditional_t<
438 std::is_void_v<std::invoke_result_t<Func, Args..., const std::string&>>,
439 VoidAction,
440 ValuedAction>;
441 if constexpr (!sizeof...(Args))
442 {
443 actions.emplace<ActionType>(std::forward<Func>(callable));
444 }
445 else
446 {
447 actions.emplace<ActionType>([func = std::forward<Func>(callable),
448 tup = std::make_tuple(std::forward<Args>(boundArgs)...)](const std::string& opt)
449 { return applyScopedOne(func, tup, opt); });
450 }
451 return *this;
452}
453
454template <typename Iterator>
455Iterator ArgTrait::consume(const Iterator start, Iterator end, const std::string_view argName)
456{
457 if (!isRepeatable && isUsed)
458 {
459 throw std::runtime_error{"Duplicate argument."};
460 }
461
462 isUsed = true;
463 usedName = argName;
464 const auto numMin = argsNumRange.min;
465 const auto numMax = argsNumRange.max;
466 if (numMax == 0)
467 {
468 values.emplace_back(args&: implicitVal);
469 std::visit([](const auto& func) { func({}); }, actions);
470 return start;
471 }
472 if (auto dist = static_cast<std::size_t>(std::distance(start, end)); dist >= numMin)
473 {
474 if (dist > numMax)
475 {
476 end = std::next(start, static_cast<typename Iterator::difference_type>(numMax));
477 }
478 if (!optionalAsValue)
479 {
480 end = std::find_if(start, end, std::bind(f&: checkIfOptional, args: std::placeholders::_1, args&: prefixChars));
481 dist = static_cast<std::size_t>(std::distance(start, end));
482 if (dist < numMin)
483 {
484 throw std::runtime_error{"Too few arguments."};
485 }
486 }
487 std::visit(ApplyAction<Iterator>{start, end, *this}, actions);
488 return end;
489 }
490 if (defaultVal.has_value())
491 {
492 return start;
493 }
494 throw std::runtime_error{"Too few arguments for '" + usedName + "'."};
495}
496
497template <typename Value>
498std::string ArgTrait::represent(const Value& value)
499{
500 if constexpr (std::is_same_v<Value, bool>)
501 {
502 return value ? "true" : "false";
503 }
504 else if constexpr (std::is_convertible_v<Value, std::string_view>)
505 {
506 return '"' + value + '"';
507 }
508 else if constexpr (isContainer<Value>)
509 {
510 std::ostringstream out{};
511 out << '{';
512 const auto size = value.size();
513 if (size > 1)
514 {
515 out << represent(*std::cbegin(value));
516 std::for_each(
517 std::next(std::cbegin(value)),
518 std::next(
519 std::cbegin(value),
520 static_cast<typename Value::iterator::difference_type>(
521 std::min<std::size_t>(size, maxRepresentSize) - 1)),
522 [&out](const auto& v) { out << ' ' << represent(v); });
523 if (size <= maxRepresentSize)
524 {
525 out << ' ';
526 }
527 else
528 {
529 out << " ... ";
530 }
531 }
532 if (size > 0)
533 {
534 out << represent(*std::prev(std::cend(value)));
535 }
536 out << '}';
537 return std::move(out).str();
538 }
539 else if constexpr (isStreamable<Value>)
540 {
541 std::ostringstream out{};
542 out << value;
543 return std::move(out).str();
544 }
545 else
546 {
547 return " not representable ";
548 }
549}
550
551template <typename Func, typename Tuple, typename Extra, std::size_t... Is>
552constexpr decltype(auto) ArgTrait::applyScopedOneImpl(
553 Func&& func, Tuple&& tup, Extra&& ext, const std::index_sequence<Is...>& /*seq*/)
554{
555 return std::invoke(std::forward<Func>(func), std::get<Is>(std::forward<Tuple>(tup))..., std::forward<Extra>(ext));
556}
557
558template <typename Func, typename Tuple, typename Extra>
559constexpr decltype(auto) ArgTrait::applyScopedOne(Func&& func, Tuple&& tup, Extra&& ext)
560{
561 return applyScopedOneImpl(
562 std::forward<Func>(func),
563 std::forward<Tuple>(tup),
564 std::forward<Extra>(ext),
565 std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
566}
567
568template <typename Value>
569Value ArgTrait::get() const
570{
571 if (!values.empty())
572 {
573 if constexpr (isContainer<Value>)
574 {
575 return anyCastContainer<Value>(values);
576 }
577 else
578 {
579 return std::any_cast<Value>(values.front());
580 }
581 }
582 if (defaultVal.has_value())
583 {
584 return std::any_cast<Value>(defaultVal);
585 }
586 if constexpr (isContainer<Value>)
587 {
588 if (!optionalAsValue)
589 {
590 return anyCastContainer<Value>(values);
591 }
592 }
593 throw std::runtime_error{"No value specified for '" + names.back() + "'."};
594}
595
596template <typename Value>
597std::optional<Value> ArgTrait::present() const
598{
599 if (defaultVal.has_value())
600 {
601 throw std::runtime_error{"Default value always presents."};
602 }
603 if (values.empty())
604 {
605 return std::nullopt;
606 }
607 if constexpr (isContainer<Value>)
608 {
609 return anyCastContainer<Value>(values);
610 }
611 return std::any_cast<Value>(values.front());
612}
613
614template <typename Container>
615Container ArgTrait::anyCastContainer(const std::vector<std::any>& operand)
616{
617 Container result{};
618 std::ranges::transform(
619 operand,
620 std::back_inserter(result),
621 [](const auto& value) { return std::any_cast<typename Container::value_type>(value); });
622 return result;
623}
624
625//! @brief Parse arguments.
626class Argument
627{
628public:
629 //! @brief Construct a new Argument object.
630 //! @param title - title name
631 //! @param version - version number
632 explicit Argument(const std::string_view title = {}, const std::string_view version = "1.0.0") :
633 titleName{title}, versionNumber{version}, parserPath{title}
634 {
635 }
636 //! @brief Destroy the Argument object.
637 virtual ~Argument() = default;
638 //! @brief Construct a new Argument object.
639 //! @param arg - object for copy constructor
640 Argument(const Argument& arg);
641 //! @brief Construct a new Argument object.
642 Argument(Argument&&) noexcept = default;
643 //! @brief The operator (=) overloading of Argument class.
644 //! @param arg - object for copy assignment operator
645 //! @return reference of the Argument object
646 Argument& operator=(const Argument& arg);
647 //! @brief The operator (=) overloading of Argument class.
648 //! @return reference of the Argument object
649 Argument& operator=(Argument&&) noexcept = default;
650
651 //! @brief The operator (bool) overloading of Argument class.
652 explicit operator bool() const;
653 //! @brief The operator ([]) overloading of ArgTrait class.
654 //! @param argName - target argument name
655 //! @return reference of the ArgTrait object
656 ArgTrait& operator[](const std::string_view argName) const;
657
658 //! @brief Add a single argument.
659 //! @tparam Args - type of arguments
660 //! @param fewArgs - argument name
661 //! @return reference of the ArgTrait object
662 template <typename... Args>
663 ArgTrait& addArgument(Args... fewArgs);
664 //! @brief Add a descrText.
665 //! @param text - descrText text
666 //! @return reference of the Argument object
667 Argument& addDescription(const std::string_view text);
668 //! @brief Get the ArgTrait or Argument instance by name.
669 //! @tparam Inst - type of instance
670 //! @param name - instance name
671 //! @return ArgTrait or Argument instance
672 template <typename Inst = ArgTrait>
673 Inst& at(const std::string_view name);
674 //! @brief Parse all input arguments.
675 //! @param arguments - container of all arguments
676 void parseArgs(const std::vector<std::string>& arguments);
677 //! @brief Parse all input arguments.
678 //! @param argc - argument count
679 //! @param argv - argument vector
680 void parseArgs(const int argc, const char* const argv[]);
681 //! @brief Get argument value by name.
682 //! @tparam Arg - type of argument
683 //! @param argName - target argument name
684 //! @return argument value
685 template <typename Arg = std::string>
686 Arg get(const std::string_view argName) const;
687 //! @brief Retrieves the value of the argument, if any.
688 //! @tparam Arg - type of argument
689 //! @param argName - target argument name
690 //! @return an optional that contains the value of the argument if it exists, otherwise an empty optional
691 template <typename Arg = std::string>
692 std::optional<Arg> present(const std::string_view argName) const;
693 //! @brief Check whether the argument is used.
694 //! @param argName - target argument name
695 //! @return be used or not
696 bool isUsed(const std::string_view argName) const;
697 //! @brief Check whether the sub-command is used.
698 //! @param parserName - target sub-parser name
699 //! @return be used or not
700 bool isSubcommandUsed(const std::string_view parserName) const;
701 //! @brief Check whether the sub-command is used.
702 //! @param parser - target sub-parser
703 //! @return be used or not
704 bool isSubcommandUsed(const Argument& parser) const;
705 //! @brief Clear all used flags.
706 void clearUsed();
707 //! @brief Get the title name.
708 //! @return title name
709 std::string title() const;
710 //! @brief Get the version number.
711 //! @return version number
712 std::string version() const;
713 //! @brief Get the help message content.
714 //! @return help message content
715 std::ostringstream help() const;
716 //! @brief Get the usage content.
717 //! @return usage content
718 std::string usage() const;
719 //! @brief Add a sub-parser.
720 //! @param parser - target sub-parser
721 void addSubParser(Argument& parser);
722
723private:
724 //! @brief Title name.
725 std::string titleName;
726 //! @brief Version number.
727 std::string versionNumber;
728
729 //! @brief Alias for the iterator in all ArgTrait instances.
730 using TraitIter = std::list<ArgTrait>::iterator;
731 //! @brief Alias for the iterator in all Argument instances.
732 using ArgumentIter = std::list<std::reference_wrapper<Argument>>::iterator;
733 //! @brief Description text.
734 std::string descrText;
735 //! @brief Prefix characters.
736 std::string prefixChars{"-"};
737 //! @brief Assign characters.
738 std::string assignChars{"="};
739 //! @brief Flag to indicate whether to be parsed.
740 bool isParsed{false};
741 //! @brief List of optional arguments.
742 std::list<ArgTrait> optionalArgs;
743 //! @brief List of positional arguments.
744 std::list<ArgTrait> positionalArgs;
745 //! @brief Mapping table of argument.
746 std::unordered_map<std::string_view, TraitIter> argumentMap;
747 //! @brief Current parser path.
748 std::string parserPath;
749 //! @brief List of sub-parsers.
750 std::list<std::reference_wrapper<Argument>> subParsers;
751 //! @brief Mapping table of sub-parser.
752 std::map<std::string_view, ArgumentIter> subParserMap;
753 //! @brief Mapping table of sub-parser usage.
754 std::map<std::string_view, bool> subParserUsed;
755
756 //! @brief Check whether the prefix character is valid.
757 //! @param c - prefix character
758 //! @return be valid or invalid
759 bool isValidPrefixChar(const char c) const;
760 //! @brief Get any valid prefix character.
761 //! @return valid prefix character
762 char getAnyValidPrefixChar() const;
763 //! @brief Preprocess all raw arguments.
764 //! @param rawArguments - container of all raw arguments
765 //! @return preprocessed argument container
766 std::vector<std::string> preprocessArguments(const std::vector<std::string>& rawArguments) const;
767 //! @brief Parse all input arguments for internal.
768 //! @param rawArguments - container of all raw arguments
769 void parseArgsInternal(const std::vector<std::string>& rawArguments);
770 //! @brief Process the registered argument.
771 //! @tparam Iterator - type of argument iterator
772 //! @param current - current argument iterator
773 //! @param end - end argument iterator
774 //! @param argName - target argument name
775 //! @return argument iterator between current and end
776 template <typename Iterator>
777 Iterator processRegArgument(Iterator current, const Iterator end, const std::string_view argName) const;
778 //! @brief Get the length of the longest argument.
779 //! @return length of the longest argument
780 std::size_t getLengthOfLongestArgument() const;
781 //! @brief Make index for argumentMap.
782 //! @param iterator - iterator in all argument traits
783 void indexArgument(const TraitIter& iterator);
784
785protected:
786 friend std::ostream& operator<<(std::ostream& os, const Argument& arg);
787};
788
789template <typename... Args>
790ArgTrait& Argument::addArgument(Args... fewArgs)
791{
792 const auto argument = optionalArgs.emplace(
793 optionalArgs.cend(), prefixChars, std::array<std::string_view, sizeof...(Args)>{fewArgs...});
794 if (!argument->isOptional)
795 {
796 positionalArgs.splice(positionalArgs.cend(), optionalArgs, argument);
797 }
798 indexArgument(iterator: argument);
799 return *argument;
800}
801
802template <typename Inst>
803Inst& Argument::at(const std::string_view name)
804{
805 if constexpr (std::is_same_v<Inst, ArgTrait>)
806 {
807 return (*this)[name];
808 }
809 else if (const auto subParserIter = subParserMap.find(x: name); subParserIter != subParserMap.cend())
810 {
811 return subParserIter->second->get();
812 }
813 throw std::runtime_error{"No such sub-parser: " + std::string{name} + '.'};
814}
815
816template <typename Arg>
817Arg Argument::get(const std::string_view argName) const
818{
819 if (!isParsed)
820 {
821 throw std::runtime_error{"Nothing parsed, no arguments are available."};
822 }
823 return (*this)[argName].get<Arg>();
824}
825
826template <typename Arg>
827std::optional<Arg> Argument::present(const std::string_view argName) const
828{
829 return (*this)[argName].present<Arg>();
830}
831
832template <typename Iterator>
833Iterator Argument::processRegArgument(Iterator current, const Iterator end, const std::string_view argName) const
834{
835 if (const auto argMapIter = argumentMap.find(x: argName); argMapIter != argumentMap.cend())
836 {
837 const auto argument = argMapIter->second;
838 current = argument->consume(std::next(current), end, argMapIter->first);
839 }
840 else if (const auto& compoundArg = argName; (compoundArg.length() > 1) && isValidPrefixChar(c: compoundArg.at(pos: 0))
841 && !isValidPrefixChar(c: compoundArg.at(pos: 1)))
842 {
843 ++current;
844 for (std::size_t i = 1; i < compoundArg.length(); ++i)
845 {
846 const auto hypotheticalArg = std::string{'-', compoundArg.at(pos: i)};
847 if (const auto argMapIter = argumentMap.find(x: hypotheticalArg); argMapIter != argumentMap.cend())
848 {
849 auto argument = argMapIter->second;
850 current = argument->consume(current, end, argMapIter->first);
851 continue;
852 }
853 throw std::runtime_error{"Unknown argument: " + std::string{argName} + '.'};
854 }
855 }
856 else
857 {
858 throw std::runtime_error{"Unknown argument: " + std::string{argName} + '.'};
859 }
860 return current;
861}
862} // namespace argument
863} // namespace utility
864