LCOV - code coverage report
Current view: top level - apps/argparse - argparse.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 649 1000 64.9 %
Date: 2024-11-21 22:18:42 Functions: 336 371 90.6 %

          Line data    Source code
       1             : /*
       2             :   __ _ _ __ __ _ _ __   __ _ _ __ ___  ___
       3             :  / _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++
       4             : | (_| | | | (_| | |_) | (_| | |  \__ \  __/ http://github.com/p-ranav/argparse
       5             :  \__,_|_|  \__, | .__/ \__,_|_|  |___/\___|
       6             :            |___/|_|
       7             : 
       8             : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
       9             : SPDX-License-Identifier: MIT
      10             : Copyright (c) 2019-2022 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>
      11             : and other contributors.
      12             : 
      13             : Permission is hereby  granted, free of charge, to any  person obtaining a copy
      14             : of this software and associated  documentation files (the "Software"), to deal
      15             : in the Software  without restriction, including without  limitation the rights
      16             : to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
      17             : copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
      18             : furnished to do so, subject to the following conditions:
      19             : 
      20             : The above copyright notice and this permission notice shall be included in all
      21             : copies or substantial portions of the Software.
      22             : 
      23             : THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
      24             : IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
      25             : FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
      26             : AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
      27             : LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      28             : OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
      29             : SOFTWARE.
      30             : */
      31             : #pragma once
      32             : 
      33             : #include <cerrno>
      34             : 
      35             : #ifndef ARGPARSE_MODULE_USE_STD_MODULE
      36             : #include <algorithm>
      37             : #include <any>
      38             : #include <array>
      39             : #include <set>
      40             : #include <charconv>
      41             : #include <cstdlib>
      42             : #include <functional>
      43             : #include <iomanip>
      44             : #include <iostream>
      45             : #include <iterator>
      46             : #include <limits>
      47             : #include <list>
      48             : #include <map>
      49             : #include <numeric>
      50             : #include <optional>
      51             : #include <sstream>
      52             : #include <stdexcept>
      53             : #include <string>
      54             : #include <string_view>
      55             : #include <tuple>
      56             : #include <type_traits>
      57             : #include <utility>
      58             : #include <variant>
      59             : #include <vector>
      60             : #endif
      61             : 
      62             : #ifndef ARGPARSE_CUSTOM_STRTOF
      63             : #define ARGPARSE_CUSTOM_STRTOF strtof
      64             : #endif
      65             : 
      66             : #ifndef ARGPARSE_CUSTOM_STRTOD
      67             : #define ARGPARSE_CUSTOM_STRTOD strtod
      68             : #endif
      69             : 
      70             : #ifndef ARGPARSE_CUSTOM_STRTOLD
      71             : #define ARGPARSE_CUSTOM_STRTOLD strtold
      72             : #endif
      73             : 
      74             : namespace argparse {
      75             : 
      76             : namespace details { // namespace for helper methods
      77             : 
      78             : template <typename T, typename = void>
      79             : struct HasContainerTraits : std::false_type {};
      80             : 
      81             : template <> struct HasContainerTraits<std::string> : std::false_type {};
      82             : 
      83             : template <> struct HasContainerTraits<std::string_view> : std::false_type {};
      84             : 
      85             : template <typename T>
      86             : struct HasContainerTraits<
      87             :     T, std::void_t<typename T::value_type, decltype(std::declval<T>().begin()),
      88             :                    decltype(std::declval<T>().end()),
      89             :                    decltype(std::declval<T>().size())>> : std::true_type {};
      90             : 
      91             : template <typename T>
      92             : inline constexpr bool IsContainer = HasContainerTraits<T>::value;
      93             : 
      94             : template <typename T, typename = void>
      95             : struct HasStreamableTraits : std::false_type {};
      96             : 
      97             : template <typename T>
      98             : struct HasStreamableTraits<
      99             :     T,
     100             :     std::void_t<decltype(std::declval<std::ostream &>() << std::declval<T>())>>
     101             :     : std::true_type {};
     102             : 
     103             : template <typename T>
     104             : inline constexpr bool IsStreamable = HasStreamableTraits<T>::value;
     105             : 
     106             : constexpr std::size_t repr_max_container_size = 5;
     107             : 
     108       89415 : template <typename T> std::string repr(T const &val) {
     109             :   if constexpr (std::is_same_v<T, bool>) {
     110       88059 :     return val ? "true" : "false";
     111             :   } else if constexpr (std::is_convertible_v<T, std::string_view>) {
     112         204 :     return '"' + std::string{std::string_view{val}} + '"';
     113             :   } else if constexpr (IsContainer<T>) {
     114             :     std::stringstream out;
     115             :     out << "{";
     116             :     const auto size = val.size();
     117             :     if (size > 1) {
     118             :       out << repr(*val.begin());
     119             :       std::for_each(
     120             :           std::next(val.begin()),
     121             :           std::next(
     122             :               val.begin(),
     123             :               static_cast<typename T::iterator::difference_type>(
     124             :                   std::min<std::size_t>(size, repr_max_container_size) - 1)),
     125             :           [&out](const auto &v) { out << " " << repr(v); });
     126             :       if (size <= repr_max_container_size) {
     127             :         out << " ";
     128             :       } else {
     129             :         out << "...";
     130             :       }
     131             :     }
     132             :     if (size > 0) {
     133             :       out << repr(*std::prev(val.end()));
     134             :     }
     135             :     out << "}";
     136             :     return out.str();
     137             :   } else if constexpr (IsStreamable<T>) {
     138        2304 :     std::stringstream out;
     139        1152 :     out << val;
     140        2304 :     return out.str();
     141             :   } else {
     142             :     return "<not representable>";
     143             :   }
     144             : }
     145             : 
     146             : namespace {
     147             : 
     148             : template <typename T> constexpr bool standard_signed_integer = false;
     149             : template <> constexpr bool standard_signed_integer<signed char> = true;
     150             : template <> constexpr bool standard_signed_integer<short int> = true;
     151             : template <> constexpr bool standard_signed_integer<int> = true;
     152             : template <> constexpr bool standard_signed_integer<long int> = true;
     153             : template <> constexpr bool standard_signed_integer<long long int> = true;
     154             : 
     155             : template <typename T> constexpr bool standard_unsigned_integer = false;
     156             : template <> constexpr bool standard_unsigned_integer<unsigned char> = true;
     157             : template <> constexpr bool standard_unsigned_integer<unsigned short int> = true;
     158             : template <> constexpr bool standard_unsigned_integer<unsigned int> = true;
     159             : template <> constexpr bool standard_unsigned_integer<unsigned long int> = true;
     160             : template <>
     161             : constexpr bool standard_unsigned_integer<unsigned long long int> = true;
     162             : 
     163             : } // namespace
     164             : 
     165             : constexpr int radix_2 = 2;
     166             : constexpr int radix_8 = 8;
     167             : constexpr int radix_10 = 10;
     168             : constexpr int radix_16 = 16;
     169             : 
     170             : template <typename T>
     171             : constexpr bool standard_integer =
     172             :     standard_signed_integer<T> || standard_unsigned_integer<T>;
     173             : 
     174             : template <class F, class Tuple, class Extra, std::size_t... I>
     175             : constexpr decltype(auto)
     176             : apply_plus_one_impl(F &&f, Tuple &&t, Extra &&x,
     177             :                     std::index_sequence<I...> /*unused*/) {
     178             :   return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...,
     179             :                      std::forward<Extra>(x));
     180             : }
     181             : 
     182             : template <class F, class Tuple, class Extra>
     183             : constexpr decltype(auto) apply_plus_one(F &&f, Tuple &&t, Extra &&x) {
     184             :   return details::apply_plus_one_impl(
     185             :       std::forward<F>(f), std::forward<Tuple>(t), std::forward<Extra>(x),
     186             :       std::make_index_sequence<
     187             :           std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
     188             : }
     189             : 
     190        6581 : constexpr auto pointer_range(std::string_view s) noexcept {
     191        6581 :   return std::tuple(s.data(), s.data() + s.size());
     192             : }
     193             : 
     194             : template <class CharT, class Traits>
     195       25206 : constexpr bool starts_with(std::basic_string_view<CharT, Traits> prefix,
     196             :                            std::basic_string_view<CharT, Traits> s) noexcept {
     197       25206 :   return s.substr(0, prefix.size()) == prefix;
     198             : }
     199             : 
     200             : enum class chars_format {
     201             :   scientific = 0xf1,
     202             :   fixed = 0xf2,
     203             :   hex = 0xf4,
     204             :   binary = 0xf8,
     205             :   general = fixed | scientific
     206             : };
     207             : 
     208             : struct ConsumeBinaryPrefixResult {
     209             :   bool is_binary;
     210             :   std::string_view rest;
     211             : };
     212             : 
     213        6162 : constexpr auto consume_binary_prefix(std::string_view s)
     214             :     -> ConsumeBinaryPrefixResult {
     215       12324 :   if (starts_with(std::string_view{"0b"}, s) ||
     216       12324 :       starts_with(std::string_view{"0B"}, s)) {
     217           0 :     s.remove_prefix(2);
     218           0 :     return {true, s};
     219             :   }
     220        6162 :   return {false, s};
     221             : }
     222             : 
     223             : struct ConsumeHexPrefixResult {
     224             :   bool is_hexadecimal;
     225             :   std::string_view rest;
     226             : };
     227             : 
     228             : using namespace std::literals;
     229             : 
     230        6162 : constexpr auto consume_hex_prefix(std::string_view s)
     231             :     -> ConsumeHexPrefixResult {
     232        6162 :   if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
     233           0 :     s.remove_prefix(2);
     234           0 :     return {true, s};
     235             :   }
     236        6162 :   return {false, s};
     237             : }
     238             : 
     239             : template <class T, auto Param>
     240         977 : inline auto do_from_chars(std::string_view s) -> T {
     241         977 :   T x{0};
     242         977 :   auto [first, last] = pointer_range(s);
     243         977 :   auto [ptr, ec] = std::from_chars(first, last, x, Param);
     244         977 :   if (ec == std::errc()) {
     245         977 :     if (ptr == last) {
     246         977 :       return x;
     247             :     }
     248             :     throw std::invalid_argument{"pattern '" + std::string(s) +
     249           0 :                                 "' does not match to the end"};
     250             :   }
     251           0 :   if (ec == std::errc::invalid_argument) {
     252           0 :     throw std::invalid_argument{"pattern '" + std::string(s) + "' not found"};
     253             :   }
     254           0 :   if (ec == std::errc::result_out_of_range) {
     255           0 :     throw std::range_error{"'" + std::string(s) + "' not representable"};
     256             :   }
     257           0 :   return x; // unreachable
     258             : }
     259             : 
     260             : template <class T, auto Param = 0> struct parse_number {
     261         419 :   auto operator()(std::string_view s) -> T {
     262         419 :     return do_from_chars<T, Param>(s);
     263             :   }
     264             : };
     265             : 
     266             : template <class T> struct parse_number<T, radix_2> {
     267             :   auto operator()(std::string_view s) -> T {
     268             :     if (auto [ok, rest] = consume_binary_prefix(s); ok) {
     269             :       return do_from_chars<T, radix_2>(rest);
     270             :     }
     271             :     throw std::invalid_argument{"pattern not found"};
     272             :   }
     273             : };
     274             : 
     275             : template <class T> struct parse_number<T, radix_16> {
     276             :   auto operator()(std::string_view s) -> T {
     277             :     if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
     278             :       if (auto [ok, rest] = consume_hex_prefix(s); ok) {
     279             :         try {
     280             :           return do_from_chars<T, radix_16>(rest);
     281             :         } catch (const std::invalid_argument &err) {
     282             :           throw std::invalid_argument("Failed to parse '" + std::string(s) +
     283             :                                       "' as hexadecimal: " + err.what());
     284             :         } catch (const std::range_error &err) {
     285             :           throw std::range_error("Failed to parse '" + std::string(s) +
     286             :                                  "' as hexadecimal: " + err.what());
     287             :         }
     288             :       }
     289             :     } else {
     290             :       // Allow passing hex numbers without prefix
     291             :       // Shape 'x' already has to be specified
     292             :       try {
     293             :         return do_from_chars<T, radix_16>(s);
     294             :       } catch (const std::invalid_argument &err) {
     295             :         throw std::invalid_argument("Failed to parse '" + std::string(s) +
     296             :                                     "' as hexadecimal: " + err.what());
     297             :       } catch (const std::range_error &err) {
     298             :         throw std::range_error("Failed to parse '" + std::string(s) +
     299             :                                "' as hexadecimal: " + err.what());
     300             :       }
     301             :     }
     302             : 
     303             :     throw std::invalid_argument{"pattern '" + std::string(s) +
     304             :                                 "' not identified as hexadecimal"};
     305             :   }
     306             : };
     307             : 
     308             : template <class T> struct parse_number<T> {
     309         558 :   auto operator()(std::string_view s) -> T {
     310         558 :     auto [ok, rest] = consume_hex_prefix(s);
     311         558 :     if (ok) {
     312             :       try {
     313           0 :         return do_from_chars<T, radix_16>(rest);
     314           0 :       } catch (const std::invalid_argument &err) {
     315             :         throw std::invalid_argument("Failed to parse '" + std::string(s) +
     316           0 :                                     "' as hexadecimal: " + err.what());
     317           0 :       } catch (const std::range_error &err) {
     318             :         throw std::range_error("Failed to parse '" + std::string(s) +
     319           0 :                                "' as hexadecimal: " + err.what());
     320             :       }
     321             :     }
     322             : 
     323         558 :     auto [ok_binary, rest_binary] = consume_binary_prefix(s);
     324         558 :     if (ok_binary) {
     325             :       try {
     326           0 :         return do_from_chars<T, radix_2>(rest_binary);
     327           0 :       } catch (const std::invalid_argument &err) {
     328             :         throw std::invalid_argument("Failed to parse '" + std::string(s) +
     329           0 :                                     "' as binary: " + err.what());
     330           0 :       } catch (const std::range_error &err) {
     331             :         throw std::range_error("Failed to parse '" + std::string(s) +
     332           0 :                                "' as binary: " + err.what());
     333             :       }
     334             :     }
     335             : 
     336         558 :     if (starts_with("0"sv, s)) {
     337             :       try {
     338          10 :         return do_from_chars<T, radix_8>(rest);
     339           0 :       } catch (const std::invalid_argument &err) {
     340             :         throw std::invalid_argument("Failed to parse '" + std::string(s) +
     341           0 :                                     "' as octal: " + err.what());
     342           0 :       } catch (const std::range_error &err) {
     343             :         throw std::range_error("Failed to parse '" + std::string(s) +
     344           0 :                                "' as octal: " + err.what());
     345             :       }
     346             :     }
     347             : 
     348             :     try {
     349         548 :       return do_from_chars<T, radix_10>(rest);
     350           0 :     } catch (const std::invalid_argument &err) {
     351             :       throw std::invalid_argument("Failed to parse '" + std::string(s) +
     352           0 :                                   "' as decimal integer: " + err.what());
     353           0 :     } catch (const std::range_error &err) {
     354             :       throw std::range_error("Failed to parse '" + std::string(s) +
     355           0 :                              "' as decimal integer: " + err.what());
     356             :     }
     357             :   }
     358             : };
     359             : 
     360             : namespace {
     361             : 
     362             : template <class T> inline const auto generic_strtod = nullptr;
     363             : template <> inline const auto generic_strtod<float> = ARGPARSE_CUSTOM_STRTOF;
     364             : template <> inline const auto generic_strtod<double> = ARGPARSE_CUSTOM_STRTOD;
     365             : template <>
     366             : inline const auto generic_strtod<long double> = ARGPARSE_CUSTOM_STRTOLD;
     367             : 
     368             : } // namespace
     369             : 
     370        5604 : template <class T> inline auto do_strtod(std::string const &s) -> T {
     371        5604 :   if (isspace(static_cast<unsigned char>(s[0])) || s[0] == '+') {
     372           0 :     throw std::invalid_argument{"pattern '" + s + "' not found"};
     373             :   }
     374             : 
     375        5604 :   auto [first, last] = pointer_range(s);
     376             :   char *ptr;
     377             : 
     378        5604 :   errno = 0;
     379        5604 :   auto x = generic_strtod<T>(first, &ptr);
     380        5604 :   if (errno == 0) {
     381        5604 :     if (ptr == last) {
     382        5599 :       return x;
     383             :     }
     384             :     throw std::invalid_argument{"pattern '" + s +
     385           5 :                                 "' does not match to the end"};
     386             :   }
     387           0 :   if (errno == ERANGE) {
     388           0 :     throw std::range_error{"'" + s + "' not representable"};
     389             :   }
     390           0 :   return x; // unreachable
     391             : }
     392             : 
     393             : template <class T> struct parse_number<T, chars_format::general> {
     394        5604 :   auto operator()(std::string const &s) -> T {
     395        5604 :     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
     396             :       throw std::invalid_argument{
     397           0 :           "chars_format::general does not parse hexfloat"};
     398             :     }
     399        5604 :     if (auto r = consume_binary_prefix(s); r.is_binary) {
     400             :       throw std::invalid_argument{
     401           0 :           "chars_format::general does not parse binfloat"};
     402             :     }
     403             : 
     404             :     try {
     405        5604 :       return do_strtod<T>(s);
     406          10 :     } catch (const std::invalid_argument &err) {
     407             :       throw std::invalid_argument("Failed to parse '" + s +
     408           5 :                                   "' as number: " + err.what());
     409           0 :     } catch (const std::range_error &err) {
     410             :       throw std::range_error("Failed to parse '" + s +
     411           0 :                              "' as number: " + err.what());
     412             :     }
     413             :   }
     414             : };
     415             : 
     416             : template <class T> struct parse_number<T, chars_format::hex> {
     417             :   auto operator()(std::string const &s) -> T {
     418             :     if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) {
     419             :       throw std::invalid_argument{"chars_format::hex parses hexfloat"};
     420             :     }
     421             :     if (auto r = consume_binary_prefix(s); r.is_binary) {
     422             :       throw std::invalid_argument{"chars_format::hex does not parse binfloat"};
     423             :     }
     424             : 
     425             :     try {
     426             :       return do_strtod<T>(s);
     427             :     } catch (const std::invalid_argument &err) {
     428             :       throw std::invalid_argument("Failed to parse '" + s +
     429             :                                   "' as hexadecimal: " + err.what());
     430             :     } catch (const std::range_error &err) {
     431             :       throw std::range_error("Failed to parse '" + s +
     432             :                              "' as hexadecimal: " + err.what());
     433             :     }
     434             :   }
     435             : };
     436             : 
     437             : template <class T> struct parse_number<T, chars_format::binary> {
     438             :   auto operator()(std::string const &s) -> T {
     439             :     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
     440             :       throw std::invalid_argument{
     441             :           "chars_format::binary does not parse hexfloat"};
     442             :     }
     443             :     if (auto r = consume_binary_prefix(s); !r.is_binary) {
     444             :       throw std::invalid_argument{"chars_format::binary parses binfloat"};
     445             :     }
     446             : 
     447             :     return do_strtod<T>(s);
     448             :   }
     449             : };
     450             : 
     451             : template <class T> struct parse_number<T, chars_format::scientific> {
     452             :   auto operator()(std::string const &s) -> T {
     453             :     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
     454             :       throw std::invalid_argument{
     455             :           "chars_format::scientific does not parse hexfloat"};
     456             :     }
     457             :     if (auto r = consume_binary_prefix(s); r.is_binary) {
     458             :       throw std::invalid_argument{
     459             :           "chars_format::scientific does not parse binfloat"};
     460             :     }
     461             :     if (s.find_first_of("eE") == std::string::npos) {
     462             :       throw std::invalid_argument{
     463             :           "chars_format::scientific requires exponent part"};
     464             :     }
     465             : 
     466             :     try {
     467             :       return do_strtod<T>(s);
     468             :     } catch (const std::invalid_argument &err) {
     469             :       throw std::invalid_argument("Failed to parse '" + s +
     470             :                                   "' as scientific notation: " + err.what());
     471             :     } catch (const std::range_error &err) {
     472             :       throw std::range_error("Failed to parse '" + s +
     473             :                              "' as scientific notation: " + err.what());
     474             :     }
     475             :   }
     476             : };
     477             : 
     478             : template <class T> struct parse_number<T, chars_format::fixed> {
     479             :   auto operator()(std::string const &s) -> T {
     480             :     if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
     481             :       throw std::invalid_argument{
     482             :           "chars_format::fixed does not parse hexfloat"};
     483             :     }
     484             :     if (auto r = consume_binary_prefix(s); r.is_binary) {
     485             :       throw std::invalid_argument{
     486             :           "chars_format::fixed does not parse binfloat"};
     487             :     }
     488             :     if (s.find_first_of("eE") != std::string::npos) {
     489             :       throw std::invalid_argument{
     490             :           "chars_format::fixed does not parse exponent part"};
     491             :     }
     492             : 
     493             :     try {
     494             :       return do_strtod<T>(s);
     495             :     } catch (const std::invalid_argument &err) {
     496             :       throw std::invalid_argument("Failed to parse '" + s +
     497             :                                   "' as fixed notation: " + err.what());
     498             :     } catch (const std::range_error &err) {
     499             :       throw std::range_error("Failed to parse '" + s +
     500             :                              "' as fixed notation: " + err.what());
     501             :     }
     502             :   }
     503             : };
     504             : 
     505             : template <typename StrIt>
     506           0 : std::string join(StrIt first, StrIt last, const std::string &separator) {
     507           0 :   if (first == last) {
     508           0 :     return "";
     509             :   }
     510           0 :   std::stringstream value;
     511           0 :   value << *first;
     512           0 :   ++first;
     513           0 :   while (first != last) {
     514           0 :     value << separator << *first;
     515           0 :     ++first;
     516             :   }
     517           0 :   return value.str();
     518             : }
     519             : 
     520             : template <typename T> struct can_invoke_to_string {
     521             :   template <typename U>
     522             :   static auto test(int)
     523             :       -> decltype(std::to_string(std::declval<U>()), std::true_type{});
     524             : 
     525             :   template <typename U> static auto test(...) -> std::false_type;
     526             : 
     527             :   static constexpr bool value = decltype(test<T>(0))::value;
     528             : };
     529             : 
     530             : template <typename T> struct IsChoiceTypeSupported {
     531             :   using CleanType = typename std::decay<T>::type;
     532             :   static const bool value = std::is_integral<CleanType>::value ||
     533             :                             std::is_same<CleanType, std::string>::value ||
     534             :                             std::is_same<CleanType, std::string_view>::value ||
     535             :                             std::is_same<CleanType, const char *>::value;
     536             : };
     537             : 
     538             : template <typename StringType>
     539           0 : std::size_t get_levenshtein_distance(const StringType &s1,
     540             :                                      const StringType &s2) {
     541           0 :   std::vector<std::vector<std::size_t>> dp(
     542           0 :       s1.size() + 1, std::vector<std::size_t>(s2.size() + 1, 0));
     543             : 
     544           0 :   for (std::size_t i = 0; i <= s1.size(); ++i) {
     545           0 :     for (std::size_t j = 0; j <= s2.size(); ++j) {
     546           0 :       if (i == 0) {
     547           0 :         dp[i][j] = j;
     548           0 :       } else if (j == 0) {
     549           0 :         dp[i][j] = i;
     550           0 :       } else if (s1[i - 1] == s2[j - 1]) {
     551           0 :         dp[i][j] = dp[i - 1][j - 1];
     552             :       } else {
     553           0 :         dp[i][j] = 1 + std::min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
     554             :       }
     555             :     }
     556             :   }
     557             : 
     558           0 :   return dp[s1.size()][s2.size()];
     559             : }
     560             : 
     561             : template <typename ValueType>
     562           0 : std::string get_most_similar_string(const std::map<std::string, ValueType> &map,
     563             :                                     const std::string &input) {
     564           0 :   std::string most_similar{};
     565           0 :   std::size_t min_distance = std::numeric_limits<std::size_t>::max();
     566             : 
     567           0 :   for (const auto &entry : map) {
     568           0 :     std::size_t distance = get_levenshtein_distance(entry.first, input);
     569           0 :     if (distance < min_distance) {
     570           0 :       min_distance = distance;
     571           0 :       most_similar = entry.first;
     572             :     }
     573             :   }
     574             : 
     575           0 :   return most_similar;
     576             : }
     577             : 
     578             : } // namespace details
     579             : 
     580             : enum class nargs_pattern { optional, any, at_least_one };
     581             : 
     582             : enum class default_arguments : unsigned int {
     583             :   none = 0,
     584             :   help = 1,
     585             :   version = 2,
     586             :   all = help | version,
     587             : };
     588             : 
     589       10844 : inline default_arguments operator&(const default_arguments &a,
     590             :                                    const default_arguments &b) {
     591             :   return static_cast<default_arguments>(
     592       10844 :       static_cast<std::underlying_type<default_arguments>::type>(a) &
     593       10844 :       static_cast<std::underlying_type<default_arguments>::type>(b));
     594             : }
     595             : 
     596             : class ArgumentParser;
     597             : 
     598             : class Argument {
     599             :   friend class ArgumentParser;
     600             :   friend auto operator<<(std::ostream &stream, const ArgumentParser &parser)
     601             :       -> std::ostream &;
     602             : 
     603             :   template <std::size_t N, std::size_t... I>
     604      230132 :   explicit Argument(std::string_view prefix_chars,
     605             :                     std::array<std::string_view, N> &&a,
     606             :                     std::index_sequence<I...> /*unused*/)
     607             :       : m_accepts_optional_like_value(false),
     608      230132 :         m_is_optional((is_optional(a[I], prefix_chars) || ...)),
     609             :         m_is_required(false), m_is_repeatable(false), m_is_used(false),
     610      238325 :         m_is_hidden(false), m_prefix_chars(prefix_chars) {
     611      230132 :     ((void)m_names.emplace_back(a[I]), ...);
     612      230132 :     std::sort(
     613       16386 :         m_names.begin(), m_names.end(), [](const auto &lhs, const auto &rhs) {
     614       16386 :           return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size();
     615             :         });
     616      230132 :   }
     617             : 
     618             : public:
     619             :   template <std::size_t N>
     620      230132 :   explicit Argument(std::string_view prefix_chars,
     621             :                     std::array<std::string_view, N> &&a)
     622      230132 :       : Argument(prefix_chars, std::move(a), std::make_index_sequence<N>{}) {}
     623             : 
     624      227714 :   Argument &help(std::string help_text) {
     625      227714 :     m_help = std::move(help_text);
     626      227714 :     return *this;
     627             :   }
     628             : 
     629      150759 :   Argument &metavar(std::string metavar) {
     630      150759 :     m_metavar = std::move(metavar);
     631      150759 :     return *this;
     632             :   }
     633             : 
     634       89415 :   template <typename T> Argument &default_value(T &&value) {
     635       89415 :     m_num_args_range = NArgsRange{0, m_num_args_range.get_max()};
     636       89415 :     m_default_value_repr = details::repr(value);
     637             : 
     638             :     if constexpr (std::is_convertible_v<T, std::string_view>) {
     639         204 :       m_default_value_str = std::string{std::string_view{value}};
     640             :     } else if constexpr (details::can_invoke_to_string<T>::value) {
     641       89211 :       m_default_value_str = std::to_string(value);
     642             :     }
     643             : 
     644       89415 :     m_default_value = std::forward<T>(value);
     645       89415 :     return *this;
     646             :   }
     647             : 
     648         204 :   Argument &default_value(const char *value) {
     649         204 :     return default_value(std::string(value));
     650             :   }
     651             : 
     652             :   Argument &required() {
     653             :     m_is_required = true;
     654             :     return *this;
     655             :   }
     656             : 
     657       88059 :   Argument &implicit_value(std::any value) {
     658       88059 :     m_implicit_value = std::move(value);
     659       88059 :     m_num_args_range = NArgsRange{0, 0};
     660       88059 :     return *this;
     661             :   }
     662             : 
     663             :   // This is shorthand for:
     664             :   //   program.add_argument("foo")
     665             :   //     .default_value(false)
     666             :   //     .implicit_value(true)
     667       87097 :   Argument &flag() {
     668       87097 :     default_value(false);
     669       87097 :     implicit_value(true);
     670       87097 :     return *this;
     671             :   }
     672             : 
     673             :   template <class F, class... Args>
     674      216362 :   auto action(F &&callable, Args &&... bound_args)
     675             :       -> std::enable_if_t<std::is_invocable_v<F, Args..., std::string const>,
     676             :                           Argument &> {
     677             :     using action_type = std::conditional_t<
     678             :         std::is_void_v<std::invoke_result_t<F, Args..., std::string const>>,
     679             :         void_action, valued_action>;
     680             :     if constexpr (sizeof...(Args) == 0) {
     681      216362 :       m_actions.emplace_back<action_type>(std::forward<F>(callable));
     682             :     } else {
     683             :       m_actions.emplace_back<action_type>(
     684             :           [f = std::forward<F>(callable),
     685             :            tup = std::make_tuple(std::forward<Args>(bound_args)...)](
     686             :               std::string const &opt) mutable {
     687             :             return details::apply_plus_one(f, tup, opt);
     688             :           });
     689             :     }
     690      216362 :     return *this;
     691             :   }
     692             : 
     693       35375 :   auto &store_into(bool &var) {
     694       35375 :     flag();
     695       35375 :     if (m_default_value.has_value()) {
     696       35375 :       var = std::any_cast<bool>(m_default_value);
     697             :     }
     698       35375 :     action([&var](const auto & /*unused*/) { var = true; });
     699       35375 :     return *this;
     700             :   }
     701             : 
     702             :   template <typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
     703        5805 :   auto &store_into(T &var) {
     704        5805 :     if (m_default_value.has_value()) {
     705             :       try
     706             :       {
     707         218 :         var = std::any_cast<T>(m_default_value);
     708             :       }
     709          22 :       catch (...)
     710             :       {
     711          22 :         var = static_cast<T>(std::any_cast<int>(m_default_value));
     712             :       }
     713             :     }
     714        5886 :     action([&var](const auto &s) {
     715         147 :       var = details::parse_number<T, details::radix_10>()(s);
     716             :     });
     717        5805 :     return *this;
     718             :   }
     719             : 
     720       13959 :   auto &store_into(double &var) {
     721       13959 :     if (m_default_value.has_value()) {
     722             :       try
     723             :       {
     724         934 :         var = std::any_cast<double>(m_default_value);
     725             :       }
     726         110 :       catch (...)
     727             :       {
     728         110 :         var = std::any_cast<int>(m_default_value);
     729             :       }
     730             :     }
     731         224 :     action([&var](const auto &s) {
     732         224 :       var = details::parse_number<double, details::chars_format::general>()(s);
     733       14178 :     });
     734       13959 :     return *this;
     735             :   }
     736             : 
     737       30260 :   auto &store_into(std::string &var) {
     738       30260 :     if (m_default_value.has_value()) {
     739          57 :       var = std::any_cast<std::string>(m_default_value);
     740             :     }
     741       33896 :     action([&var](const std::string &s) { var = s; });
     742       30260 :     return *this;
     743             :   }
     744             : 
     745         452 :   auto &store_into(std::vector<std::string> &var) {
     746         452 :     if (m_default_value.has_value()) {
     747           0 :       var = std::any_cast<std::vector<std::string>>(m_default_value);
     748             :     }
     749         645 :     action([this, &var](const std::string &s) {
     750         220 :       if (!m_is_used) {
     751         205 :         var.clear();
     752             :       }
     753         220 :       m_is_used = true;
     754         220 :       var.push_back(s);
     755         452 :     });
     756         452 :     return *this;
     757             :   }
     758             : 
     759        1981 :   auto &store_into(std::vector<int> &var) {
     760        1981 :     if (m_default_value.has_value()) {
     761           0 :       var = std::any_cast<std::vector<int>>(m_default_value);
     762             :     }
     763         588 :     action([this, &var](const std::string &s) {
     764         272 :       if (!m_is_used) {
     765          44 :         var.clear();
     766             :       }
     767         272 :       m_is_used = true;
     768         272 :       var.push_back(details::parse_number<int, details::radix_10>()(s));
     769        2253 :     });
     770        1981 :     return *this;
     771             :   }
     772             : 
     773          30 :   auto &store_into(std::set<std::string> &var) {
     774          30 :     if (m_default_value.has_value()) {
     775           0 :       var = std::any_cast<std::set<std::string>>(m_default_value);
     776             :     }
     777           6 :     action([this, &var](const std::string &s) {
     778           2 :       if (!m_is_used) {
     779           2 :         var.clear();
     780             :       }
     781           2 :       m_is_used = true;
     782           2 :       var.insert(s);
     783          30 :     });
     784          30 :     return *this;
     785             :   }
     786             : 
     787             :   auto &store_into(std::set<int> &var) {
     788             :     if (m_default_value.has_value()) {
     789             :       var = std::any_cast<std::set<int>>(m_default_value);
     790             :     }
     791             :     action([this, &var](const std::string &s) {
     792             :       if (!m_is_used) {
     793             :         var.clear();
     794             :       }
     795             :       m_is_used = true;
     796             :       var.insert(details::parse_number<int, details::radix_10>()(s));
     797             :     });
     798             :     return *this;
     799             :   }
     800             : 
     801       40439 :   auto &append() {
     802       40439 :     m_is_repeatable = true;
     803       40439 :     return *this;
     804             :   }
     805             : 
     806             :   // Cause the argument to be invisible in usage and help
     807       11200 :   auto &hidden() {
     808       11200 :     m_is_hidden = true;
     809       11200 :     return *this;
     810             :   }
     811             : 
     812             :   template <char Shape, typename T>
     813       28771 :   auto scan() -> std::enable_if_t<std::is_arithmetic_v<T>, Argument &> {
     814             :     static_assert(!(std::is_const_v<T> || std::is_volatile_v<T>),
     815             :                   "T should not be cv-qualified");
     816             :     auto is_one_of = [](char c, auto... x) constexpr {
     817             :       return ((c == x) || ...);
     818             :     };
     819             : 
     820             :     if constexpr (is_one_of(Shape, 'd') && details::standard_integer<T>) {
     821             :       action(details::parse_number<T, details::radix_10>());
     822             :     } else if constexpr (is_one_of(Shape, 'i') &&
     823             :                          details::standard_integer<T>) {
     824        2463 :       action(details::parse_number<T>());
     825             :     } else if constexpr (is_one_of(Shape, 'u') &&
     826             :                          details::standard_unsigned_integer<T>) {
     827             :       action(details::parse_number<T, details::radix_10>());
     828             :     } else if constexpr (is_one_of(Shape, 'b') &&
     829             :                          details::standard_unsigned_integer<T>) {
     830             :       action(details::parse_number<T, details::radix_2>());
     831             :     } else if constexpr (is_one_of(Shape, 'o') &&
     832             :                          details::standard_unsigned_integer<T>) {
     833             :       action(details::parse_number<T, details::radix_8>());
     834             :     } else if constexpr (is_one_of(Shape, 'x', 'X') &&
     835             :                          details::standard_unsigned_integer<T>) {
     836             :       action(details::parse_number<T, details::radix_16>());
     837             :     } else if constexpr (is_one_of(Shape, 'a', 'A') &&
     838             :                          std::is_floating_point_v<T>) {
     839             :       action(details::parse_number<T, details::chars_format::hex>());
     840             :     } else if constexpr (is_one_of(Shape, 'e', 'E') &&
     841             :                          std::is_floating_point_v<T>) {
     842             :       action(details::parse_number<T, details::chars_format::scientific>());
     843             :     } else if constexpr (is_one_of(Shape, 'f', 'F') &&
     844             :                          std::is_floating_point_v<T>) {
     845             :       action(details::parse_number<T, details::chars_format::fixed>());
     846             :     } else if constexpr (is_one_of(Shape, 'g', 'G') &&
     847             :                          std::is_floating_point_v<T>) {
     848       26308 :       action(details::parse_number<T, details::chars_format::general>());
     849             :     } else {
     850             :       static_assert(alignof(T) == 0, "No scan specification for T");
     851             :     }
     852             : 
     853       28771 :     return *this;
     854             :   }
     855             : 
     856       19093 :   Argument &nargs(std::size_t num_args) {
     857       19093 :     m_num_args_range = NArgsRange{num_args, num_args};
     858       19093 :     return *this;
     859             :   }
     860             : 
     861        2964 :   Argument &nargs(std::size_t num_args_min, std::size_t num_args_max) {
     862        2964 :     m_num_args_range = NArgsRange{num_args_min, num_args_max};
     863        2964 :     return *this;
     864             :   }
     865             : 
     866        1169 :   Argument &nargs(nargs_pattern pattern) {
     867        1169 :     switch (pattern) {
     868         105 :     case nargs_pattern::optional:
     869         105 :       m_num_args_range = NArgsRange{0, 1};
     870         105 :       break;
     871         943 :     case nargs_pattern::any:
     872         943 :       m_num_args_range =
     873         943 :           NArgsRange{0, (std::numeric_limits<std::size_t>::max)()};
     874         943 :       break;
     875         121 :     case nargs_pattern::at_least_one:
     876         121 :       m_num_args_range =
     877         121 :           NArgsRange{1, (std::numeric_limits<std::size_t>::max)()};
     878         121 :       break;
     879             :     }
     880        1169 :     return *this;
     881             :   }
     882             : 
     883         913 :   Argument &remaining() {
     884         913 :     m_accepts_optional_like_value = true;
     885         913 :     return nargs(nargs_pattern::any);
     886             :   }
     887             : 
     888        4326 :   template <typename T> void add_choice(T &&choice) {
     889             :     static_assert(details::IsChoiceTypeSupported<T>::value,
     890             :                   "Only string or integer type supported for choice");
     891             :     static_assert(std::is_convertible_v<T, std::string_view> ||
     892             :                       details::can_invoke_to_string<T>::value,
     893             :                   "Choice is not convertible to string_type");
     894        4326 :     if (!m_choices.has_value()) {
     895        1369 :       m_choices = std::vector<std::string>{};
     896             :     }
     897             : 
     898             :     if constexpr (std::is_convertible_v<T, std::string_view>) {
     899        8652 :       m_choices.value().push_back(
     900        4326 :           std::string{std::string_view{std::forward<T>(choice)}});
     901             :     } else if constexpr (details::can_invoke_to_string<T>::value) {
     902             :       m_choices.value().push_back(std::to_string(std::forward<T>(choice)));
     903             :     }
     904        4326 :   }
     905             : 
     906        1369 :   Argument &choices() {
     907        1369 :     if (!m_choices.has_value()) {
     908           0 :       throw std::runtime_error("Zero choices provided");
     909             :     }
     910        1369 :     return *this;
     911             :   }
     912             : 
     913             :   template <typename T, typename... U>
     914        4326 :   Argument &choices(T &&first, U &&... rest) {
     915        4326 :     add_choice(std::forward<T>(first));
     916        4326 :     choices(std::forward<U>(rest)...);
     917        4326 :     return *this;
     918             :   }
     919             : 
     920        1343 :   void find_default_value_in_choices_or_throw() const {
     921             : 
     922        1343 :     const auto &choices = m_choices.value();
     923             : 
     924        1343 :     if (m_default_value.has_value()) {
     925          96 :       if (std::find(choices.begin(), choices.end(), m_default_value_str) ==
     926         192 :           choices.end()) {
     927             :         // provided arg not in list of allowed choices
     928             :         // report error
     929             : 
     930             :         std::string choices_as_csv =
     931           0 :             std::accumulate(choices.begin(), choices.end(), std::string(),
     932           0 :                             [](const std::string &a, const std::string &b) {
     933           0 :                               return a + (a.empty() ? "" : ", ") + b;
     934           0 :                             });
     935             : 
     936             :         throw std::runtime_error(
     937           0 :             std::string{"Invalid default value "} + m_default_value_repr +
     938           0 :             " - allowed options: {" + choices_as_csv + "}");
     939             :       }
     940             :     }
     941        1343 :   }
     942             : 
     943             :   template <typename Iterator>
     944         188 :   void find_value_in_choices_or_throw(Iterator it) const {
     945             : 
     946         188 :     const auto &choices = m_choices.value();
     947             : 
     948         188 :     if (std::find(choices.begin(), choices.end(), *it) == choices.end()) {
     949             :       // provided arg not in list of allowed choices
     950             :       // report error
     951             : 
     952           0 :       std::string choices_as_csv =
     953             :           std::accumulate(choices.begin(), choices.end(), std::string(),
     954           0 :                           [](const std::string &a, const std::string &b) {
     955           0 :                             return a + (a.empty() ? "" : ", ") + b;
     956             :                           });
     957             : 
     958             :       throw std::runtime_error(std::string{"Invalid argument "} +
     959           0 :                                details::repr(*it) + " - allowed options: {" +
     960           0 :                                choices_as_csv + "}");
     961             :     }
     962         188 :   }
     963             : 
     964             :   /* The dry_run parameter can be set to true to avoid running the actions,
     965             :    * and setting m_is_used. This may be used by a pre-processing step to do
     966             :    * a first iteration over arguments.
     967             :    */
     968             :   template <typename Iterator>
     969       23679 :   Iterator consume(Iterator start, Iterator end,
     970             :                    std::string_view used_name = {}, bool dry_run = false) {
     971       23679 :     if (!m_is_repeatable && m_is_used) {
     972             :       throw std::runtime_error(
     973           0 :           std::string("Duplicate argument ").append(used_name));
     974             :     }
     975       23679 :     m_used_name = used_name;
     976             : 
     977       23679 :     if (m_choices.has_value()) {
     978             :       // Check each value in (start, end) and make sure
     979             :       // it is in the list of allowed choices/options
     980         188 :       std::size_t i = 0;
     981         188 :       auto max_number_of_args = m_num_args_range.get_max();
     982         376 :       for (auto it = start; it != end; ++it) {
     983         270 :         if (i == max_number_of_args) {
     984          82 :           break;
     985             :         }
     986         188 :         find_value_in_choices_or_throw(it);
     987         188 :         i += 1;
     988             :       }
     989             :     }
     990             : 
     991       23679 :     const auto num_args_max = m_num_args_range.get_max();
     992       23679 :     const auto num_args_min = m_num_args_range.get_min();
     993       23679 :     std::size_t dist = 0;
     994       23679 :     if (num_args_max == 0) {
     995        1791 :       if (!dry_run) {
     996         836 :         m_values.emplace_back(m_implicit_value);
     997        1644 :         for(auto &action: m_actions) {
     998        1684 :           std::visit([&](const auto &f) { f({}); }, action);
     999             :         }
    1000         802 :         if(m_actions.empty()){
    1001           0 :           std::visit([&](const auto &f) { f({}); }, m_default_action);
    1002             :         }
    1003         802 :         m_is_used = true;
    1004             :       }
    1005        1757 :       return start;
    1006             :     }
    1007       21888 :     if ((dist = static_cast<std::size_t>(std::distance(start, end))) >=
    1008             :         num_args_min) {
    1009       21887 :       if (num_args_max < dist) {
    1010       12645 :         end = std::next(start, static_cast<typename Iterator::difference_type>(
    1011             :                                    num_args_max));
    1012             :       }
    1013       21887 :       if (!m_accepts_optional_like_value) {
    1014       21780 :         end = std::find_if(
    1015             :             start, end,
    1016       21780 :             std::bind(is_optional, std::placeholders::_1, m_prefix_chars));
    1017       21780 :         dist = static_cast<std::size_t>(std::distance(start, end));
    1018       21780 :         if (dist < num_args_min) {
    1019             :           throw std::runtime_error("Too few arguments for '" +
    1020           0 :                                    std::string(m_used_name) + "'.");
    1021             :         }
    1022             :       }
    1023             : 
    1024             :       struct ActionApply {
    1025        2128 :         void operator()(valued_action &f) {
    1026        2128 :           std::transform(first, last, std::back_inserter(self.m_values), f);
    1027        2128 :         }
    1028             : 
    1029        8269 :         void operator()(void_action &f) {
    1030        8269 :           std::for_each(first, last, f);
    1031        8238 :           if (!self.m_default_value.has_value()) {
    1032        8086 :             if (!self.m_accepts_optional_like_value) {
    1033       16154 :               self.m_values.resize(
    1034        8077 :                   static_cast<std::size_t>(std::distance(first, last)));
    1035             :             }
    1036             :           }
    1037        8238 :         }
    1038             : 
    1039             :         Iterator first, last;
    1040             :         Argument &self;
    1041             :       };
    1042       21887 :       if (!dry_run) {
    1043       20313 :         for(auto &action: m_actions) {
    1044       10130 :           std::visit(ActionApply{start, end, *this}, action);
    1045             :         }
    1046       10183 :         if(m_actions.empty()){
    1047         267 :           std::visit(ActionApply{start, end, *this}, m_default_action);
    1048             :         }
    1049       10183 :         m_is_used = true;
    1050             :       }
    1051       21856 :       return end;
    1052             :     }
    1053           1 :     if (m_default_value.has_value()) {
    1054           0 :       if (!dry_run) {
    1055           0 :         m_is_used = true;
    1056             :       }
    1057           0 :       return start;
    1058             :     }
    1059             :     throw std::runtime_error("Too few arguments for '" +
    1060           1 :                              std::string(m_used_name) + "'.");
    1061             :   }
    1062             : 
    1063             :   /*
    1064             :    * @throws std::runtime_error if argument values are not valid
    1065             :    */
    1066      235443 :   void validate() const {
    1067      235443 :     if (m_is_optional) {
    1068             :       // TODO: check if an implicit value was programmed for this argument
    1069      233162 :       if (!m_is_used && !m_default_value.has_value() && m_is_required) {
    1070           0 :         throw_required_arg_not_used_error();
    1071             :       }
    1072      233162 :       if (m_is_used && m_is_required && m_values.empty()) {
    1073           0 :         throw_required_arg_no_value_provided_error();
    1074             :       }
    1075             :     } else {
    1076        2286 :       if (!m_num_args_range.contains(m_values.size()) &&
    1077           5 :           !m_default_value.has_value()) {
    1078           5 :         throw_nargs_range_validation_error();
    1079             :       }
    1080             :     }
    1081             : 
    1082      235438 :     if (m_choices.has_value()) {
    1083             :       // Make sure the default value (if provided)
    1084             :       // is in the list of choices
    1085        1343 :       find_default_value_in_choices_or_throw();
    1086             :     }
    1087      235438 :   }
    1088             : 
    1089           2 :   std::string get_names_csv(char separator = ',') const {
    1090             :     return std::accumulate(
    1091           4 :         m_names.begin(), m_names.end(), std::string{""},
    1092           2 :         [&](const std::string &result, const std::string &name) {
    1093           4 :           return result.empty() ? name : result + separator + name;
    1094           6 :         });
    1095             :   }
    1096             : 
    1097           2 :   std::string get_usage_full() const {
    1098           4 :     std::stringstream usage;
    1099             : 
    1100           2 :     usage << get_names_csv('/');
    1101           4 :     const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
    1102           2 :     if (m_num_args_range.get_max() > 0) {
    1103           2 :       usage << " " << metavar;
    1104           2 :       if (m_num_args_range.get_max() > 1) {
    1105           0 :         usage << "...";
    1106             :       }
    1107             :     }
    1108           4 :     return usage.str();
    1109             :   }
    1110             : 
    1111         499 :   std::string get_inline_usage() const {
    1112         998 :     std::stringstream usage;
    1113             :     // Find the longest variant to show in the usage string
    1114         998 :     std::string longest_name = m_names.front();
    1115        1032 :     for (const auto &s : m_names) {
    1116         533 :       if (s.size() > longest_name.size()) {
    1117          34 :         longest_name = s;
    1118             :       }
    1119             :     }
    1120         499 :     if (!m_is_required) {
    1121         499 :       usage << "[";
    1122             :     }
    1123         499 :     usage << longest_name;
    1124         998 :     const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
    1125         499 :     if (m_num_args_range.get_max() > 0) {
    1126         317 :       usage << " " << metavar;
    1127         354 :       if (m_num_args_range.get_max() > 1 &&
    1128          37 :           m_metavar.find("> <") == std::string::npos) {
    1129           0 :         usage << "...";
    1130             :       }
    1131             :     }
    1132         499 :     if (!m_is_required) {
    1133         499 :       usage << "]";
    1134             :     }
    1135         499 :     if (m_is_repeatable) {
    1136          78 :       usage << "...";
    1137             :     }
    1138         998 :     return usage.str();
    1139             :   }
    1140             : 
    1141           0 :   std::size_t get_arguments_length() const {
    1142             : 
    1143           0 :     std::size_t names_size = std::accumulate(
    1144           0 :         std::begin(m_names), std::end(m_names), std::size_t(0),
    1145           0 :         [](const auto &sum, const auto &s) { return sum + s.size(); });
    1146             : 
    1147           0 :     if (is_positional(m_names.front(), m_prefix_chars)) {
    1148             :       // A set metavar means this replaces the names
    1149           0 :       if (!m_metavar.empty()) {
    1150             :         // Indent and metavar
    1151           0 :         return 2 + m_metavar.size();
    1152             :       }
    1153             : 
    1154             :       // Indent and space-separated
    1155           0 :       return 2 + names_size + (m_names.size() - 1);
    1156             :     }
    1157             :     // Is an option - include both names _and_ metavar
    1158             :     // size = text + (", " between names)
    1159           0 :     std::size_t size = names_size + 2 * (m_names.size() - 1);
    1160           0 :     if (!m_metavar.empty() && m_num_args_range == NArgsRange{1, 1}) {
    1161           0 :       size += m_metavar.size() + 1;
    1162             :     }
    1163           0 :     return size + 2; // indent
    1164             :   }
    1165             : 
    1166           0 :   friend std::ostream &operator<<(std::ostream &stream,
    1167             :                                   const Argument &argument) {
    1168           0 :     std::stringstream name_stream;
    1169           0 :     name_stream << "  "; // indent
    1170           0 :     if (argument.is_positional(argument.m_names.front(),
    1171             :                                argument.m_prefix_chars)) {
    1172           0 :       if (!argument.m_metavar.empty()) {
    1173           0 :         name_stream << argument.m_metavar;
    1174             :       } else {
    1175           0 :         name_stream << details::join(argument.m_names.begin(),
    1176           0 :                                      argument.m_names.end(), " ");
    1177             :       }
    1178             :     } else {
    1179           0 :       name_stream << details::join(argument.m_names.begin(),
    1180           0 :                                    argument.m_names.end(), ", ");
    1181             :       // If we have a metavar, and one narg - print the metavar
    1182           0 :       if (!argument.m_metavar.empty() &&
    1183           0 :           argument.m_num_args_range == NArgsRange{1, 1}) {
    1184           0 :         name_stream << " " << argument.m_metavar;
    1185             :       }
    1186           0 :       else if (!argument.m_metavar.empty() &&
    1187           0 :                argument.m_num_args_range.get_min() == argument.m_num_args_range.get_max() &&
    1188           0 :                argument.m_metavar.find("> <") != std::string::npos) {
    1189           0 :         name_stream << " " << argument.m_metavar;
    1190             :       }
    1191             :     }
    1192             : 
    1193             :     // align multiline help message
    1194           0 :     auto stream_width = stream.width();
    1195           0 :     auto name_padding = std::string(name_stream.str().size(), ' ');
    1196           0 :     auto pos = std::string::size_type{};
    1197           0 :     auto prev = std::string::size_type{};
    1198           0 :     auto first_line = true;
    1199           0 :     auto hspace = "  "; // minimal space between name and help message
    1200           0 :     stream << name_stream.str();
    1201           0 :     std::string_view help_view(argument.m_help);
    1202           0 :     while ((pos = argument.m_help.find('\n', prev)) != std::string::npos) {
    1203           0 :       auto line = help_view.substr(prev, pos - prev + 1);
    1204           0 :       if (first_line) {
    1205           0 :         stream << hspace << line;
    1206           0 :         first_line = false;
    1207             :       } else {
    1208           0 :         stream.width(stream_width);
    1209           0 :         stream << name_padding << hspace << line;
    1210             :       }
    1211           0 :       prev += pos - prev + 1;
    1212             :     }
    1213           0 :     if (first_line) {
    1214           0 :       stream << hspace << argument.m_help;
    1215             :     } else {
    1216           0 :       auto leftover = help_view.substr(prev, argument.m_help.size() - prev);
    1217           0 :       if (!leftover.empty()) {
    1218           0 :         stream.width(stream_width);
    1219           0 :         stream << name_padding << hspace << leftover;
    1220             :       }
    1221             :     }
    1222             : 
    1223             :     // print nargs spec
    1224           0 :     if (!argument.m_help.empty()) {
    1225           0 :       stream << " ";
    1226             :     }
    1227           0 :     stream << argument.m_num_args_range;
    1228             : 
    1229           0 :     bool add_space = false;
    1230           0 :     if (argument.m_default_value.has_value() &&
    1231           0 :         argument.m_num_args_range != NArgsRange{0, 0}) {
    1232           0 :       stream << "[default: " << argument.m_default_value_repr << "]";
    1233           0 :       add_space = true;
    1234           0 :     } else if (argument.m_is_required) {
    1235           0 :       stream << "[required]";
    1236           0 :       add_space = true;
    1237             :     }
    1238           0 :     if (argument.m_is_repeatable) {
    1239           0 :       if (add_space) {
    1240           0 :         stream << " ";
    1241             :       }
    1242           0 :       stream << "[may be repeated]";
    1243             :     }
    1244           0 :     stream << "\n";
    1245           0 :     return stream;
    1246             :   }
    1247             : 
    1248             :   template <typename T> bool operator!=(const T &rhs) const {
    1249             :     return !(*this == rhs);
    1250             :   }
    1251             : 
    1252             :   /*
    1253             :    * Compare to an argument value of known type
    1254             :    * @throws std::logic_error in case of incompatible types
    1255             :    */
    1256             :   template <typename T> bool operator==(const T &rhs) const {
    1257             :     if constexpr (!details::IsContainer<T>) {
    1258             :       return get<T>() == rhs;
    1259             :     } else {
    1260             :       using ValueType = typename T::value_type;
    1261             :       auto lhs = get<T>();
    1262             :       return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
    1263             :                         std::end(rhs), [](const auto &a, const auto &b) {
    1264             :                           return std::any_cast<const ValueType &>(a) == b;
    1265             :                         });
    1266             :     }
    1267             :   }
    1268             : 
    1269             :   /*
    1270             :    * positional:
    1271             :    *    _empty_
    1272             :    *    '-'
    1273             :    *    '-' decimal-literal
    1274             :    *    !'-' anything
    1275             :    */
    1276      286062 :   static bool is_positional(std::string_view name,
    1277             :                             std::string_view prefix_chars) {
    1278      286062 :     auto first = lookahead(name);
    1279             : 
    1280      286062 :     if (first == eof) {
    1281           3 :       return true;
    1282             :     }
    1283      286059 :     if (prefix_chars.find(static_cast<char>(first)) !=
    1284             :                           std::string_view::npos) {
    1285      249054 :       name.remove_prefix(1);
    1286      249054 :       if (name.empty()) {
    1287           0 :         return true;
    1288             :       }
    1289      249054 :       return is_decimal_literal(name);
    1290             :     }
    1291       37005 :     return true;
    1292             :   }
    1293             : 
    1294             : private:
    1295             :   class NArgsRange {
    1296             :     std::size_t m_min;
    1297             :     std::size_t m_max;
    1298             : 
    1299             :   public:
    1300      430832 :     NArgsRange(std::size_t minimum, std::size_t maximum)
    1301      430832 :         : m_min(minimum), m_max(maximum) {
    1302      430832 :       if (minimum > maximum) {
    1303           0 :         throw std::logic_error("Range of number of arguments is invalid");
    1304             :       }
    1305      430832 :     }
    1306             : 
    1307        2281 :     bool contains(std::size_t value) const {
    1308        2281 :       return value >= m_min && value <= m_max;
    1309             :     }
    1310             : 
    1311           5 :     bool is_exact() const { return m_min == m_max; }
    1312             : 
    1313          25 :     bool is_right_bounded() const {
    1314          25 :       return m_max < (std::numeric_limits<std::size_t>::max)();
    1315             :     }
    1316             : 
    1317       25144 :     std::size_t get_min() const { return m_min; }
    1318             : 
    1319      115407 :     std::size_t get_max() const { return m_max; }
    1320             : 
    1321             :     // Print help message
    1322           0 :     friend auto operator<<(std::ostream &stream, const NArgsRange &range)
    1323             :         -> std::ostream & {
    1324           0 :       if (range.m_min == range.m_max) {
    1325           0 :         if (range.m_min != 0 && range.m_min != 1) {
    1326           0 :           stream << "[nargs: " << range.m_min << "] ";
    1327             :         }
    1328             :       } else {
    1329           0 :         if (range.m_max == (std::numeric_limits<std::size_t>::max)()) {
    1330           0 :           stream << "[nargs: " << range.m_min << " or more] ";
    1331             :         } else {
    1332           0 :           stream << "[nargs=" << range.m_min << ".." << range.m_max << "] ";
    1333             :         }
    1334             :       }
    1335           0 :       return stream;
    1336             :     }
    1337             : 
    1338           0 :     bool operator==(const NArgsRange &rhs) const {
    1339           0 :       return rhs.m_min == m_min && rhs.m_max == m_max;
    1340             :     }
    1341             : 
    1342           0 :     bool operator!=(const NArgsRange &rhs) const { return !(*this == rhs); }
    1343             :   };
    1344             : 
    1345           5 :   void throw_nargs_range_validation_error() const {
    1346          10 :     std::stringstream stream;
    1347           5 :     if (!m_used_name.empty()) {
    1348           0 :       stream << m_used_name << ": ";
    1349             :     } else {
    1350           5 :       stream << m_names.front() << ": ";
    1351             :     }
    1352           5 :     if (m_num_args_range.is_exact()) {
    1353           5 :       stream << m_num_args_range.get_min();
    1354           0 :     } else if (m_num_args_range.is_right_bounded()) {
    1355           0 :       stream << m_num_args_range.get_min() << " to "
    1356           0 :              << m_num_args_range.get_max();
    1357             :     } else {
    1358           0 :       stream << m_num_args_range.get_min() << " or more";
    1359             :     }
    1360           5 :     stream << " argument(s) expected. " << m_values.size() << " provided.";
    1361           5 :     throw std::runtime_error(stream.str());
    1362             :   }
    1363             : 
    1364           0 :   void throw_required_arg_not_used_error() const {
    1365           0 :     std::stringstream stream;
    1366           0 :     stream << m_names.front() << ": required.";
    1367           0 :     throw std::runtime_error(stream.str());
    1368             :   }
    1369             : 
    1370           0 :   void throw_required_arg_no_value_provided_error() const {
    1371           0 :     std::stringstream stream;
    1372           0 :     stream << m_used_name << ": no value provided.";
    1373           0 :     throw std::runtime_error(stream.str());
    1374             :   }
    1375             : 
    1376             :   static constexpr int eof = std::char_traits<char>::eof();
    1377             : 
    1378      537874 :   static auto lookahead(std::string_view s) -> int {
    1379      537874 :     if (s.empty()) {
    1380         671 :       return eof;
    1381             :     }
    1382      537203 :     return static_cast<int>(static_cast<unsigned char>(s[0]));
    1383             :   }
    1384             : 
    1385             :   /*
    1386             :    * decimal-literal:
    1387             :    *    '0'
    1388             :    *    nonzero-digit digit-sequence_opt
    1389             :    *    integer-part fractional-part
    1390             :    *    fractional-part
    1391             :    *    integer-part '.' exponent-part_opt
    1392             :    *    integer-part exponent-part
    1393             :    *
    1394             :    * integer-part:
    1395             :    *    digit-sequence
    1396             :    *
    1397             :    * fractional-part:
    1398             :    *    '.' post-decimal-point
    1399             :    *
    1400             :    * post-decimal-point:
    1401             :    *    digit-sequence exponent-part_opt
    1402             :    *
    1403             :    * exponent-part:
    1404             :    *    'e' post-e
    1405             :    *    'E' post-e
    1406             :    *
    1407             :    * post-e:
    1408             :    *    sign_opt digit-sequence
    1409             :    *
    1410             :    * sign: one of
    1411             :    *    '+' '-'
    1412             :    */
    1413      249054 :   static bool is_decimal_literal(std::string_view s) {
    1414      249054 :     if (s == "inf") {
    1415           2 :       return true;
    1416             :     }
    1417             : 
    1418       13079 :     auto is_digit = [](auto c) constexpr {
    1419       13079 :       switch (c) {
    1420       12339 :       case '0':
    1421             :       case '1':
    1422             :       case '2':
    1423             :       case '3':
    1424             :       case '4':
    1425             :       case '5':
    1426             :       case '6':
    1427             :       case '7':
    1428             :       case '8':
    1429             :       case '9':
    1430       12339 :         return true;
    1431         740 :       default:
    1432         740 :         return false;
    1433             :       }
    1434             :     };
    1435             : 
    1436             :     // precondition: we have consumed or will consume at least one digit
    1437        1619 :     auto consume_digits = [=](std::string_view sd) {
    1438             :       // NOLINTNEXTLINE(readability-qualified-auto)
    1439        1619 :       auto it = std::find_if_not(std::begin(sd), std::end(sd), is_digit);
    1440        1619 :       return sd.substr(static_cast<std::size_t>(it - std::begin(sd)));
    1441             :     };
    1442             : 
    1443      249052 :     switch (lookahead(s)) {
    1444          48 :     case '0': {
    1445          48 :       s.remove_prefix(1);
    1446          48 :       if (s.empty()) {
    1447           0 :         return true;
    1448             :       }
    1449          48 :       goto integer_part;
    1450             :     }
    1451         895 :     case '1':
    1452             :     case '2':
    1453             :     case '3':
    1454             :     case '4':
    1455             :     case '5':
    1456             :     case '6':
    1457             :     case '7':
    1458             :     case '8':
    1459             :     case '9': {
    1460         895 :       s = consume_digits(s);
    1461         895 :       if (s.empty()) {
    1462         207 :         return true;
    1463             :       }
    1464         688 :       goto integer_part_consumed;
    1465             :     }
    1466           0 :     case '.': {
    1467           0 :       s.remove_prefix(1);
    1468           0 :       goto post_decimal_point;
    1469             :     }
    1470      248109 :     default:
    1471      248109 :       return false;
    1472             :     }
    1473             : 
    1474          48 :   integer_part:
    1475          48 :     s = consume_digits(s);
    1476         736 :   integer_part_consumed:
    1477         736 :     switch (lookahead(s)) {
    1478         672 :     case '.': {
    1479         672 :       s.remove_prefix(1);
    1480         672 :       if (is_digit(lookahead(s))) {
    1481         672 :         goto post_decimal_point;
    1482             :       } else {
    1483           0 :         goto exponent_part_opt;
    1484             :       }
    1485             :     }
    1486           0 :     case 'e':
    1487             :     case 'E': {
    1488           0 :       s.remove_prefix(1);
    1489           0 :       goto post_e;
    1490             :     }
    1491          64 :     default:
    1492          64 :       return false;
    1493             :     }
    1494             : 
    1495         672 :   post_decimal_point:
    1496         672 :     if (is_digit(lookahead(s))) {
    1497         672 :       s = consume_digits(s);
    1498         672 :       goto exponent_part_opt;
    1499             :     }
    1500           0 :     return false;
    1501             : 
    1502         672 :   exponent_part_opt:
    1503         672 :     switch (lookahead(s)) {
    1504         668 :     case eof:
    1505         668 :       return true;
    1506           4 :     case 'e':
    1507             :     case 'E': {
    1508           4 :       s.remove_prefix(1);
    1509           4 :       goto post_e;
    1510             :     }
    1511           0 :     default:
    1512           0 :       return false;
    1513             :     }
    1514             : 
    1515           4 :   post_e:
    1516           4 :     switch (lookahead(s)) {
    1517           4 :     case '-':
    1518             :     case '+':
    1519           4 :       s.remove_prefix(1);
    1520             :     }
    1521           4 :     if (is_digit(lookahead(s))) {
    1522           4 :       s = consume_digits(s);
    1523           4 :       return s.empty();
    1524             :     }
    1525           0 :     return false;
    1526             :   }
    1527             : 
    1528      260765 :   static bool is_optional(std::string_view name,
    1529             :                           std::string_view prefix_chars) {
    1530      260765 :     return !is_positional(name, prefix_chars);
    1531             :   }
    1532             : 
    1533             :   /*
    1534             :    * Get argument value given a type
    1535             :    * @throws std::logic_error in case of incompatible types
    1536             :    */
    1537           4 :   template <typename T> T get() const {
    1538           4 :     if (!m_values.empty()) {
    1539             :       if constexpr (details::IsContainer<T>) {
    1540             :         return any_cast_container<T>(m_values);
    1541             :       } else {
    1542           4 :         return std::any_cast<T>(m_values.front());
    1543             :       }
    1544             :     }
    1545           0 :     if (m_default_value.has_value()) {
    1546           0 :       return std::any_cast<T>(m_default_value);
    1547             :     }
    1548             :     if constexpr (details::IsContainer<T>) {
    1549             :       if (!m_accepts_optional_like_value) {
    1550             :         return any_cast_container<T>(m_values);
    1551             :       }
    1552             :     }
    1553             : 
    1554           0 :     throw std::logic_error("No value provided for '" + m_names.back() + "'.");
    1555             :   }
    1556             : 
    1557             :   /*
    1558             :    * Get argument value given a type.
    1559             :    * @pre The object has no default value.
    1560             :    * @returns The stored value if any, std::nullopt otherwise.
    1561             :    */
    1562       19134 :   template <typename T> auto present() const -> std::optional<T> {
    1563       19134 :     if (m_default_value.has_value()) {
    1564           0 :       throw std::logic_error("Argument with default value always presents");
    1565             :     }
    1566       19134 :     if (m_values.empty()) {
    1567       17204 :       return std::nullopt;
    1568             :     }
    1569             :     if constexpr (details::IsContainer<T>) {
    1570        1930 :       return any_cast_container<T>(m_values);
    1571             :     }
    1572             :     return std::any_cast<T>(m_values.front());
    1573             :   }
    1574             : 
    1575             :   template <typename T>
    1576        1930 :   static auto any_cast_container(const std::vector<std::any> &operand) -> T {
    1577             :     using ValueType = typename T::value_type;
    1578             : 
    1579        1930 :     T result;
    1580        1930 :     std::transform(
    1581             :         std::begin(operand), std::end(operand), std::back_inserter(result),
    1582        6221 :         [](const auto &value) { return std::any_cast<ValueType>(value); });
    1583        1930 :     return result;
    1584             :   }
    1585             : 
    1586      265220 :   void set_usage_newline_counter(int i) { m_usage_newline_counter = i; }
    1587             : 
    1588      265220 :   void set_group_idx(std::size_t i) { m_group_idx = i; }
    1589             : 
    1590             :   std::vector<std::string> m_names;
    1591             :   std::string_view m_used_name;
    1592             :   std::string m_help;
    1593             :   std::string m_metavar;
    1594             :   std::any m_default_value;
    1595             :   std::string m_default_value_repr;
    1596             :   std::optional<std::string>
    1597             :       m_default_value_str; // used for checking default_value against choices
    1598             :   std::any m_implicit_value;
    1599             :   std::optional<std::vector<std::string>> m_choices{std::nullopt};
    1600             :   using valued_action = std::function<std::any(const std::string &)>;
    1601             :   using void_action = std::function<void(const std::string &)>;
    1602             :   std::vector<std::variant<valued_action, void_action>> m_actions;
    1603             :   std::variant<valued_action, void_action> m_default_action{
    1604             :     std::in_place_type<valued_action>,
    1605         498 :     [](const std::string &value) { return value; }};
    1606             :   std::vector<std::any> m_values;
    1607             :   NArgsRange m_num_args_range{1, 1};
    1608             :   // Bit field of bool values. Set default value in ctor.
    1609             :   bool m_accepts_optional_like_value : 1;
    1610             :   bool m_is_optional : 1;
    1611             :   bool m_is_required : 1;
    1612             :   bool m_is_repeatable : 1;
    1613             :   bool m_is_used : 1;
    1614             :   bool m_is_hidden : 1;            // if set, does not appear in usage or help
    1615             :   std::string_view m_prefix_chars; // ArgumentParser has the prefix_chars
    1616             :   int m_usage_newline_counter = 0;
    1617             :   std::size_t m_group_idx = 0;
    1618             : };
    1619             : 
    1620             : class ArgumentParser {
    1621             : public:
    1622        5422 :   explicit ArgumentParser(std::string program_name = {},
    1623             :                           std::string version = "1.0",
    1624             :                           default_arguments add_args = default_arguments::all,
    1625             :                           bool exit_on_default_arguments = true,
    1626             :                           std::ostream &os = std::cout)
    1627       10844 :       : m_program_name(std::move(program_name)), m_version(std::move(version)),
    1628             :         m_exit_on_default_arguments(exit_on_default_arguments),
    1629        5422 :         m_parser_path(m_program_name) {
    1630        5422 :     if ((add_args & default_arguments::help) == default_arguments::help) {
    1631           0 :       add_argument("-h", "--help")
    1632           0 :           .action([&](const auto & /*unused*/) {
    1633           0 :             os << help().str();
    1634           0 :             if (m_exit_on_default_arguments) {
    1635           0 :               std::exit(0);
    1636             :             }
    1637           0 :           })
    1638           0 :           .default_value(false)
    1639           0 :           .help("shows help message and exits")
    1640           0 :           .implicit_value(true)
    1641           0 :           .nargs(0);
    1642             :     }
    1643        5422 :     if ((add_args & default_arguments::version) == default_arguments::version) {
    1644           0 :       add_argument("-v", "--version")
    1645           0 :           .action([&](const auto & /*unused*/) {
    1646           0 :             os << m_version << std::endl;
    1647           0 :             if (m_exit_on_default_arguments) {
    1648           0 :               std::exit(0);
    1649             :             }
    1650           0 :           })
    1651           0 :           .default_value(false)
    1652           0 :           .help("prints version information and exits")
    1653           0 :           .implicit_value(true)
    1654           0 :           .nargs(0);
    1655             :     }
    1656        5422 :   }
    1657             : 
    1658        5263 :   ~ArgumentParser() = default;
    1659             : 
    1660             :   // ArgumentParser is meant to be used in a single function.
    1661             :   // Setup everything and parse arguments in one place.
    1662             :   //
    1663             :   // ArgumentParser internally uses std::string_views,
    1664             :   // references, iterators, etc.
    1665             :   // Many of these elements become invalidated after a copy or move.
    1666             :   ArgumentParser(const ArgumentParser &other) = delete;
    1667             :   ArgumentParser &operator=(const ArgumentParser &other) = delete;
    1668             :   ArgumentParser(ArgumentParser &&) noexcept = delete;
    1669             :   ArgumentParser &operator=(ArgumentParser &&) = delete;
    1670             : 
    1671             :   explicit operator bool() const {
    1672             :     auto arg_used = std::any_of(m_argument_map.cbegin(), m_argument_map.cend(),
    1673             :                                 [](auto &it) { return it.second->m_is_used; });
    1674             :     auto subparser_used =
    1675             :         std::any_of(m_subparser_used.cbegin(), m_subparser_used.cend(),
    1676             :                     [](auto &it) { return it.second; });
    1677             : 
    1678             :     return m_is_parsed && (arg_used || subparser_used);
    1679             :   }
    1680             : 
    1681             :   // Parameter packing
    1682             :   // Call add_argument with variadic number of string arguments
    1683      230132 :   template <typename... Targs> Argument &add_argument(Targs... f_args) {
    1684             :     using array_of_sv = std::array<std::string_view, sizeof...(Targs)>;
    1685      460264 :     auto argument =
    1686      230132 :         m_optional_arguments.emplace(std::cend(m_optional_arguments),
    1687      230132 :                                      m_prefix_chars, array_of_sv{f_args...});
    1688             : 
    1689      230132 :     if (!argument->m_is_optional) {
    1690        5560 :       m_positional_arguments.splice(std::cend(m_positional_arguments),
    1691        2780 :                                     m_optional_arguments, argument);
    1692             :     }
    1693      230132 :     argument->set_usage_newline_counter(m_usage_newline_counter);
    1694      230132 :     argument->set_group_idx(m_group_names.size());
    1695             : 
    1696      230132 :     index_argument(argument);
    1697      460264 :     return *argument;
    1698             :   }
    1699             : 
    1700             :   class MutuallyExclusiveGroup {
    1701             :     friend class ArgumentParser;
    1702             : 
    1703             :   public:
    1704             :     MutuallyExclusiveGroup() = delete;
    1705             : 
    1706       15528 :     explicit MutuallyExclusiveGroup(ArgumentParser &parent,
    1707             :                                     bool required = false)
    1708       15528 :         : m_parent(parent), m_required(required), m_elements({}) {}
    1709             : 
    1710             :     MutuallyExclusiveGroup(const MutuallyExclusiveGroup &other) = delete;
    1711             :     MutuallyExclusiveGroup &
    1712             :     operator=(const MutuallyExclusiveGroup &other) = delete;
    1713             : 
    1714       11738 :     MutuallyExclusiveGroup(MutuallyExclusiveGroup &&other) noexcept
    1715       11738 :         : m_parent(other.m_parent), m_required(other.m_required),
    1716       11738 :           m_elements(std::move(other.m_elements)) {
    1717       11738 :       other.m_elements.clear();
    1718       11738 :     }
    1719             : 
    1720       35088 :     template <typename... Targs> Argument &add_argument(Targs... f_args) {
    1721       35088 :       auto &argument = m_parent.add_argument(std::forward<Targs>(f_args)...);
    1722       35088 :       m_elements.push_back(&argument);
    1723       35088 :       argument.set_usage_newline_counter(m_parent.m_usage_newline_counter);
    1724       35088 :       argument.set_group_idx(m_parent.m_group_names.size());
    1725       35088 :       return argument;
    1726             :     }
    1727             : 
    1728             :   private:
    1729             :     ArgumentParser &m_parent;
    1730             :     bool m_required{false};
    1731             :     std::vector<Argument *> m_elements{};
    1732             :   };
    1733             : 
    1734       15528 :   MutuallyExclusiveGroup &add_mutually_exclusive_group(bool required = false) {
    1735       15528 :     m_mutually_exclusive_groups.emplace_back(*this, required);
    1736       15528 :     return m_mutually_exclusive_groups.back();
    1737             :   }
    1738             : 
    1739             :   // Parameter packed add_parents method
    1740             :   // Accepts a variadic number of ArgumentParser objects
    1741             :   template <typename... Targs>
    1742             :   ArgumentParser &add_parents(const Targs &... f_args) {
    1743             :     for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) {
    1744             :       for (const auto &argument : parent_parser.m_positional_arguments) {
    1745             :         auto it = m_positional_arguments.insert(
    1746             :             std::cend(m_positional_arguments), argument);
    1747             :         index_argument(it);
    1748             :       }
    1749             :       for (const auto &argument : parent_parser.m_optional_arguments) {
    1750             :         auto it = m_optional_arguments.insert(std::cend(m_optional_arguments),
    1751             :                                               argument);
    1752             :         index_argument(it);
    1753             :       }
    1754             :     }
    1755             :     return *this;
    1756             :   }
    1757             : 
    1758             :   // Ask for the next optional arguments to be displayed on a separate
    1759             :   // line in usage() output. Only effective if set_usage_max_line_width() is
    1760             :   // also used.
    1761        8227 :   ArgumentParser &add_usage_newline() {
    1762        8227 :     ++m_usage_newline_counter;
    1763        8227 :     return *this;
    1764             :   }
    1765             : 
    1766             :   // Ask for the next optional arguments to be displayed in a separate section
    1767             :   // in usage() and help (<< *this) output.
    1768             :   // For usage(), this is only effective if set_usage_max_line_width() is
    1769             :   // also used.
    1770        3148 :   ArgumentParser &add_group(std::string group_name) {
    1771        3148 :     m_group_names.emplace_back(std::move(group_name));
    1772        3148 :     return *this;
    1773             :   }
    1774             : 
    1775        5422 :   ArgumentParser &add_description(std::string description) {
    1776        5422 :     m_description = std::move(description);
    1777        5422 :     return *this;
    1778             :   }
    1779             : 
    1780        4819 :   ArgumentParser &add_epilog(std::string epilog) {
    1781        4819 :     m_epilog = std::move(epilog);
    1782        4819 :     return *this;
    1783             :   }
    1784             : 
    1785             :   // Add a un-documented/hidden alias for an argument.
    1786             :   // Ideally we'd want this to be a method of Argument, but Argument
    1787             :   // does not own its owing ArgumentParser.
    1788        6965 :   ArgumentParser &add_hidden_alias_for(Argument &arg, std::string_view alias) {
    1789       80548 :     for (auto it = m_optional_arguments.begin();
    1790      154131 :          it != m_optional_arguments.end(); ++it) {
    1791       80548 :       if (&(*it) == &arg) {
    1792        6965 :         m_argument_map.insert_or_assign(std::string(alias), it);
    1793        6965 :         return *this;
    1794             :       }
    1795             :     }
    1796             :     throw std::logic_error(
    1797           0 :         "Argument is not an optional argument of this parser");
    1798             :   }
    1799             : 
    1800             :   /* Getter for arguments and subparsers.
    1801             :    * @throws std::logic_error in case of an invalid argument or subparser name
    1802             :    */
    1803             :   template <typename T = Argument> T &at(std::string_view name) {
    1804             :     if constexpr (std::is_same_v<T, Argument>) {
    1805             :       return (*this)[name];
    1806             :     } else {
    1807             :       std::string str_name(name);
    1808             :       auto subparser_it = m_subparser_map.find(str_name);
    1809             :       if (subparser_it != m_subparser_map.end()) {
    1810             :         return subparser_it->second->get();
    1811             :       }
    1812             :       throw std::logic_error("No such subparser: " + str_name);
    1813             :     }
    1814             :   }
    1815             : 
    1816             :   ArgumentParser &set_prefix_chars(std::string prefix_chars) {
    1817             :     m_prefix_chars = std::move(prefix_chars);
    1818             :     return *this;
    1819             :   }
    1820             : 
    1821             :   ArgumentParser &set_assign_chars(std::string assign_chars) {
    1822             :     m_assign_chars = std::move(assign_chars);
    1823             :     return *this;
    1824             :   }
    1825             : 
    1826             :   /* Call parse_args_internal - which does all the work
    1827             :    * Then, validate the parsed arguments
    1828             :    * This variant is used mainly for testing
    1829             :    * @throws std::runtime_error in case of any invalid argument
    1830             :    */
    1831        4808 :   void parse_args(const std::vector<std::string> &arguments) {
    1832        4808 :     parse_args_internal(arguments);
    1833             :     // Check if all arguments are parsed
    1834      240181 :     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
    1835      235443 :       argument->validate();
    1836             :     }
    1837             : 
    1838             :     // Check each mutually exclusive group and make sure
    1839             :     // there are no constraint violations
    1840       20048 :     for (const auto &group : m_mutually_exclusive_groups) {
    1841       15311 :       auto mutex_argument_used{false};
    1842       15311 :       Argument *mutex_argument_it{nullptr};
    1843       49882 :       for (Argument *arg : group.m_elements) {
    1844       34572 :         if (!mutex_argument_used && arg->m_is_used) {
    1845         518 :           mutex_argument_used = true;
    1846         518 :           mutex_argument_it = arg;
    1847       34054 :         } else if (mutex_argument_used && arg->m_is_used) {
    1848             :           // Violation
    1849           2 :           throw std::runtime_error("Argument '" + arg->get_usage_full() +
    1850           2 :                                    "' not allowed with '" +
    1851           3 :                                    mutex_argument_it->get_usage_full() + "'");
    1852             :         }
    1853             :       }
    1854             : 
    1855       15310 :       if (!mutex_argument_used && group.m_required) {
    1856             :         // at least one argument from the group is
    1857             :         // required
    1858           0 :         std::string argument_names{};
    1859           0 :         std::size_t i = 0;
    1860           0 :         std::size_t size = group.m_elements.size();
    1861           0 :         for (Argument *arg : group.m_elements) {
    1862           0 :           if (i + 1 == size) {
    1863             :             // last
    1864           0 :             argument_names += "'" + arg->get_usage_full() + "' ";
    1865             :           } else {
    1866           0 :             argument_names += "'" + arg->get_usage_full() + "' or ";
    1867             :           }
    1868           0 :           i += 1;
    1869             :         }
    1870           0 :         throw std::runtime_error("One of the arguments " + argument_names +
    1871           0 :                                  "is required");
    1872             :       }
    1873             :     }
    1874        4737 :   }
    1875             : 
    1876             :   /* Call parse_known_args_internal - which does all the work
    1877             :    * Then, validate the parsed arguments
    1878             :    * This variant is used mainly for testing
    1879             :    * @throws std::runtime_error in case of any invalid argument
    1880             :    */
    1881             :   std::vector<std::string>
    1882             :   parse_known_args(const std::vector<std::string> &arguments) {
    1883             :     auto unknown_arguments = parse_known_args_internal(arguments);
    1884             :     // Check if all arguments are parsed
    1885             :     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
    1886             :       argument->validate();
    1887             :     }
    1888             :     return unknown_arguments;
    1889             :   }
    1890             : 
    1891             :   /* Main entry point for parsing command-line arguments using this
    1892             :    * ArgumentParser
    1893             :    * @throws std::runtime_error in case of any invalid argument
    1894             :    */
    1895             :   // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
    1896             :   void parse_args(int argc, const char *const argv[]) {
    1897             :     parse_args({argv, argv + argc});
    1898             :   }
    1899             : 
    1900             :   /* Main entry point for parsing command-line arguments using this
    1901             :    * ArgumentParser
    1902             :    * @throws std::runtime_error in case of any invalid argument
    1903             :    */
    1904             :   // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
    1905             :   auto parse_known_args(int argc, const char *const argv[]) {
    1906             :     return parse_known_args({argv, argv + argc});
    1907             :   }
    1908             : 
    1909             :   /* Getter for options with default values.
    1910             :    * @throws std::logic_error if parse_args() has not been previously called
    1911             :    * @throws std::logic_error if there is no such option
    1912             :    * @throws std::logic_error if the option has no value
    1913             :    * @throws std::bad_any_cast if the option is not of type T
    1914             :    */
    1915           4 :   template <typename T = std::string> T get(std::string_view arg_name) const {
    1916           4 :     if (!m_is_parsed) {
    1917           0 :       throw std::logic_error("Nothing parsed, no arguments are available.");
    1918             :     }
    1919           4 :     return (*this)[arg_name].get<T>();
    1920             :   }
    1921             : 
    1922             :   /* Getter for options without default values.
    1923             :    * @pre The option has no default value.
    1924             :    * @throws std::logic_error if there is no such option
    1925             :    * @throws std::bad_any_cast if the option is not of type T
    1926             :    */
    1927             :   template <typename T = std::string>
    1928       19134 :   auto present(std::string_view arg_name) const -> std::optional<T> {
    1929       19134 :     return (*this)[arg_name].present<T>();
    1930             :   }
    1931             : 
    1932             :   /* Getter that returns true for user-supplied options. Returns false if not
    1933             :    * user-supplied, even with a default value.
    1934             :    */
    1935        4632 :   auto is_used(std::string_view arg_name) const {
    1936        4632 :     return (*this)[arg_name].m_is_used;
    1937             :   }
    1938             : 
    1939             :   /* Getter that returns true if a subcommand is used.
    1940             :    */
    1941          30 :   auto is_subcommand_used(std::string_view subcommand_name) const {
    1942          30 :     return m_subparser_used.at(std::string(subcommand_name));
    1943             :   }
    1944             : 
    1945             :   /* Getter that returns true if a subcommand is used.
    1946             :    */
    1947             :   auto is_subcommand_used(const ArgumentParser &subparser) const {
    1948             :     return is_subcommand_used(subparser.m_program_name);
    1949             :   }
    1950             : 
    1951             :   /* Indexing operator. Return a reference to an Argument object
    1952             :    * Used in conjunction with Argument.operator== e.g., parser["foo"] == true
    1953             :    * @throws std::logic_error in case of an invalid argument name
    1954             :    */
    1955       23770 :   Argument &operator[](std::string_view arg_name) const {
    1956       47540 :     std::string name(arg_name);
    1957       23770 :     auto it = m_argument_map.find(name);
    1958       23770 :     if (it != m_argument_map.end()) {
    1959       23750 :       return *(it->second);
    1960             :     }
    1961          20 :     if (!is_valid_prefix_char(arg_name.front())) {
    1962           0 :       const auto legal_prefix_char = get_any_valid_prefix_char();
    1963           0 :       const auto prefix = std::string(1, legal_prefix_char);
    1964             : 
    1965             :       // "-" + arg_name
    1966           0 :       name = prefix + name;
    1967           0 :       it = m_argument_map.find(name);
    1968           0 :       if (it != m_argument_map.end()) {
    1969           0 :         return *(it->second);
    1970             :       }
    1971             :       // "--" + arg_name
    1972           0 :       name = prefix + name;
    1973           0 :       it = m_argument_map.find(name);
    1974           0 :       if (it != m_argument_map.end()) {
    1975           0 :         return *(it->second);
    1976             :       }
    1977             :     }
    1978          20 :     throw std::logic_error("No such argument: " + std::string(arg_name));
    1979             :   }
    1980             : 
    1981             :   // Print help message
    1982           0 :   friend auto operator<<(std::ostream &stream, const ArgumentParser &parser)
    1983             :       -> std::ostream & {
    1984           0 :     stream.setf(std::ios_base::left);
    1985             : 
    1986           0 :     auto longest_arg_length = parser.get_length_of_longest_argument();
    1987             : 
    1988           0 :     stream << parser.usage() << "\n\n";
    1989             : 
    1990           0 :     if (!parser.m_description.empty()) {
    1991           0 :       stream << parser.m_description << "\n\n";
    1992             :     }
    1993             : 
    1994           0 :     const bool has_visible_positional_args = std::find_if(
    1995             :       parser.m_positional_arguments.begin(),
    1996             :       parser.m_positional_arguments.end(),
    1997           0 :       [](const auto &argument) {
    1998           0 :       return !argument.m_is_hidden; }) !=
    1999           0 :       parser.m_positional_arguments.end();
    2000           0 :     if (has_visible_positional_args) {
    2001           0 :       stream << "Positional arguments:\n";
    2002             :     }
    2003             : 
    2004           0 :     for (const auto &argument : parser.m_positional_arguments) {
    2005           0 :       if (!argument.m_is_hidden) {
    2006           0 :         stream.width(static_cast<std::streamsize>(longest_arg_length));
    2007           0 :         stream << argument;
    2008             :       }
    2009             :     }
    2010             : 
    2011           0 :     if (!parser.m_optional_arguments.empty()) {
    2012             :       stream << (!has_visible_positional_args ? "" : "\n")
    2013           0 :              << "Optional arguments:\n";
    2014             :     }
    2015             : 
    2016           0 :     for (const auto &argument : parser.m_optional_arguments) {
    2017           0 :       if (argument.m_group_idx == 0 && !argument.m_is_hidden) {
    2018           0 :         stream.width(static_cast<std::streamsize>(longest_arg_length));
    2019           0 :         stream << argument;
    2020             :       }
    2021             :     }
    2022             : 
    2023           0 :     for (size_t i_group = 0; i_group < parser.m_group_names.size(); ++i_group) {
    2024           0 :       stream << "\n" << parser.m_group_names[i_group] << " (detailed usage):\n";
    2025           0 :       for (const auto &argument : parser.m_optional_arguments) {
    2026           0 :         if (argument.m_group_idx == i_group + 1 && !argument.m_is_hidden) {
    2027           0 :           stream.width(static_cast<std::streamsize>(longest_arg_length));
    2028           0 :           stream << argument;
    2029             :         }
    2030             :       }
    2031             :     }
    2032             : 
    2033           0 :     bool has_visible_subcommands = std::any_of(
    2034             :         parser.m_subparser_map.begin(), parser.m_subparser_map.end(),
    2035           0 :         [](auto &p) { return !p.second->get().m_suppress; });
    2036             : 
    2037           0 :     if (has_visible_subcommands) {
    2038           0 :       stream << (parser.m_positional_arguments.empty()
    2039           0 :                      ? (parser.m_optional_arguments.empty() ? "" : "\n")
    2040             :                      : "\n")
    2041           0 :              << "Subcommands:\n";
    2042           0 :       for (const auto &[command, subparser] : parser.m_subparser_map) {
    2043           0 :         if (subparser->get().m_suppress) {
    2044           0 :           continue;
    2045             :         }
    2046             : 
    2047           0 :         stream << std::setw(2) << " ";
    2048           0 :         if (longest_arg_length >= 2) {
    2049           0 :           stream << std::setw(static_cast<int>(longest_arg_length - 2))
    2050           0 :                  << command;
    2051             :         }
    2052           0 :         stream << " " << subparser->get().m_description << "\n";
    2053             :       }
    2054             :     }
    2055             : 
    2056           0 :     if (!parser.m_epilog.empty()) {
    2057           0 :       stream << '\n';
    2058           0 :       stream << parser.m_epilog << "\n\n";
    2059             :     }
    2060             : 
    2061           0 :     return stream;
    2062             :   }
    2063             : 
    2064             :   // Format help message
    2065           0 :   auto help() const -> std::stringstream {
    2066           0 :     std::stringstream out;
    2067           0 :     out << *this;
    2068           0 :     return out;
    2069             :   }
    2070             : 
    2071             :   // Sets the maximum width for a line of the Usage message
    2072        5422 :   ArgumentParser &set_usage_max_line_width(size_t w) {
    2073        5422 :     this->m_usage_max_line_width = w;
    2074        5422 :     return *this;
    2075             :   }
    2076             : 
    2077             :   // Asks to display arguments of mutually exclusive group on separate lines in
    2078             :   // the Usage message
    2079        5422 :   ArgumentParser &set_usage_break_on_mutex() {
    2080        5422 :     this->m_usage_break_on_mutex = true;
    2081        5422 :     return *this;
    2082             :   }
    2083             : 
    2084             :   // Format usage part of help only
    2085          16 :   auto usage() const -> std::string {
    2086          32 :     std::stringstream stream;
    2087             : 
    2088          32 :     std::string curline("Usage: ");
    2089          16 :     curline += this->m_parser_path;
    2090             :     const bool multiline_usage =
    2091          16 :         this->m_usage_max_line_width < std::numeric_limits<std::size_t>::max();
    2092          16 :     const size_t indent_size = curline.size();
    2093             : 
    2094          21 :     const auto deal_with_options_of_group = [&](std::size_t group_idx) {
    2095          21 :       bool found_options = false;
    2096             :       // Add any options inline here
    2097          21 :       const MutuallyExclusiveGroup *cur_mutex = nullptr;
    2098          21 :       int usage_newline_counter = -1;
    2099         922 :       for (const auto &argument : this->m_optional_arguments) {
    2100         901 :         if (argument.m_is_hidden) {
    2101         402 :           continue;
    2102             :         }
    2103         839 :         if (multiline_usage) {
    2104         839 :           if (argument.m_group_idx != group_idx) {
    2105         340 :             continue;
    2106             :           }
    2107         499 :           if (usage_newline_counter != argument.m_usage_newline_counter) {
    2108          35 :             if (usage_newline_counter >= 0) {
    2109          14 :               if (curline.size() > indent_size) {
    2110          14 :                 stream << curline << std::endl;
    2111          14 :                 curline = std::string(indent_size, ' ');
    2112             :               }
    2113             :             }
    2114          35 :             usage_newline_counter = argument.m_usage_newline_counter;
    2115             :           }
    2116             :         }
    2117         499 :         found_options = true;
    2118         998 :         const std::string arg_inline_usage = argument.get_inline_usage();
    2119             :         const MutuallyExclusiveGroup *arg_mutex =
    2120         499 :             get_belonging_mutex(&argument);
    2121         499 :         if ((cur_mutex != nullptr) && (arg_mutex == nullptr)) {
    2122          26 :           curline += ']';
    2123          26 :           if (this->m_usage_break_on_mutex) {
    2124          26 :             stream << curline << std::endl;
    2125          26 :             curline = std::string(indent_size, ' ');
    2126             :           }
    2127         473 :         } else if ((cur_mutex == nullptr) && (arg_mutex != nullptr)) {
    2128          28 :           if ((this->m_usage_break_on_mutex && curline.size() > indent_size) ||
    2129           1 :               curline.size() + 3 + arg_inline_usage.size() >
    2130           1 :                   this->m_usage_max_line_width) {
    2131          26 :             stream << curline << std::endl;
    2132          26 :             curline = std::string(indent_size, ' ');
    2133             :           }
    2134          27 :           curline += " [";
    2135         446 :         } else if ((cur_mutex != nullptr) && (arg_mutex != nullptr)) {
    2136          42 :           if (cur_mutex != arg_mutex) {
    2137           4 :             curline += ']';
    2138           4 :             if (this->m_usage_break_on_mutex ||
    2139           0 :                 curline.size() + 3 + arg_inline_usage.size() >
    2140           0 :                     this->m_usage_max_line_width) {
    2141           4 :               stream << curline << std::endl;
    2142           4 :               curline = std::string(indent_size, ' ');
    2143             :             }
    2144           4 :             curline += " [";
    2145             :           } else {
    2146          38 :             curline += '|';
    2147             :           }
    2148             :         }
    2149         499 :         cur_mutex = arg_mutex;
    2150         499 :         if (curline.size() + 1 + arg_inline_usage.size() >
    2151         499 :             this->m_usage_max_line_width) {
    2152          88 :           stream << curline << std::endl;
    2153          88 :           curline = std::string(indent_size, ' ');
    2154          88 :           curline += " ";
    2155         411 :         } else if (cur_mutex == nullptr) {
    2156         346 :           curline += " ";
    2157             :         }
    2158         499 :         curline += arg_inline_usage;
    2159             :       }
    2160          21 :       if (cur_mutex != nullptr) {
    2161           1 :         curline += ']';
    2162             :       }
    2163          21 :       return found_options;
    2164          16 :     };
    2165             : 
    2166          16 :     const bool found_options = deal_with_options_of_group(0);
    2167             : 
    2168          32 :     if (found_options && multiline_usage &&
    2169          16 :         !this->m_positional_arguments.empty()) {
    2170          12 :       stream << curline << std::endl;
    2171          12 :       curline = std::string(indent_size, ' ');
    2172             :     }
    2173             :     // Put positional arguments after the optionals
    2174          41 :     for (const auto &argument : this->m_positional_arguments) {
    2175          25 :       if (argument.m_is_hidden) {
    2176           0 :         continue;
    2177             :       }
    2178          25 :       const std::string pos_arg = !argument.m_metavar.empty()
    2179             :                                       ? argument.m_metavar
    2180          50 :                                       : argument.m_names.front();
    2181          25 :       if (curline.size() + 1 + pos_arg.size() > this->m_usage_max_line_width) {
    2182           0 :         stream << curline << std::endl;
    2183           0 :         curline = std::string(indent_size, ' ');
    2184             :       }
    2185          25 :       curline += " ";
    2186          28 :       if (argument.m_num_args_range.get_min() == 0 &&
    2187           3 :           !argument.m_num_args_range.is_right_bounded()) {
    2188           3 :         curline += "[";
    2189           3 :         curline += pos_arg;
    2190           3 :         curline += "]...";
    2191          44 :       } else if (argument.m_num_args_range.get_min() == 1 &&
    2192          22 :                  !argument.m_num_args_range.is_right_bounded()) {
    2193           2 :         curline += pos_arg;
    2194           2 :         curline += "...";
    2195             :       } else {
    2196          20 :         curline += pos_arg;
    2197             :       }
    2198             :     }
    2199             : 
    2200          16 :     if (multiline_usage) {
    2201             :       // Display options of other groups
    2202          21 :       for (std::size_t i = 0; i < m_group_names.size(); ++i) {
    2203           5 :         stream << curline << std::endl << std::endl;
    2204           5 :         stream << m_group_names[i] << ":" << std::endl;
    2205           5 :         curline = std::string(indent_size, ' ');
    2206           5 :         deal_with_options_of_group(i + 1);
    2207             :       }
    2208             :     }
    2209             : 
    2210          16 :     stream << curline;
    2211             : 
    2212             :     // Put subcommands after positional arguments
    2213          16 :     if (!m_subparser_map.empty()) {
    2214           4 :       stream << " {";
    2215           4 :       std::size_t i{0};
    2216          20 :       for (const auto &[command, subparser] : m_subparser_map) {
    2217          16 :         if (subparser->get().m_suppress) {
    2218           0 :           continue;
    2219             :         }
    2220             : 
    2221          16 :         if (i == 0) {
    2222           4 :           stream << command;
    2223             :         } else {
    2224          12 :           stream << "," << command;
    2225             :         }
    2226          16 :         ++i;
    2227             :       }
    2228           4 :       stream << "}";
    2229             :     }
    2230             : 
    2231          32 :     return stream.str();
    2232             :   }
    2233             : 
    2234             :   // Printing the one and only help message
    2235             :   // I've stuck with a simple message format, nothing fancy.
    2236             :   [[deprecated("Use cout << program; instead.  See also help().")]] std::string
    2237             :   print_help() const {
    2238             :     auto out = help();
    2239             :     std::cout << out.rdbuf();
    2240             :     return out.str();
    2241             :   }
    2242             : 
    2243         603 :   void add_subparser(ArgumentParser &parser) {
    2244         603 :     parser.m_parser_path = m_program_name + " " + parser.m_program_name;
    2245         603 :     auto it = m_subparsers.emplace(std::cend(m_subparsers), parser);
    2246         603 :     m_subparser_map.insert_or_assign(parser.m_program_name, it);
    2247         603 :     m_subparser_used.insert_or_assign(parser.m_program_name, false);
    2248         603 :   }
    2249             : 
    2250             :   void set_suppress(bool suppress) { m_suppress = suppress; }
    2251             : 
    2252             : protected:
    2253         499 :   const MutuallyExclusiveGroup *get_belonging_mutex(const Argument *arg) const {
    2254        1840 :     for (const auto &mutex : m_mutually_exclusive_groups) {
    2255        1410 :       if (std::find(mutex.m_elements.begin(), mutex.m_elements.end(), arg) !=
    2256        2820 :           mutex.m_elements.end()) {
    2257          69 :         return &mutex;
    2258             :       }
    2259             :     }
    2260         430 :     return nullptr;
    2261             :   }
    2262             : 
    2263          20 :   bool is_valid_prefix_char(char c) const {
    2264          20 :     return m_prefix_chars.find(c) != std::string::npos;
    2265             :   }
    2266             : 
    2267           0 :   char get_any_valid_prefix_char() const { return m_prefix_chars[0]; }
    2268             : 
    2269             :   /*
    2270             :    * Pre-process this argument list. Anything starting with "--", that
    2271             :    * contains an =, where the prefix before the = has an entry in the
    2272             :    * options table, should be split.
    2273             :    */
    2274             :   std::vector<std::string>
    2275       10589 :   preprocess_arguments(const std::vector<std::string> &raw_arguments) const {
    2276       10589 :     std::vector<std::string> arguments{};
    2277       73733 :     for (const auto &arg : raw_arguments) {
    2278             : 
    2279             :       const auto argument_starts_with_prefix_chars =
    2280       84941 :           [this](const std::string &a) -> bool {
    2281       42472 :         if (!a.empty()) {
    2282             : 
    2283       81089 :           const auto legal_prefix = [this](char c) -> bool {
    2284       81089 :             return m_prefix_chars.find(c) != std::string::npos;
    2285       42469 :           };
    2286             : 
    2287             :           // Windows-style
    2288             :           // if '/' is a legal prefix char
    2289             :           // then allow single '/' followed by argument name, followed by an
    2290             :           // assign char, e.g., ':' e.g., 'test.exe /A:Foo'
    2291       42469 :           const auto windows_style = legal_prefix('/');
    2292             : 
    2293       42469 :           if (windows_style) {
    2294           0 :             if (legal_prefix(a[0])) {
    2295       37228 :               return true;
    2296             :             }
    2297             :           } else {
    2298             :             // Slash '/' is not a legal prefix char
    2299             :             // For all other characters, only support long arguments
    2300             :             // i.e., the argument must start with 2 prefix chars, e.g,
    2301             :             // '--foo' e,g, './test --foo=Bar -DARG=yes'
    2302       42469 :             if (a.size() > 1) {
    2303       37228 :               return (legal_prefix(a[0]) && legal_prefix(a[1]));
    2304             :             }
    2305             :           }
    2306             :         }
    2307        5244 :         return false;
    2308       63144 :       };
    2309             : 
    2310             :       // Check that:
    2311             :       // - We don't have an argument named exactly this
    2312             :       // - The argument starts with a prefix char, e.g., "--"
    2313             :       // - The argument contains an assign char, e.g., "="
    2314       63144 :       auto assign_char_pos = arg.find_first_of(m_assign_chars);
    2315             : 
    2316      105616 :       if (m_argument_map.find(arg) == m_argument_map.end() &&
    2317      105616 :           argument_starts_with_prefix_chars(arg) &&
    2318             :           assign_char_pos != std::string::npos) {
    2319             :         // Get the name of the potential option, and check it exists
    2320           5 :         std::string opt_name = arg.substr(0, assign_char_pos);
    2321           5 :         if (m_argument_map.find(opt_name) != m_argument_map.end()) {
    2322             :           // This is the name of an option! Split it into two parts
    2323           5 :           arguments.push_back(std::move(opt_name));
    2324           5 :           arguments.push_back(arg.substr(assign_char_pos + 1));
    2325           5 :           continue;
    2326             :         }
    2327             :       }
    2328             :       // If we've fallen through to here, then it's a standard argument
    2329       63139 :       arguments.push_back(arg);
    2330             :     }
    2331       10589 :     return arguments;
    2332             :   }
    2333             : 
    2334             :   /*
    2335             :    * @throws std::runtime_error in case of any invalid argument
    2336             :    */
    2337        4808 :   void parse_args_internal(const std::vector<std::string> &raw_arguments) {
    2338        4839 :     auto arguments = preprocess_arguments(raw_arguments);
    2339        4808 :     if (m_program_name.empty() && !arguments.empty()) {
    2340           0 :       m_program_name = arguments.front();
    2341             :     }
    2342        4808 :     auto end = std::end(arguments);
    2343        4808 :     auto positional_argument_it = std::begin(m_positional_arguments);
    2344       15693 :     for (auto it = std::next(std::begin(arguments)); it != end;) {
    2345       10950 :       const auto &current_argument = *it;
    2346       10950 :       if (Argument::is_positional(current_argument, m_prefix_chars)) {
    2347        1313 :         if (positional_argument_it == std::end(m_positional_arguments)) {
    2348             : 
    2349             :           // Check sub-parsers
    2350           0 :           auto subparser_it = m_subparser_map.find(current_argument);
    2351           0 :           if (subparser_it != m_subparser_map.end()) {
    2352             : 
    2353             :             // build list of remaining args
    2354             :             const auto unprocessed_arguments =
    2355           0 :                 std::vector<std::string>(it, end);
    2356             : 
    2357             :             // invoke subparser
    2358           0 :             m_is_parsed = true;
    2359           0 :             m_subparser_used[current_argument] = true;
    2360           0 :             return subparser_it->second->get().parse_args(
    2361           0 :                 unprocessed_arguments);
    2362             :           }
    2363             : 
    2364           0 :           if (m_positional_arguments.empty()) {
    2365             : 
    2366             :             // Ask the user if they argument they provided was a typo
    2367             :             // for some sub-parser,
    2368             :             // e.g., user provided `git totes` instead of `git notes`
    2369           0 :             if (!m_subparser_map.empty()) {
    2370             :               throw std::runtime_error(
    2371           0 :                   "Failed to parse '" + current_argument + "', did you mean '" +
    2372           0 :                   std::string{details::get_most_similar_string(
    2373           0 :                       m_subparser_map, current_argument)} +
    2374           0 :                   "'");
    2375             :             }
    2376             : 
    2377             :             // Ask the user if they meant to use a specific optional argument
    2378           0 :             if (!m_optional_arguments.empty()) {
    2379           0 :               for (const auto &opt : m_optional_arguments) {
    2380           0 :                 if (!opt.m_implicit_value.has_value()) {
    2381             :                   // not a flag, requires a value
    2382           0 :                   if (!opt.m_is_used) {
    2383             :                     throw std::runtime_error(
    2384           0 :                         "Zero positional arguments expected, did you mean " +
    2385           0 :                         opt.get_usage_full());
    2386             :                   }
    2387             :                 }
    2388             :               }
    2389             : 
    2390           0 :               throw std::runtime_error("Zero positional arguments expected");
    2391             :             } else {
    2392           0 :               throw std::runtime_error("Zero positional arguments expected");
    2393             :             }
    2394             :           } else {
    2395             :             throw std::runtime_error("Maximum number of positional arguments "
    2396           0 :                                      "exceeded, failed to parse '" +
    2397           0 :                                      current_argument + "'");
    2398             :           }
    2399             :         }
    2400        1313 :         auto argument = positional_argument_it++;
    2401             : 
    2402             :         // Deal with the situation of <positional_arg1>... <positional_arg2>
    2403        2518 :         if (argument->m_num_args_range.get_min() == 1 &&
    2404        1318 :             argument->m_num_args_range.get_max() == (std::numeric_limits<std::size_t>::max)() &&
    2405         213 :             positional_argument_it != std::end(m_positional_arguments) &&
    2406         200 :             std::next(positional_argument_it) == std::end(m_positional_arguments) &&
    2407        2618 :             positional_argument_it->m_num_args_range.get_min() == 1 &&
    2408         100 :             positional_argument_it->m_num_args_range.get_max() == 1 ) {
    2409         100 :           if (std::next(it) != end) {
    2410         100 :             positional_argument_it->consume(std::prev(end), end);
    2411         100 :             end = std::prev(end);
    2412             :           } else {
    2413           0 :             throw std::runtime_error("Missing " + positional_argument_it->m_names.front());
    2414             :           }
    2415             :         }
    2416             : 
    2417        1313 :         it = argument->consume(it, end);
    2418        1313 :         continue;
    2419             :       }
    2420             : 
    2421        9637 :       auto arg_map_it = m_argument_map.find(current_argument);
    2422        9637 :       if (arg_map_it != m_argument_map.end()) {
    2423        9637 :         auto argument = arg_map_it->second;
    2424        9637 :         it = argument->consume(std::next(it), end, arg_map_it->first);
    2425           0 :       } else if (const auto &compound_arg = current_argument;
    2426           0 :                  compound_arg.size() > 1 &&
    2427           0 :                  is_valid_prefix_char(compound_arg[0]) &&
    2428           0 :                  !is_valid_prefix_char(compound_arg[1])) {
    2429           0 :         ++it;
    2430           0 :         for (std::size_t j = 1; j < compound_arg.size(); j++) {
    2431           0 :           auto hypothetical_arg = std::string{'-', compound_arg[j]};
    2432           0 :           auto arg_map_it2 = m_argument_map.find(hypothetical_arg);
    2433           0 :           if (arg_map_it2 != m_argument_map.end()) {
    2434           0 :             auto argument = arg_map_it2->second;
    2435           0 :             it = argument->consume(it, end, arg_map_it2->first);
    2436             :           } else {
    2437           0 :             throw std::runtime_error("Unknown argument: " + current_argument);
    2438             :           }
    2439             :         }
    2440             :       } else {
    2441           0 :         throw std::runtime_error("Unknown argument: " + current_argument);
    2442             :       }
    2443             :     }
    2444        4743 :     m_is_parsed = true;
    2445             :   }
    2446             : 
    2447             :   /*
    2448             :    * Like parse_args_internal but collects unused args into a vector<string>
    2449             :    */
    2450             :   std::vector<std::string>
    2451             :   parse_known_args_internal(const std::vector<std::string> &raw_arguments) {
    2452             :     auto arguments = preprocess_arguments(raw_arguments);
    2453             : 
    2454             :     std::vector<std::string> unknown_arguments{};
    2455             : 
    2456             :     if (m_program_name.empty() && !arguments.empty()) {
    2457             :       m_program_name = arguments.front();
    2458             :     }
    2459             :     auto end = std::end(arguments);
    2460             :     auto positional_argument_it = std::begin(m_positional_arguments);
    2461             :     for (auto it = std::next(std::begin(arguments)); it != end;) {
    2462             :       const auto &current_argument = *it;
    2463             :       if (Argument::is_positional(current_argument, m_prefix_chars)) {
    2464             :         if (positional_argument_it == std::end(m_positional_arguments)) {
    2465             : 
    2466             :           // Check sub-parsers
    2467             :           auto subparser_it = m_subparser_map.find(current_argument);
    2468             :           if (subparser_it != m_subparser_map.end()) {
    2469             : 
    2470             :             // build list of remaining args
    2471             :             const auto unprocessed_arguments =
    2472             :                 std::vector<std::string>(it, end);
    2473             : 
    2474             :             // invoke subparser
    2475             :             m_is_parsed = true;
    2476             :             m_subparser_used[current_argument] = true;
    2477             :             return subparser_it->second->get().parse_known_args_internal(
    2478             :                 unprocessed_arguments);
    2479             :           }
    2480             : 
    2481             :           // save current argument as unknown and go to next argument
    2482             :           unknown_arguments.push_back(current_argument);
    2483             :           ++it;
    2484             :         } else {
    2485             :           // current argument is the value of a positional argument
    2486             :           // consume it
    2487             :           auto argument = positional_argument_it++;
    2488             :           it = argument->consume(it, end);
    2489             :         }
    2490             :         continue;
    2491             :       }
    2492             : 
    2493             :       auto arg_map_it = m_argument_map.find(current_argument);
    2494             :       if (arg_map_it != m_argument_map.end()) {
    2495             :         auto argument = arg_map_it->second;
    2496             :         it = argument->consume(std::next(it), end, arg_map_it->first);
    2497             :       } else if (const auto &compound_arg = current_argument;
    2498             :                  compound_arg.size() > 1 &&
    2499             :                  is_valid_prefix_char(compound_arg[0]) &&
    2500             :                  !is_valid_prefix_char(compound_arg[1])) {
    2501             :         ++it;
    2502             :         for (std::size_t j = 1; j < compound_arg.size(); j++) {
    2503             :           auto hypothetical_arg = std::string{'-', compound_arg[j]};
    2504             :           auto arg_map_it2 = m_argument_map.find(hypothetical_arg);
    2505             :           if (arg_map_it2 != m_argument_map.end()) {
    2506             :             auto argument = arg_map_it2->second;
    2507             :             it = argument->consume(it, end, arg_map_it2->first);
    2508             :           } else {
    2509             :             unknown_arguments.push_back(current_argument);
    2510             :             break;
    2511             :           }
    2512             :         }
    2513             :       } else {
    2514             :         // current argument is an optional-like argument that is unknown
    2515             :         // save it and move to next argument
    2516             :         unknown_arguments.push_back(current_argument);
    2517             :         ++it;
    2518             :       }
    2519             :     }
    2520             :     m_is_parsed = true;
    2521             :     return unknown_arguments;
    2522             :   }
    2523             : 
    2524             :   // Used by print_help.
    2525           0 :   std::size_t get_length_of_longest_argument() const {
    2526           0 :     if (m_argument_map.empty()) {
    2527           0 :       return 0;
    2528             :     }
    2529           0 :     std::size_t max_size = 0;
    2530           0 :     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
    2531           0 :       max_size =
    2532           0 :           std::max<std::size_t>(max_size, argument->get_arguments_length());
    2533             :     }
    2534           0 :     for ([[maybe_unused]] const auto &[command, unused] : m_subparser_map) {
    2535           0 :       max_size = std::max<std::size_t>(max_size, command.size());
    2536             :     }
    2537           0 :     return max_size;
    2538             :   }
    2539             : 
    2540             :   using argument_it = std::list<Argument>::iterator;
    2541             :   using mutex_group_it = std::vector<MutuallyExclusiveGroup>::iterator;
    2542             :   using argument_parser_it =
    2543             :       std::list<std::reference_wrapper<ArgumentParser>>::iterator;
    2544             : 
    2545      230132 :   void index_argument(argument_it it) {
    2546      468457 :     for (const auto &name : std::as_const(it->m_names)) {
    2547      238325 :       m_argument_map.insert_or_assign(name, it);
    2548             :     }
    2549      230132 :   }
    2550             : 
    2551             :   std::string m_program_name;
    2552             :   std::string m_version;
    2553             :   std::string m_description;
    2554             :   std::string m_epilog;
    2555             :   bool m_exit_on_default_arguments = true;
    2556             :   std::string m_prefix_chars{"-"};
    2557             :   std::string m_assign_chars{"="};
    2558             :   bool m_is_parsed = false;
    2559             :   std::list<Argument> m_positional_arguments;
    2560             :   std::list<Argument> m_optional_arguments;
    2561             :   std::map<std::string, argument_it> m_argument_map;
    2562             :   std::string m_parser_path;
    2563             :   std::list<std::reference_wrapper<ArgumentParser>> m_subparsers;
    2564             :   std::map<std::string, argument_parser_it> m_subparser_map;
    2565             :   std::map<std::string, bool> m_subparser_used;
    2566             :   std::vector<MutuallyExclusiveGroup> m_mutually_exclusive_groups;
    2567             :   bool m_suppress = false;
    2568             :   std::size_t m_usage_max_line_width = std::numeric_limits<std::size_t>::max();
    2569             :   bool m_usage_break_on_mutex = false;
    2570             :   int m_usage_newline_counter = 0;
    2571             :   std::vector<std::string> m_group_names;
    2572             : };
    2573             : 
    2574             : } // namespace argparse

Generated by: LCOV version 1.14