LCOV - code coverage report
Current view: top level - apps/argparse - argparse.hpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 652 1001 65.1 %
Date: 2025-03-25 20:12:57 Functions: 337 372 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      104969 : template <typename T> std::string repr(T const &val) {
     109             :   if constexpr (std::is_same_v<T, bool>) {
     110      103511 :     return val ? "true" : "false";
     111             :   } else if constexpr (std::is_convertible_v<T, std::string_view>) {
     112         194 :     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        2528 :     std::stringstream out;
     139        1264 :     out << val;
     140        2528 :     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        6856 : constexpr auto pointer_range(std::string_view s) noexcept {
     191        6856 :   return std::tuple(s.data(), s.data() + s.size());
     192             : }
     193             : 
     194             : template <class CharT, class Traits>
     195       26265 : constexpr bool starts_with(std::basic_string_view<CharT, Traits> prefix,
     196             :                            std::basic_string_view<CharT, Traits> s) noexcept {
     197       26265 :   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        6424 : constexpr auto consume_binary_prefix(std::string_view s)
     214             :     -> ConsumeBinaryPrefixResult {
     215       12848 :   if (starts_with(std::string_view{"0b"}, s) ||
     216       12848 :       starts_with(std::string_view{"0B"}, s)) {
     217           0 :     s.remove_prefix(2);
     218           0 :     return {true, s};
     219             :   }
     220        6424 :   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        6424 : constexpr auto consume_hex_prefix(std::string_view s)
     231             :     -> ConsumeHexPrefixResult {
     232        6424 :   if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
     233           0 :     s.remove_prefix(2);
     234           0 :     return {true, s};
     235             :   }
     236        6424 :   return {false, s};
     237             : }
     238             : 
     239             : template <class T, auto Param>
     240        1001 : inline auto do_from_chars(std::string_view s) -> T {
     241        1001 :   T x{0};
     242        1001 :   auto [first, last] = pointer_range(s);
     243        1001 :   auto [ptr, ec] = std::from_chars(first, last, x, Param);
     244        1001 :   if (ec == std::errc()) {
     245        1001 :     if (ptr == last) {
     246        1001 :       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         432 :   auto operator()(std::string_view s) -> T {
     262         432 :     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         569 :   auto operator()(std::string_view s) -> T {
     310         569 :     auto [ok, rest] = consume_hex_prefix(s);
     311         569 :     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         569 :     auto [ok_binary, rest_binary] = consume_binary_prefix(s);
     324         569 :     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         569 :     if (starts_with("0"sv, s)) {
     337             :       try {
     338          11 :         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         558 :       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        5855 : template <class T> inline auto do_strtod(std::string const &s) -> T {
     371        5855 :   if (isspace(static_cast<unsigned char>(s[0])) || s[0] == '+') {
     372           0 :     throw std::invalid_argument{"pattern '" + s + "' not found"};
     373             :   }
     374             : 
     375        5855 :   auto [first, last] = pointer_range(s);
     376             :   char *ptr;
     377             : 
     378        5855 :   errno = 0;
     379        5855 :   auto x = generic_strtod<T>(first, &ptr);
     380        5855 :   if (errno == 0) {
     381        5855 :     if (ptr == last) {
     382        5850 :       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        5855 :   auto operator()(std::string const &s) -> T {
     395        5855 :     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        5855 :     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        5855 :       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       11720 : inline default_arguments operator&(const default_arguments &a,
     590             :                                    const default_arguments &b) {
     591             :   return static_cast<default_arguments>(
     592       11720 :       static_cast<std::underlying_type<default_arguments>::type>(a) &
     593       11720 :       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      259078 :   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      259078 :         m_is_optional((is_optional(a[I], prefix_chars) || ...)),
     609             :         m_is_required(false), m_is_repeatable(false), m_is_used(false),
     610      267887 :         m_is_hidden(false), m_prefix_chars(prefix_chars) {
     611      259078 :     ((void)m_names.emplace_back(a[I]), ...);
     612      259078 :     std::sort(
     613       17618 :         m_names.begin(), m_names.end(), [](const auto &lhs, const auto &rhs) {
     614       17618 :           return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size();
     615             :         });
     616      259078 :   }
     617             : 
     618             : public:
     619             :   template <std::size_t N>
     620      259078 :   explicit Argument(std::string_view prefix_chars,
     621             :                     std::array<std::string_view, N> &&a)
     622      259078 :       : Argument(prefix_chars, std::move(a), std::make_index_sequence<N>{}) {}
     623             : 
     624      249736 :   Argument &help(std::string help_text) {
     625      249736 :     m_help = std::move(help_text);
     626      249736 :     return *this;
     627             :   }
     628             : 
     629      164887 :   Argument &metavar(std::string metavar) {
     630      164887 :     m_metavar = std::move(metavar);
     631      164887 :     return *this;
     632             :   }
     633             : 
     634      104969 :   template <typename T> Argument &default_value(T &&value) {
     635      104969 :     m_num_args_range = NArgsRange{0, m_num_args_range.get_max()};
     636      104969 :     m_default_value_repr = details::repr(value);
     637             : 
     638             :     if constexpr (std::is_convertible_v<T, std::string_view>) {
     639         194 :       m_default_value_str = std::string{std::string_view{value}};
     640             :     } else if constexpr (details::can_invoke_to_string<T>::value) {
     641      104775 :       m_default_value_str = std::to_string(value);
     642             :     }
     643             : 
     644      104969 :     m_default_value = std::forward<T>(value);
     645      104969 :     return *this;
     646             :   }
     647             : 
     648         194 :   Argument &default_value(const char *value) {
     649         194 :     return default_value(std::string(value));
     650             :   }
     651             : 
     652             :   Argument &required() {
     653             :     m_is_required = true;
     654             :     return *this;
     655             :   }
     656             : 
     657      103511 :   Argument &implicit_value(std::any value) {
     658      103511 :     m_implicit_value = std::move(value);
     659      103511 :     m_num_args_range = NArgsRange{0, 0};
     660      103511 :     return *this;
     661             :   }
     662             : 
     663             :   // This is shorthand for:
     664             :   //   program.add_argument("foo")
     665             :   //     .default_value(false)
     666             :   //     .implicit_value(true)
     667      102480 :   Argument &flag() {
     668      102480 :     default_value(false);
     669      102480 :     implicit_value(true);
     670      102480 :     return *this;
     671             :   }
     672             : 
     673             :   template <class F, class... Args>
     674      244250 :   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      244250 :       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      244250 :     return *this;
     691             :   }
     692             : 
     693       45320 :   auto &store_into(bool &var) {
     694       45320 :     flag();
     695       45320 :     if (m_default_value.has_value()) {
     696       45320 :       var = std::any_cast<bool>(m_default_value);
     697             :     }
     698       45320 :     action([&var](const auto & /*unused*/) { var = true; });
     699       45320 :     return *this;
     700             :   }
     701             : 
     702             :   template <typename T, typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
     703        6386 :   auto &store_into(T &var) {
     704        6386 :     if (m_default_value.has_value()) {
     705             :       try
     706             :       {
     707         238 :         var = std::any_cast<T>(m_default_value);
     708             :       }
     709          21 :       catch (...)
     710             :       {
     711          21 :         var = static_cast<T>(std::any_cast<int>(m_default_value));
     712             :       }
     713             :     }
     714        6467 :     action([&var](const auto &s) {
     715         158 :       var = details::parse_number<T, details::radix_10>()(s);
     716             :     });
     717        6386 :     return *this;
     718             :   }
     719             : 
     720       15324 :   auto &store_into(double &var) {
     721       15324 :     if (m_default_value.has_value()) {
     722             :       try
     723             :       {
     724        1026 :         var = std::any_cast<double>(m_default_value);
     725             :       }
     726         105 :       catch (...)
     727             :       {
     728         105 :         var = std::any_cast<int>(m_default_value);
     729             :       }
     730             :     }
     731         245 :     action([&var](const auto &s) {
     732         245 :       var = details::parse_number<double, details::chars_format::general>()(s);
     733       15564 :     });
     734       15324 :     return *this;
     735             :   }
     736             : 
     737       33246 :   auto &store_into(std::string &var) {
     738       33246 :     if (m_default_value.has_value()) {
     739          50 :       var = std::any_cast<std::string>(m_default_value);
     740             :     }
     741       37192 :     action([&var](const std::string &s) { var = s; });
     742       33246 :     return *this;
     743             :   }
     744             : 
     745         461 :   auto &store_into(std::vector<std::string> &var) {
     746         461 :     if (m_default_value.has_value()) {
     747           0 :       var = std::any_cast<std::vector<std::string>>(m_default_value);
     748             :     }
     749         662 :     action([this, &var](const std::string &s) {
     750         222 :       if (!m_is_used) {
     751         218 :         var.clear();
     752             :       }
     753         222 :       m_is_used = true;
     754         222 :       var.push_back(s);
     755         461 :     });
     756         461 :     return *this;
     757             :   }
     758             : 
     759        2134 :   auto &store_into(std::vector<int> &var) {
     760        2134 :     if (m_default_value.has_value()) {
     761           0 :       var = std::any_cast<std::vector<int>>(m_default_value);
     762             :     }
     763         593 :     action([this, &var](const std::string &s) {
     764         274 :       if (!m_is_used) {
     765          45 :         var.clear();
     766             :       }
     767         274 :       m_is_used = true;
     768         274 :       var.push_back(details::parse_number<int, details::radix_10>()(s));
     769        2408 :     });
     770        2134 :     return *this;
     771             :   }
     772             : 
     773          29 :   auto &store_into(std::set<std::string> &var) {
     774          29 :     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          29 :     });
     784          29 :     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       44107 :   auto &append() {
     802       44107 :     m_is_repeatable = true;
     803       44107 :     return *this;
     804             :   }
     805             : 
     806             :   // Cause the argument to be invisible in usage and help
     807       19750 :   auto &hidden() {
     808       19750 :     m_is_hidden = true;
     809       19750 :     return *this;
     810             :   }
     811             : 
     812             :   template <char Shape, typename T>
     813       31429 :   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        2630 :       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       28799 :       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       31429 :     return *this;
     854             :   }
     855             : 
     856       20797 :   Argument &nargs(std::size_t num_args) {
     857       20797 :     m_num_args_range = NArgsRange{num_args, num_args};
     858       20797 :     return *this;
     859             :   }
     860             : 
     861        3249 :   Argument &nargs(std::size_t num_args_min, std::size_t num_args_max) {
     862        3249 :     m_num_args_range = NArgsRange{num_args_min, num_args_max};
     863        3249 :     return *this;
     864             :   }
     865             : 
     866        1280 :   Argument &nargs(nargs_pattern pattern) {
     867        1280 :     switch (pattern) {
     868         118 :     case nargs_pattern::optional:
     869         118 :       m_num_args_range = NArgsRange{0, 1};
     870         118 :       break;
     871        1050 :     case nargs_pattern::any:
     872        1050 :       m_num_args_range =
     873        1050 :           NArgsRange{0, (std::numeric_limits<std::size_t>::max)()};
     874        1050 :       break;
     875         112 :     case nargs_pattern::at_least_one:
     876         112 :       m_num_args_range =
     877         112 :           NArgsRange{1, (std::numeric_limits<std::size_t>::max)()};
     878         112 :       break;
     879             :     }
     880        1280 :     return *this;
     881             :   }
     882             : 
     883        1022 :   Argument &remaining() {
     884        1022 :     m_accepts_optional_like_value = true;
     885        1022 :     return nargs(nargs_pattern::any);
     886             :   }
     887             : 
     888        4590 :   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        4590 :     if (!m_choices.has_value()) {
     895        1448 :       m_choices = std::vector<std::string>{};
     896             :     }
     897             : 
     898             :     if constexpr (std::is_convertible_v<T, std::string_view>) {
     899        9180 :       m_choices.value().push_back(
     900        4590 :           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        4590 :   }
     905             : 
     906        1448 :   Argument &choices() {
     907        1448 :     if (!m_choices.has_value()) {
     908           0 :       throw std::runtime_error("Zero choices provided");
     909             :     }
     910        1448 :     return *this;
     911             :   }
     912             : 
     913             :   template <typename T, typename... U>
     914        4590 :   Argument &choices(T &&first, U &&... rest) {
     915        4590 :     add_choice(std::forward<T>(first));
     916        4590 :     choices(std::forward<U>(rest)...);
     917        4590 :     return *this;
     918             :   }
     919             : 
     920        1437 :   void find_default_value_in_choices_or_throw() const {
     921             : 
     922        1437 :     const auto &choices = m_choices.value();
     923             : 
     924        1437 :     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        1437 :   }
     942             : 
     943             :   template <typename Iterator>
     944         180 :   void find_value_in_choices_or_throw(Iterator it) const {
     945             : 
     946         180 :     const auto &choices = m_choices.value();
     947             : 
     948         180 :     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         180 :   }
     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       26100 :   Iterator consume(Iterator start, Iterator end,
     970             :                    std::string_view used_name = {}, bool dry_run = false) {
     971       26100 :     if (!m_is_repeatable && m_is_used) {
     972             :       throw std::runtime_error(
     973           0 :           std::string("Duplicate argument ").append(used_name));
     974             :     }
     975       26100 :     m_used_name = used_name;
     976             : 
     977       26100 :     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         180 :       std::size_t i = 0;
     981         180 :       auto max_number_of_args = m_num_args_range.get_max();
     982         360 :       for (auto it = start; it != end; ++it) {
     983         257 :         if (i == max_number_of_args) {
     984          77 :           break;
     985             :         }
     986         180 :         find_value_in_choices_or_throw(it);
     987         180 :         i += 1;
     988             :       }
     989             :     }
     990             : 
     991       26100 :     const auto num_args_max = m_num_args_range.get_max();
     992       26100 :     const auto num_args_min = m_num_args_range.get_min();
     993       26100 :     std::size_t dist = 0;
     994       26100 :     if (num_args_max == 0) {
     995        2650 :       if (!dry_run) {
     996        1182 :         m_values.emplace_back(m_implicit_value);
     997        2373 :         for(auto &action: m_actions) {
     998        2382 :           std::visit([&](const auto &f) { f({}); }, action);
     999             :         }
    1000        1182 :         if(m_actions.empty()){
    1001           0 :           std::visit([&](const auto &f) { f({}); }, m_default_action);
    1002             :         }
    1003        1182 :         m_is_used = true;
    1004             :       }
    1005        2650 :       return start;
    1006             :     }
    1007       23450 :     if ((dist = static_cast<std::size_t>(std::distance(start, end))) >=
    1008             :         num_args_min) {
    1009       23449 :       if (num_args_max < dist) {
    1010       13526 :         end = std::next(start, num_args_max);
    1011             :       }
    1012       23449 :       if (!m_accepts_optional_like_value) {
    1013       23320 :         end = std::find_if(
    1014             :             start, end,
    1015       23320 :             std::bind(is_optional, std::placeholders::_1, m_prefix_chars));
    1016       23320 :         dist = static_cast<std::size_t>(std::distance(start, end));
    1017       23320 :         if (dist < num_args_min) {
    1018             :           throw std::runtime_error("Too few arguments for '" +
    1019           0 :                                    std::string(m_used_name) + "'.");
    1020             :         }
    1021             :       }
    1022             : 
    1023             :       struct ActionApply {
    1024        2290 :         void operator()(valued_action &f) {
    1025        2290 :           std::transform(first, last, std::back_inserter(self.m_values), f);
    1026        2290 :         }
    1027             : 
    1028        8893 :         void operator()(void_action &f) {
    1029        8893 :           std::for_each(first, last, f);
    1030        8861 :           if (!self.m_default_value.has_value()) {
    1031        8698 :             if (!self.m_accepts_optional_like_value) {
    1032       17378 :               self.m_values.resize(
    1033        8689 :                   static_cast<std::size_t>(std::distance(first, last)));
    1034             :             }
    1035             :           }
    1036        8861 :         }
    1037             : 
    1038             :         Iterator first, last;
    1039             :         Argument &self;
    1040             :       };
    1041       23449 :       if (!dry_run) {
    1042       21831 :         for(auto &action: m_actions) {
    1043       10900 :           std::visit(ActionApply{start, end, *this}, action);
    1044             :         }
    1045       10931 :         if(m_actions.empty()){
    1046         283 :           std::visit(ActionApply{start, end, *this}, m_default_action);
    1047             :         }
    1048       10931 :         m_is_used = true;
    1049             :       }
    1050       23417 :       return end;
    1051             :     }
    1052           1 :     if (m_default_value.has_value()) {
    1053           0 :       if (!dry_run) {
    1054           0 :         m_is_used = true;
    1055             :       }
    1056           0 :       return start;
    1057             :     }
    1058             :     throw std::runtime_error("Too few arguments for '" +
    1059           1 :                              std::string(m_used_name) + "'.");
    1060             :   }
    1061             : 
    1062             :   /*
    1063             :    * @throws std::runtime_error if argument values are not valid
    1064             :    */
    1065      266321 :   void validate() const {
    1066      266321 :     if (m_is_optional) {
    1067             :       // TODO: check if an implicit value was programmed for this argument
    1068      263927 :       if (!m_is_used && !m_default_value.has_value() && m_is_required) {
    1069           0 :         throw_required_arg_not_used_error();
    1070             :       }
    1071      263927 :       if (m_is_used && m_is_required && m_values.empty()) {
    1072           0 :         throw_required_arg_no_value_provided_error();
    1073             :       }
    1074             :     } else {
    1075        2399 :       if (!m_num_args_range.contains(m_values.size()) &&
    1076           5 :           !m_default_value.has_value()) {
    1077           5 :         throw_nargs_range_validation_error();
    1078             :       }
    1079             :     }
    1080             : 
    1081      266316 :     if (m_choices.has_value()) {
    1082             :       // Make sure the default value (if provided)
    1083             :       // is in the list of choices
    1084        1437 :       find_default_value_in_choices_or_throw();
    1085             :     }
    1086      266316 :   }
    1087             : 
    1088           6 :   std::string get_names_csv(char separator = ',') const {
    1089             :     return std::accumulate(
    1090          12 :         m_names.begin(), m_names.end(), std::string{""},
    1091           6 :         [&](const std::string &result, const std::string &name) {
    1092          12 :           return result.empty() ? name : result + separator + name;
    1093          18 :         });
    1094             :   }
    1095             : 
    1096           6 :   std::string get_usage_full() const {
    1097          12 :     std::stringstream usage;
    1098             : 
    1099           6 :     usage << get_names_csv('/');
    1100          12 :     const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
    1101           6 :     if (m_num_args_range.get_max() > 0) {
    1102           5 :       usage << " " << metavar;
    1103           5 :       if (m_num_args_range.get_max() > 1) {
    1104           0 :         usage << "...";
    1105             :       }
    1106             :     }
    1107          12 :     return usage.str();
    1108             :   }
    1109             : 
    1110         558 :   std::string get_inline_usage() const {
    1111        1116 :     std::stringstream usage;
    1112             :     // Find the longest variant to show in the usage string
    1113        1116 :     std::string longest_name = m_names.front();
    1114        1152 :     for (const auto &s : m_names) {
    1115         594 :       if (s.size() > longest_name.size()) {
    1116          36 :         longest_name = s;
    1117             :       }
    1118             :     }
    1119         558 :     if (!m_is_required) {
    1120         558 :       usage << "[";
    1121             :     }
    1122         558 :     usage << longest_name;
    1123        1116 :     const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
    1124         558 :     if (m_num_args_range.get_max() > 0) {
    1125         364 :       usage << " " << metavar;
    1126         401 :       if (m_num_args_range.get_max() > 1 &&
    1127          37 :           m_metavar.find("> <") == std::string::npos) {
    1128           0 :         usage << "...";
    1129             :       }
    1130             :     }
    1131         558 :     if (!m_is_required) {
    1132         558 :       usage << "]";
    1133             :     }
    1134         558 :     if (m_is_repeatable) {
    1135          85 :       usage << "...";
    1136             :     }
    1137        1116 :     return usage.str();
    1138             :   }
    1139             : 
    1140           0 :   std::size_t get_arguments_length() const {
    1141             : 
    1142           0 :     std::size_t names_size = std::accumulate(
    1143           0 :         std::begin(m_names), std::end(m_names), std::size_t(0),
    1144           0 :         [](const auto &sum, const auto &s) { return sum + s.size(); });
    1145             : 
    1146           0 :     if (is_positional(m_names.front(), m_prefix_chars)) {
    1147             :       // A set metavar means this replaces the names
    1148           0 :       if (!m_metavar.empty()) {
    1149             :         // Indent and metavar
    1150           0 :         return 2 + m_metavar.size();
    1151             :       }
    1152             : 
    1153             :       // Indent and space-separated
    1154           0 :       return 2 + names_size + (m_names.size() - 1);
    1155             :     }
    1156             :     // Is an option - include both names _and_ metavar
    1157             :     // size = text + (", " between names)
    1158           0 :     std::size_t size = names_size + 2 * (m_names.size() - 1);
    1159           0 :     if (!m_metavar.empty() && m_num_args_range == NArgsRange{1, 1}) {
    1160           0 :       size += m_metavar.size() + 1;
    1161             :     }
    1162           0 :     return size + 2; // indent
    1163             :   }
    1164             : 
    1165           0 :   friend std::ostream &operator<<(std::ostream &stream,
    1166             :                                   const Argument &argument) {
    1167           0 :     std::stringstream name_stream;
    1168           0 :     name_stream << "  "; // indent
    1169           0 :     if (argument.is_positional(argument.m_names.front(),
    1170             :                                argument.m_prefix_chars)) {
    1171           0 :       if (!argument.m_metavar.empty()) {
    1172           0 :         name_stream << argument.m_metavar;
    1173             :       } else {
    1174           0 :         name_stream << details::join(argument.m_names.begin(),
    1175           0 :                                      argument.m_names.end(), " ");
    1176             :       }
    1177             :     } else {
    1178           0 :       name_stream << details::join(argument.m_names.begin(),
    1179           0 :                                    argument.m_names.end(), ", ");
    1180             :       // If we have a metavar, and one narg - print the metavar
    1181           0 :       if (!argument.m_metavar.empty() &&
    1182           0 :           argument.m_num_args_range == NArgsRange{1, 1}) {
    1183           0 :         name_stream << " " << argument.m_metavar;
    1184             :       }
    1185           0 :       else if (!argument.m_metavar.empty() &&
    1186           0 :                argument.m_num_args_range.get_min() == argument.m_num_args_range.get_max() &&
    1187           0 :                argument.m_metavar.find("> <") != std::string::npos) {
    1188           0 :         name_stream << " " << argument.m_metavar;
    1189             :       }
    1190             :     }
    1191             : 
    1192             :     // align multiline help message
    1193           0 :     auto stream_width = stream.width();
    1194           0 :     auto name_padding = std::string(name_stream.str().size(), ' ');
    1195           0 :     auto pos = std::string::size_type{};
    1196           0 :     auto prev = std::string::size_type{};
    1197           0 :     auto first_line = true;
    1198           0 :     auto hspace = "  "; // minimal space between name and help message
    1199           0 :     stream << name_stream.str();
    1200           0 :     std::string_view help_view(argument.m_help);
    1201           0 :     while ((pos = argument.m_help.find('\n', prev)) != std::string::npos) {
    1202           0 :       auto line = help_view.substr(prev, pos - prev + 1);
    1203           0 :       if (first_line) {
    1204           0 :         stream << hspace << line;
    1205           0 :         first_line = false;
    1206             :       } else {
    1207           0 :         stream.width(stream_width);
    1208           0 :         stream << name_padding << hspace << line;
    1209             :       }
    1210           0 :       prev += pos - prev + 1;
    1211             :     }
    1212           0 :     if (first_line) {
    1213           0 :       stream << hspace << argument.m_help;
    1214             :     } else {
    1215           0 :       auto leftover = help_view.substr(prev, argument.m_help.size() - prev);
    1216           0 :       if (!leftover.empty()) {
    1217           0 :         stream.width(stream_width);
    1218           0 :         stream << name_padding << hspace << leftover;
    1219             :       }
    1220             :     }
    1221             : 
    1222             :     // print nargs spec
    1223           0 :     if (!argument.m_help.empty()) {
    1224           0 :       stream << " ";
    1225             :     }
    1226           0 :     stream << argument.m_num_args_range;
    1227             : 
    1228           0 :     bool add_space = false;
    1229           0 :     if (argument.m_default_value.has_value() &&
    1230           0 :         argument.m_num_args_range != NArgsRange{0, 0}) {
    1231           0 :       stream << "[default: " << argument.m_default_value_repr << "]";
    1232           0 :       add_space = true;
    1233           0 :     } else if (argument.m_is_required) {
    1234           0 :       stream << "[required]";
    1235           0 :       add_space = true;
    1236             :     }
    1237           0 :     if (argument.m_is_repeatable) {
    1238           0 :       if (add_space) {
    1239           0 :         stream << " ";
    1240             :       }
    1241           0 :       stream << "[may be repeated]";
    1242             :     }
    1243           0 :     stream << "\n";
    1244           0 :     return stream;
    1245             :   }
    1246             : 
    1247             :   template <typename T> bool operator!=(const T &rhs) const {
    1248             :     return !(*this == rhs);
    1249             :   }
    1250             : 
    1251             :   /*
    1252             :    * Compare to an argument value of known type
    1253             :    * @throws std::logic_error in case of incompatible types
    1254             :    */
    1255             :   template <typename T> bool operator==(const T &rhs) const {
    1256             :     if constexpr (!details::IsContainer<T>) {
    1257             :       return get<T>() == rhs;
    1258             :     } else {
    1259             :       using ValueType = typename T::value_type;
    1260             :       auto lhs = get<T>();
    1261             :       return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
    1262             :                         std::end(rhs), [](const auto &a, const auto &b) {
    1263             :                           return std::any_cast<const ValueType &>(a) == b;
    1264             :                         });
    1265             :     }
    1266             :   }
    1267             : 
    1268             :   /*
    1269             :    * positional:
    1270             :    *    _empty_
    1271             :    *    '-'
    1272             :    *    '-' decimal-literal
    1273             :    *    !'-' anything
    1274             :    */
    1275      319139 :   static bool is_positional(std::string_view name,
    1276             :                             std::string_view prefix_chars) {
    1277      319139 :     auto first = lookahead(name);
    1278             : 
    1279      319139 :     if (first == eof) {
    1280           3 :       return true;
    1281             :     }
    1282      319136 :     if (prefix_chars.find(static_cast<char>(first)) !=
    1283             :                           std::string_view::npos) {
    1284      280395 :       name.remove_prefix(1);
    1285      280395 :       if (name.empty()) {
    1286           0 :         return true;
    1287             :       }
    1288      280395 :       return is_decimal_literal(name);
    1289             :     }
    1290       38741 :     return true;
    1291             :   }
    1292             : 
    1293             : private:
    1294             :   class NArgsRange {
    1295             :     std::size_t m_min;
    1296             :     std::size_t m_max;
    1297             : 
    1298             :   public:
    1299      492884 :     NArgsRange(std::size_t minimum, std::size_t maximum)
    1300      492884 :         : m_min(minimum), m_max(maximum) {
    1301      492884 :       if (minimum > maximum) {
    1302           0 :         throw std::logic_error("Range of number of arguments is invalid");
    1303             :       }
    1304      492884 :     }
    1305             : 
    1306        2394 :     bool contains(std::size_t value) const {
    1307        2394 :       return value >= m_min && value <= m_max;
    1308             :     }
    1309             : 
    1310           5 :     bool is_exact() const { return m_min == m_max; }
    1311             : 
    1312          21 :     bool is_right_bounded() const {
    1313          21 :       return m_max < (std::numeric_limits<std::size_t>::max)();
    1314             :     }
    1315             : 
    1316       27559 :     std::size_t get_min() const { return m_min; }
    1317             : 
    1318      133464 :     std::size_t get_max() const { return m_max; }
    1319             : 
    1320             :     // Print help message
    1321           0 :     friend auto operator<<(std::ostream &stream, const NArgsRange &range)
    1322             :         -> std::ostream & {
    1323           0 :       if (range.m_min == range.m_max) {
    1324           0 :         if (range.m_min != 0 && range.m_min != 1) {
    1325           0 :           stream << "[nargs: " << range.m_min << "] ";
    1326             :         }
    1327             :       } else {
    1328           0 :         if (range.m_max == (std::numeric_limits<std::size_t>::max)()) {
    1329           0 :           stream << "[nargs: " << range.m_min << " or more] ";
    1330             :         } else {
    1331           0 :           stream << "[nargs=" << range.m_min << ".." << range.m_max << "] ";
    1332             :         }
    1333             :       }
    1334           0 :       return stream;
    1335             :     }
    1336             : 
    1337           0 :     bool operator==(const NArgsRange &rhs) const {
    1338           0 :       return rhs.m_min == m_min && rhs.m_max == m_max;
    1339             :     }
    1340             : 
    1341           0 :     bool operator!=(const NArgsRange &rhs) const { return !(*this == rhs); }
    1342             :   };
    1343             : 
    1344           5 :   void throw_nargs_range_validation_error() const {
    1345          10 :     std::stringstream stream;
    1346           5 :     if (!m_used_name.empty()) {
    1347           0 :       stream << m_used_name << ": ";
    1348             :     } else {
    1349           5 :       stream << m_names.front() << ": ";
    1350             :     }
    1351           5 :     if (m_num_args_range.is_exact()) {
    1352           5 :       stream << m_num_args_range.get_min();
    1353           0 :     } else if (m_num_args_range.is_right_bounded()) {
    1354           0 :       stream << m_num_args_range.get_min() << " to "
    1355           0 :              << m_num_args_range.get_max();
    1356             :     } else {
    1357           0 :       stream << m_num_args_range.get_min() << " or more";
    1358             :     }
    1359           5 :     stream << " argument(s) expected. " << m_values.size() << " provided.";
    1360           5 :     throw std::runtime_error(stream.str());
    1361             :   }
    1362             : 
    1363           0 :   void throw_required_arg_not_used_error() const {
    1364           0 :     std::stringstream stream;
    1365           0 :     stream << m_names.front() << ": required.";
    1366           0 :     throw std::runtime_error(stream.str());
    1367             :   }
    1368             : 
    1369           0 :   void throw_required_arg_no_value_provided_error() const {
    1370           0 :     std::stringstream stream;
    1371           0 :     stream << m_used_name << ": no value provided.";
    1372           0 :     throw std::runtime_error(stream.str());
    1373             :   }
    1374             : 
    1375             :   static constexpr int eof = std::char_traits<char>::eof();
    1376             : 
    1377      602411 :   static auto lookahead(std::string_view s) -> int {
    1378      602411 :     if (s.empty()) {
    1379         683 :       return eof;
    1380             :     }
    1381      601728 :     return static_cast<int>(static_cast<unsigned char>(s[0]));
    1382             :   }
    1383             : 
    1384             :   /*
    1385             :    * decimal-literal:
    1386             :    *    '0'
    1387             :    *    nonzero-digit digit-sequence_opt
    1388             :    *    integer-part fractional-part
    1389             :    *    fractional-part
    1390             :    *    integer-part '.' exponent-part_opt
    1391             :    *    integer-part exponent-part
    1392             :    *
    1393             :    * integer-part:
    1394             :    *    digit-sequence
    1395             :    *
    1396             :    * fractional-part:
    1397             :    *    '.' post-decimal-point
    1398             :    *
    1399             :    * post-decimal-point:
    1400             :    *    digit-sequence exponent-part_opt
    1401             :    *
    1402             :    * exponent-part:
    1403             :    *    'e' post-e
    1404             :    *    'E' post-e
    1405             :    *
    1406             :    * post-e:
    1407             :    *    sign_opt digit-sequence
    1408             :    *
    1409             :    * sign: one of
    1410             :    *    '+' '-'
    1411             :    */
    1412      280395 :   static bool is_decimal_literal(std::string_view s) {
    1413      280395 :     if (s == "inf") {
    1414           2 :       return true;
    1415             :     }
    1416             : 
    1417       13450 :     auto is_digit = [](auto c) constexpr {
    1418       13450 :       switch (c) {
    1419       12635 :       case '0':
    1420             :       case '1':
    1421             :       case '2':
    1422             :       case '3':
    1423             :       case '4':
    1424             :       case '5':
    1425             :       case '6':
    1426             :       case '7':
    1427             :       case '8':
    1428             :       case '9':
    1429       12635 :         return true;
    1430         815 :       default:
    1431         815 :         return false;
    1432             :       }
    1433             :     };
    1434             : 
    1435             :     // precondition: we have consumed or will consume at least one digit
    1436        1717 :     auto consume_digits = [=](std::string_view sd) {
    1437             :       // NOLINTNEXTLINE(readability-qualified-auto)
    1438        1717 :       auto it = std::find_if_not(std::begin(sd), std::end(sd), is_digit);
    1439        1717 :       return sd.substr(static_cast<std::size_t>(it - std::begin(sd)));
    1440             :     };
    1441             : 
    1442      280393 :     switch (lookahead(s)) {
    1443          48 :     case '0': {
    1444          48 :       s.remove_prefix(1);
    1445          48 :       if (s.empty()) {
    1446           0 :         return true;
    1447             :       }
    1448          48 :       goto integer_part;
    1449             :     }
    1450         977 :     case '1':
    1451             :     case '2':
    1452             :     case '3':
    1453             :     case '4':
    1454             :     case '5':
    1455             :     case '6':
    1456             :     case '7':
    1457             :     case '8':
    1458             :     case '9': {
    1459         977 :       s = consume_digits(s);
    1460         977 :       if (s.empty()) {
    1461         216 :         return true;
    1462             :       }
    1463         761 :       goto integer_part_consumed;
    1464             :     }
    1465           0 :     case '.': {
    1466           0 :       s.remove_prefix(1);
    1467           0 :       goto post_decimal_point;
    1468             :     }
    1469      279368 :     default:
    1470      279368 :       return false;
    1471             :     }
    1472             : 
    1473          48 :   integer_part:
    1474          48 :     s = consume_digits(s);
    1475         809 :   integer_part_consumed:
    1476         809 :     switch (lookahead(s)) {
    1477         686 :     case '.': {
    1478         686 :       s.remove_prefix(1);
    1479         686 :       if (is_digit(lookahead(s))) {
    1480         686 :         goto post_decimal_point;
    1481             :       } else {
    1482           0 :         goto exponent_part_opt;
    1483             :       }
    1484             :     }
    1485           0 :     case 'e':
    1486             :     case 'E': {
    1487           0 :       s.remove_prefix(1);
    1488           0 :       goto post_e;
    1489             :     }
    1490         123 :     default:
    1491         123 :       return false;
    1492             :     }
    1493             : 
    1494         686 :   post_decimal_point:
    1495         686 :     if (is_digit(lookahead(s))) {
    1496         686 :       s = consume_digits(s);
    1497         686 :       goto exponent_part_opt;
    1498             :     }
    1499           0 :     return false;
    1500             : 
    1501         686 :   exponent_part_opt:
    1502         686 :     switch (lookahead(s)) {
    1503         680 :     case eof:
    1504         680 :       return true;
    1505           6 :     case 'e':
    1506             :     case 'E': {
    1507           6 :       s.remove_prefix(1);
    1508           6 :       goto post_e;
    1509             :     }
    1510           0 :     default:
    1511           0 :       return false;
    1512             :     }
    1513             : 
    1514           6 :   post_e:
    1515           6 :     switch (lookahead(s)) {
    1516           6 :     case '-':
    1517             :     case '+':
    1518           6 :       s.remove_prefix(1);
    1519             :     }
    1520           6 :     if (is_digit(lookahead(s))) {
    1521           6 :       s = consume_digits(s);
    1522           6 :       return s.empty();
    1523             :     }
    1524           0 :     return false;
    1525             :   }
    1526             : 
    1527      291425 :   static bool is_optional(std::string_view name,
    1528             :                           std::string_view prefix_chars) {
    1529      291425 :     return !is_positional(name, prefix_chars);
    1530             :   }
    1531             : 
    1532             :   /*
    1533             :    * Get argument value given a type
    1534             :    * @throws std::logic_error in case of incompatible types
    1535             :    */
    1536           4 :   template <typename T> T get() const {
    1537           4 :     if (!m_values.empty()) {
    1538             :       if constexpr (details::IsContainer<T>) {
    1539             :         return any_cast_container<T>(m_values);
    1540             :       } else {
    1541           4 :         return std::any_cast<T>(m_values.front());
    1542             :       }
    1543             :     }
    1544           0 :     if (m_default_value.has_value()) {
    1545           0 :       return std::any_cast<T>(m_default_value);
    1546             :     }
    1547             :     if constexpr (details::IsContainer<T>) {
    1548             :       if (!m_accepts_optional_like_value) {
    1549             :         return any_cast_container<T>(m_values);
    1550             :       }
    1551             :     }
    1552             : 
    1553           0 :     throw std::logic_error("No value provided for '" + m_names.back() + "'.");
    1554             :   }
    1555             : 
    1556             :   /*
    1557             :    * Get argument value given a type.
    1558             :    * @pre The object has no default value.
    1559             :    * @returns The stored value if any, std::nullopt otherwise.
    1560             :    */
    1561       20997 :   template <typename T> auto present() const -> std::optional<T> {
    1562       20997 :     if (m_default_value.has_value()) {
    1563           0 :       throw std::logic_error("Argument with default value always presents");
    1564             :     }
    1565       20997 :     if (m_values.empty()) {
    1566       19008 :       return std::nullopt;
    1567             :     }
    1568             :     if constexpr (details::IsContainer<T>) {
    1569        1989 :       return any_cast_container<T>(m_values);
    1570             :     }
    1571             :     return std::any_cast<T>(m_values.front());
    1572             :   }
    1573             : 
    1574             :   template <typename T>
    1575        1989 :   static auto any_cast_container(const std::vector<std::any> &operand) -> T {
    1576             :     using ValueType = typename T::value_type;
    1577             : 
    1578        1989 :     T result;
    1579        1989 :     std::transform(
    1580             :         std::begin(operand), std::end(operand), std::back_inserter(result),
    1581        6383 :         [](const auto &value) { return std::any_cast<ValueType>(value); });
    1582        1989 :     return result;
    1583             :   }
    1584             : 
    1585      297430 :   void set_usage_newline_counter(int i) { m_usage_newline_counter = i; }
    1586             : 
    1587      297430 :   void set_group_idx(std::size_t i) { m_group_idx = i; }
    1588             : 
    1589             :   std::vector<std::string> m_names;
    1590             :   std::string_view m_used_name;
    1591             :   std::string m_help;
    1592             :   std::string m_metavar;
    1593             :   std::any m_default_value;
    1594             :   std::string m_default_value_repr;
    1595             :   std::optional<std::string>
    1596             :       m_default_value_str; // used for checking default_value against choices
    1597             :   std::any m_implicit_value;
    1598             :   std::optional<std::vector<std::string>> m_choices{std::nullopt};
    1599             :   using valued_action = std::function<std::any(const std::string &)>;
    1600             :   using void_action = std::function<void(const std::string &)>;
    1601             :   std::vector<std::variant<valued_action, void_action>> m_actions;
    1602             :   std::variant<valued_action, void_action> m_default_action{
    1603             :     std::in_place_type<valued_action>,
    1604         522 :     [](const std::string &value) { return value; }};
    1605             :   std::vector<std::any> m_values;
    1606             :   NArgsRange m_num_args_range{1, 1};
    1607             :   // Bit field of bool values. Set default value in ctor.
    1608             :   bool m_accepts_optional_like_value : 1;
    1609             :   bool m_is_optional : 1;
    1610             :   bool m_is_required : 1;
    1611             :   bool m_is_repeatable : 1;
    1612             :   bool m_is_used : 1;
    1613             :   bool m_is_hidden : 1;            // if set, does not appear in usage or help
    1614             :   std::string_view m_prefix_chars; // ArgumentParser has the prefix_chars
    1615             :   int m_usage_newline_counter = 0;
    1616             :   std::size_t m_group_idx = 0;
    1617             : };
    1618             : 
    1619             : class ArgumentParser {
    1620             : public:
    1621        5860 :   explicit ArgumentParser(std::string program_name = {},
    1622             :                           std::string version = "1.0",
    1623             :                           default_arguments add_args = default_arguments::all,
    1624             :                           bool exit_on_default_arguments = true,
    1625             :                           std::ostream &os = std::cout)
    1626       11720 :       : m_program_name(std::move(program_name)), m_version(std::move(version)),
    1627             :         m_exit_on_default_arguments(exit_on_default_arguments),
    1628        5860 :         m_parser_path(m_program_name) {
    1629        5860 :     if ((add_args & default_arguments::help) == default_arguments::help) {
    1630           0 :       add_argument("-h", "--help")
    1631           0 :           .action([&](const auto & /*unused*/) {
    1632           0 :             os << help().str();
    1633           0 :             if (m_exit_on_default_arguments) {
    1634           0 :               std::exit(0);
    1635             :             }
    1636           0 :           })
    1637           0 :           .default_value(false)
    1638           0 :           .help("shows help message and exits")
    1639           0 :           .implicit_value(true)
    1640           0 :           .nargs(0);
    1641             :     }
    1642        5860 :     if ((add_args & default_arguments::version) == default_arguments::version) {
    1643           0 :       add_argument("-v", "--version")
    1644           0 :           .action([&](const auto & /*unused*/) {
    1645           0 :             os << m_version << std::endl;
    1646           0 :             if (m_exit_on_default_arguments) {
    1647           0 :               std::exit(0);
    1648             :             }
    1649           0 :           })
    1650           0 :           .default_value(false)
    1651           0 :           .help("prints version information and exits")
    1652           0 :           .implicit_value(true)
    1653           0 :           .nargs(0);
    1654             :     }
    1655        5860 :   }
    1656             : 
    1657        5746 :   ~ArgumentParser() = default;
    1658             : 
    1659             :   // ArgumentParser is meant to be used in a single function.
    1660             :   // Setup everything and parse arguments in one place.
    1661             :   //
    1662             :   // ArgumentParser internally uses std::string_views,
    1663             :   // references, iterators, etc.
    1664             :   // Many of these elements become invalidated after a copy or move.
    1665             :   ArgumentParser(const ArgumentParser &other) = delete;
    1666             :   ArgumentParser &operator=(const ArgumentParser &other) = delete;
    1667             :   ArgumentParser(ArgumentParser &&) noexcept = delete;
    1668             :   ArgumentParser &operator=(ArgumentParser &&) = delete;
    1669             : 
    1670             :   explicit operator bool() const {
    1671             :     auto arg_used = std::any_of(m_argument_map.cbegin(), m_argument_map.cend(),
    1672             :                                 [](auto &it) { return it.second->m_is_used; });
    1673             :     auto subparser_used =
    1674             :         std::any_of(m_subparser_used.cbegin(), m_subparser_used.cend(),
    1675             :                     [](auto &it) { return it.second; });
    1676             : 
    1677             :     return m_is_parsed && (arg_used || subparser_used);
    1678             :   }
    1679             : 
    1680             :   // Parameter packing
    1681             :   // Call add_argument with variadic number of string arguments
    1682      259078 :   template <typename... Targs> Argument &add_argument(Targs... f_args) {
    1683             :     using array_of_sv = std::array<std::string_view, sizeof...(Targs)>;
    1684      518156 :     auto argument =
    1685      259078 :         m_optional_arguments.emplace(std::cend(m_optional_arguments),
    1686      259078 :                                      m_prefix_chars, array_of_sv{f_args...});
    1687             : 
    1688      259078 :     if (!argument->m_is_optional) {
    1689        5638 :       m_positional_arguments.splice(std::cend(m_positional_arguments),
    1690        2819 :                                     m_optional_arguments, argument);
    1691             :     }
    1692      259078 :     argument->set_usage_newline_counter(m_usage_newline_counter);
    1693      259078 :     argument->set_group_idx(m_group_names.size());
    1694             : 
    1695      259078 :     index_argument(argument);
    1696      518156 :     return *argument;
    1697             :   }
    1698             : 
    1699             :   class MutuallyExclusiveGroup {
    1700             :     friend class ArgumentParser;
    1701             : 
    1702             :   public:
    1703             :     MutuallyExclusiveGroup() = delete;
    1704             : 
    1705       16976 :     explicit MutuallyExclusiveGroup(ArgumentParser &parent,
    1706             :                                     bool required = false)
    1707       16976 :         : m_parent(parent), m_required(required), m_elements({}) {}
    1708             : 
    1709             :     MutuallyExclusiveGroup(const MutuallyExclusiveGroup &other) = delete;
    1710             :     MutuallyExclusiveGroup &
    1711             :     operator=(const MutuallyExclusiveGroup &other) = delete;
    1712             : 
    1713       12853 :     MutuallyExclusiveGroup(MutuallyExclusiveGroup &&other) noexcept
    1714       12853 :         : m_parent(other.m_parent), m_required(other.m_required),
    1715       12853 :           m_elements(std::move(other.m_elements)) {
    1716       12853 :       other.m_elements.clear();
    1717       12853 :     }
    1718             : 
    1719       38352 :     template <typename... Targs> Argument &add_argument(Targs... f_args) {
    1720       38352 :       auto &argument = m_parent.add_argument(std::forward<Targs>(f_args)...);
    1721       38352 :       m_elements.push_back(&argument);
    1722       38352 :       argument.set_usage_newline_counter(m_parent.m_usage_newline_counter);
    1723       38352 :       argument.set_group_idx(m_parent.m_group_names.size());
    1724       38352 :       return argument;
    1725             :     }
    1726             : 
    1727             :   private:
    1728             :     ArgumentParser &m_parent;
    1729             :     bool m_required{false};
    1730             :     std::vector<Argument *> m_elements{};
    1731             :   };
    1732             : 
    1733       16976 :   MutuallyExclusiveGroup &add_mutually_exclusive_group(bool required = false) {
    1734       16976 :     m_mutually_exclusive_groups.emplace_back(*this, required);
    1735       16976 :     return m_mutually_exclusive_groups.back();
    1736             :   }
    1737             : 
    1738             :   // Parameter packed add_parents method
    1739             :   // Accepts a variadic number of ArgumentParser objects
    1740             :   template <typename... Targs>
    1741             :   ArgumentParser &add_parents(const Targs &... f_args) {
    1742             :     for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) {
    1743             :       for (const auto &argument : parent_parser.m_positional_arguments) {
    1744             :         auto it = m_positional_arguments.insert(
    1745             :             std::cend(m_positional_arguments), argument);
    1746             :         index_argument(it);
    1747             :       }
    1748             :       for (const auto &argument : parent_parser.m_optional_arguments) {
    1749             :         auto it = m_optional_arguments.insert(std::cend(m_optional_arguments),
    1750             :                                               argument);
    1751             :         index_argument(it);
    1752             :       }
    1753             :     }
    1754             :     return *this;
    1755             :   }
    1756             : 
    1757             :   // Ask for the next optional arguments to be displayed on a separate
    1758             :   // line in usage() output. Only effective if set_usage_max_line_width() is
    1759             :   // also used.
    1760        8833 :   ArgumentParser &add_usage_newline() {
    1761        8833 :     ++m_usage_newline_counter;
    1762        8833 :     return *this;
    1763             :   }
    1764             : 
    1765             :   // Ask for the next optional arguments to be displayed in a separate section
    1766             :   // in usage() and help (<< *this) output.
    1767             :   // For usage(), this is only effective if set_usage_max_line_width() is
    1768             :   // also used.
    1769        3504 :   ArgumentParser &add_group(std::string group_name) {
    1770        3504 :     m_group_names.emplace_back(std::move(group_name));
    1771        3504 :     return *this;
    1772             :   }
    1773             : 
    1774        5860 :   ArgumentParser &add_description(std::string description) {
    1775        5860 :     m_description = std::move(description);
    1776        5860 :     return *this;
    1777             :   }
    1778             : 
    1779        5265 :   ArgumentParser &add_epilog(std::string epilog) {
    1780        5265 :     m_epilog = std::move(epilog);
    1781        5265 :     return *this;
    1782             :   }
    1783             : 
    1784             :   // Add a un-documented/hidden alias for an argument.
    1785             :   // Ideally we'd want this to be a method of Argument, but Argument
    1786             :   // does not own its owing ArgumentParser.
    1787        7460 :   ArgumentParser &add_hidden_alias_for(Argument &arg, std::string_view alias) {
    1788       91105 :     for (auto it = m_optional_arguments.begin();
    1789      174750 :          it != m_optional_arguments.end(); ++it) {
    1790       91105 :       if (&(*it) == &arg) {
    1791        7460 :         m_argument_map.insert_or_assign(std::string(alias), it);
    1792        7460 :         return *this;
    1793             :       }
    1794             :     }
    1795             :     throw std::logic_error(
    1796           0 :         "Argument is not an optional argument of this parser");
    1797             :   }
    1798             : 
    1799             :   /* Getter for arguments and subparsers.
    1800             :    * @throws std::logic_error in case of an invalid argument or subparser name
    1801             :    */
    1802             :   template <typename T = Argument> T &at(std::string_view name) {
    1803             :     if constexpr (std::is_same_v<T, Argument>) {
    1804             :       return (*this)[name];
    1805             :     } else {
    1806             :       std::string str_name(name);
    1807             :       auto subparser_it = m_subparser_map.find(str_name);
    1808             :       if (subparser_it != m_subparser_map.end()) {
    1809             :         return subparser_it->second->get();
    1810             :       }
    1811             :       throw std::logic_error("No such subparser: " + str_name);
    1812             :     }
    1813             :   }
    1814             : 
    1815             :   ArgumentParser &set_prefix_chars(std::string prefix_chars) {
    1816             :     m_prefix_chars = std::move(prefix_chars);
    1817             :     return *this;
    1818             :   }
    1819             : 
    1820             :   ArgumentParser &set_assign_chars(std::string assign_chars) {
    1821             :     m_assign_chars = std::move(assign_chars);
    1822             :     return *this;
    1823             :   }
    1824             : 
    1825             :   /* Call parse_args_internal - which does all the work
    1826             :    * Then, validate the parsed arguments
    1827             :    * This variant is used mainly for testing
    1828             :    * @throws std::runtime_error in case of any invalid argument
    1829             :    */
    1830        5249 :   void parse_args(const std::vector<std::string> &arguments) {
    1831        5249 :     parse_args_internal(arguments);
    1832             :     // Check if all arguments are parsed
    1833      271533 :     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
    1834      266321 :       argument->validate();
    1835             :     }
    1836             : 
    1837             :     // Check each mutually exclusive group and make sure
    1838             :     // there are no constraint violations
    1839       22012 :     for (const auto &group : m_mutually_exclusive_groups) {
    1840       16803 :       auto mutex_argument_used{false};
    1841       16803 :       Argument *mutex_argument_it{nullptr};
    1842       54738 :       for (Argument *arg : group.m_elements) {
    1843       37938 :         if (!mutex_argument_used && arg->m_is_used) {
    1844         665 :           mutex_argument_used = true;
    1845         665 :           mutex_argument_it = arg;
    1846       37273 :         } else if (mutex_argument_used && arg->m_is_used) {
    1847             :           // Violation
    1848           6 :           throw std::runtime_error("Argument '" + arg->get_usage_full() +
    1849           6 :                                    "' not allowed with '" +
    1850           9 :                                    mutex_argument_it->get_usage_full() + "'");
    1851             :         }
    1852             :       }
    1853             : 
    1854       16800 :       if (!mutex_argument_used && group.m_required) {
    1855             :         // at least one argument from the group is
    1856             :         // required
    1857           0 :         std::string argument_names{};
    1858           0 :         std::size_t i = 0;
    1859           0 :         std::size_t size = group.m_elements.size();
    1860           0 :         for (Argument *arg : group.m_elements) {
    1861           0 :           if (i + 1 == size) {
    1862             :             // last
    1863           0 :             argument_names += "'" + arg->get_usage_full() + "' ";
    1864             :           } else {
    1865           0 :             argument_names += "'" + arg->get_usage_full() + "' or ";
    1866             :           }
    1867           0 :           i += 1;
    1868             :         }
    1869           0 :         throw std::runtime_error("One of the arguments " + argument_names +
    1870           0 :                                  "is required");
    1871             :       }
    1872             :     }
    1873        5209 :   }
    1874             : 
    1875             :   /* Call parse_known_args_internal - which does all the work
    1876             :    * Then, validate the parsed arguments
    1877             :    * This variant is used mainly for testing
    1878             :    * @throws std::runtime_error in case of any invalid argument
    1879             :    */
    1880             :   std::vector<std::string>
    1881             :   parse_known_args(const std::vector<std::string> &arguments) {
    1882             :     auto unknown_arguments = parse_known_args_internal(arguments);
    1883             :     // Check if all arguments are parsed
    1884             :     for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
    1885             :       argument->validate();
    1886             :     }
    1887             :     return unknown_arguments;
    1888             :   }
    1889             : 
    1890             :   /* Main entry point for parsing command-line arguments using this
    1891             :    * ArgumentParser
    1892             :    * @throws std::runtime_error in case of any invalid argument
    1893             :    */
    1894             :   // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
    1895             :   void parse_args(int argc, const char *const argv[]) {
    1896             :     parse_args({argv, argv + argc});
    1897             :   }
    1898             : 
    1899             :   /* Main entry point for parsing command-line arguments using this
    1900             :    * ArgumentParser
    1901             :    * @throws std::runtime_error in case of any invalid argument
    1902             :    */
    1903             :   // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
    1904             :   auto parse_known_args(int argc, const char *const argv[]) {
    1905             :     return parse_known_args({argv, argv + argc});
    1906             :   }
    1907             : 
    1908             :   /* Getter for options with default values.
    1909             :    * @throws std::logic_error if parse_args() has not been previously called
    1910             :    * @throws std::logic_error if there is no such option
    1911             :    * @throws std::logic_error if the option has no value
    1912             :    * @throws std::bad_any_cast if the option is not of type T
    1913             :    */
    1914           4 :   template <typename T = std::string> T get(std::string_view arg_name) const {
    1915           4 :     if (!m_is_parsed) {
    1916           0 :       throw std::logic_error("Nothing parsed, no arguments are available.");
    1917             :     }
    1918           4 :     return (*this)[arg_name].get<T>();
    1919             :   }
    1920             : 
    1921             :   /* Getter for options without default values.
    1922             :    * @pre The option has no default value.
    1923             :    * @throws std::logic_error if there is no such option
    1924             :    * @throws std::bad_any_cast if the option is not of type T
    1925             :    */
    1926             :   template <typename T = std::string>
    1927       20997 :   auto present(std::string_view arg_name) const -> std::optional<T> {
    1928       20997 :     return (*this)[arg_name].present<T>();
    1929             :   }
    1930             : 
    1931             :   /* Getter that returns true for user-supplied options. Returns false if not
    1932             :    * user-supplied, even with a default value.
    1933             :    */
    1934        4997 :   auto is_used(std::string_view arg_name) const {
    1935        4997 :     return (*this)[arg_name].m_is_used;
    1936             :   }
    1937             : 
    1938             :   /* Getter that returns true if a subcommand is used.
    1939             :    */
    1940          30 :   auto is_subcommand_used(std::string_view subcommand_name) const {
    1941          30 :     return m_subparser_used.at(std::string(subcommand_name));
    1942             :   }
    1943             : 
    1944             :   /* Getter that returns true if a subcommand is used.
    1945             :    */
    1946             :   auto is_subcommand_used(const ArgumentParser &subparser) const {
    1947             :     return is_subcommand_used(subparser.m_program_name);
    1948             :   }
    1949             : 
    1950             :   /* Indexing operator. Return a reference to an Argument object
    1951             :    * Used in conjunction with Argument.operator== e.g., parser["foo"] == true
    1952             :    * @throws std::logic_error in case of an invalid argument name
    1953             :    */
    1954       25998 :   Argument &operator[](std::string_view arg_name) const {
    1955       51996 :     std::string name(arg_name);
    1956       25998 :     auto it = m_argument_map.find(name);
    1957       25998 :     if (it != m_argument_map.end()) {
    1958       25978 :       return *(it->second);
    1959             :     }
    1960          20 :     if (!is_valid_prefix_char(arg_name.front())) {
    1961           0 :       const auto legal_prefix_char = get_any_valid_prefix_char();
    1962           0 :       const auto prefix = std::string(1, legal_prefix_char);
    1963             : 
    1964             :       // "-" + arg_name
    1965           0 :       name = prefix + name;
    1966           0 :       it = m_argument_map.find(name);
    1967           0 :       if (it != m_argument_map.end()) {
    1968           0 :         return *(it->second);
    1969             :       }
    1970             :       // "--" + arg_name
    1971           0 :       name = prefix + name;
    1972           0 :       it = m_argument_map.find(name);
    1973           0 :       if (it != m_argument_map.end()) {
    1974           0 :         return *(it->second);
    1975             :       }
    1976             :     }
    1977          20 :     throw std::logic_error("No such argument: " + std::string(arg_name));
    1978             :   }
    1979             : 
    1980             :   // Print help message
    1981           0 :   friend auto operator<<(std::ostream &stream, const ArgumentParser &parser)
    1982             :       -> std::ostream & {
    1983           0 :     stream.setf(std::ios_base::left);
    1984             : 
    1985           0 :     auto longest_arg_length = parser.get_length_of_longest_argument();
    1986             : 
    1987           0 :     stream << parser.usage() << "\n\n";
    1988             : 
    1989           0 :     if (!parser.m_description.empty()) {
    1990           0 :       stream << parser.m_description << "\n\n";
    1991             :     }
    1992             : 
    1993           0 :     const bool has_visible_positional_args = std::find_if(
    1994             :       parser.m_positional_arguments.begin(),
    1995             :       parser.m_positional_arguments.end(),
    1996           0 :       [](const auto &argument) {
    1997           0 :       return !argument.m_is_hidden; }) !=
    1998           0 :       parser.m_positional_arguments.end();
    1999           0 :     if (has_visible_positional_args) {
    2000           0 :       stream << "Positional arguments:\n";
    2001             :     }
    2002             : 
    2003           0 :     for (const auto &argument : parser.m_positional_arguments) {
    2004           0 :       if (!argument.m_is_hidden) {
    2005           0 :         stream.width(static_cast<std::streamsize>(longest_arg_length));
    2006           0 :         stream << argument;
    2007             :       }
    2008             :     }
    2009             : 
    2010           0 :     if (!parser.m_optional_arguments.empty()) {
    2011             :       stream << (!has_visible_positional_args ? "" : "\n")
    2012           0 :              << "Optional arguments:\n";
    2013             :     }
    2014             : 
    2015           0 :     for (const auto &argument : parser.m_optional_arguments) {
    2016           0 :       if (argument.m_group_idx == 0 && !argument.m_is_hidden) {
    2017           0 :         stream.width(static_cast<std::streamsize>(longest_arg_length));
    2018           0 :         stream << argument;
    2019             :       }
    2020             :     }
    2021             : 
    2022           0 :     for (size_t i_group = 0; i_group < parser.m_group_names.size(); ++i_group) {
    2023           0 :       stream << "\n" << parser.m_group_names[i_group] << " (detailed usage):\n";
    2024           0 :       for (const auto &argument : parser.m_optional_arguments) {
    2025           0 :         if (argument.m_group_idx == i_group + 1 && !argument.m_is_hidden) {
    2026           0 :           stream.width(static_cast<std::streamsize>(longest_arg_length));
    2027           0 :           stream << argument;
    2028             :         }
    2029             :       }
    2030             :     }
    2031             : 
    2032           0 :     bool has_visible_subcommands = std::any_of(
    2033             :         parser.m_subparser_map.begin(), parser.m_subparser_map.end(),
    2034           0 :         [](auto &p) { return !p.second->get().m_suppress; });
    2035             : 
    2036           0 :     if (has_visible_subcommands) {
    2037           0 :       stream << (parser.m_positional_arguments.empty()
    2038           0 :                      ? (parser.m_optional_arguments.empty() ? "" : "\n")
    2039             :                      : "\n")
    2040           0 :              << "Subcommands:\n";
    2041           0 :       for (const auto &[command, subparser] : parser.m_subparser_map) {
    2042           0 :         if (subparser->get().m_suppress) {
    2043           0 :           continue;
    2044             :         }
    2045             : 
    2046           0 :         stream << std::setw(2) << " ";
    2047           0 :         if (longest_arg_length >= 2) {
    2048           0 :           stream << std::setw(static_cast<int>(longest_arg_length - 2))
    2049           0 :                  << command;
    2050             :         }
    2051           0 :         stream << " " << subparser->get().m_description << "\n";
    2052             :       }
    2053             :     }
    2054             : 
    2055           0 :     if (!parser.m_epilog.empty()) {
    2056           0 :       stream << '\n';
    2057           0 :       stream << parser.m_epilog << "\n\n";
    2058             :     }
    2059             : 
    2060           0 :     return stream;
    2061             :   }
    2062             : 
    2063             :   // Format help message
    2064           0 :   auto help() const -> std::stringstream {
    2065           0 :     std::stringstream out;
    2066           0 :     out << *this;
    2067           0 :     return out;
    2068             :   }
    2069             : 
    2070             :   // Sets the maximum width for a line of the Usage message
    2071        5860 :   ArgumentParser &set_usage_max_line_width(size_t w) {
    2072        5860 :     this->m_usage_max_line_width = w;
    2073        5860 :     return *this;
    2074             :   }
    2075             : 
    2076             :   // Asks to display arguments of mutually exclusive group on separate lines in
    2077             :   // the Usage message
    2078        5860 :   ArgumentParser &set_usage_break_on_mutex() {
    2079        5860 :     this->m_usage_break_on_mutex = true;
    2080        5860 :     return *this;
    2081             :   }
    2082             : 
    2083             :   // Format usage part of help only
    2084          21 :   auto usage() const -> std::string {
    2085          42 :     std::stringstream stream;
    2086             : 
    2087          42 :     std::string curline("Usage: ");
    2088          21 :     curline += this->m_parser_path;
    2089             :     const bool multiline_usage =
    2090          21 :         this->m_usage_max_line_width < std::numeric_limits<std::size_t>::max();
    2091          21 :     const size_t indent_size = curline.size();
    2092             : 
    2093          26 :     const auto deal_with_options_of_group = [&](std::size_t group_idx) {
    2094          26 :       bool found_options = false;
    2095             :       // Add any options inline here
    2096          26 :       const MutuallyExclusiveGroup *cur_mutex = nullptr;
    2097          26 :       int usage_newline_counter = -1;
    2098        1025 :       for (const auto &argument : this->m_optional_arguments) {
    2099         999 :         if (argument.m_is_hidden) {
    2100         441 :           continue;
    2101             :         }
    2102         898 :         if (multiline_usage) {
    2103         898 :           if (argument.m_group_idx != group_idx) {
    2104         340 :             continue;
    2105             :           }
    2106         558 :           if (usage_newline_counter != argument.m_usage_newline_counter) {
    2107          39 :             if (usage_newline_counter >= 0) {
    2108          13 :               if (curline.size() > indent_size) {
    2109          13 :                 stream << curline << std::endl;
    2110          13 :                 curline = std::string(indent_size, ' ');
    2111             :               }
    2112             :             }
    2113          39 :             usage_newline_counter = argument.m_usage_newline_counter;
    2114             :           }
    2115             :         }
    2116         558 :         found_options = true;
    2117        1116 :         const std::string arg_inline_usage = argument.get_inline_usage();
    2118             :         const MutuallyExclusiveGroup *arg_mutex =
    2119         558 :             get_belonging_mutex(&argument);
    2120         558 :         if ((cur_mutex != nullptr) && (arg_mutex == nullptr)) {
    2121          27 :           curline += ']';
    2122          27 :           if (this->m_usage_break_on_mutex) {
    2123          27 :             stream << curline << std::endl;
    2124          27 :             curline = std::string(indent_size, ' ');
    2125             :           }
    2126         531 :         } else if ((cur_mutex == nullptr) && (arg_mutex != nullptr)) {
    2127          29 :           if ((this->m_usage_break_on_mutex && curline.size() > indent_size) ||
    2128           1 :               curline.size() + 3 + arg_inline_usage.size() >
    2129           1 :                   this->m_usage_max_line_width) {
    2130          27 :             stream << curline << std::endl;
    2131          27 :             curline = std::string(indent_size, ' ');
    2132             :           }
    2133          28 :           curline += " [";
    2134         503 :         } else if ((cur_mutex != nullptr) && (arg_mutex != nullptr)) {
    2135          43 :           if (cur_mutex != arg_mutex) {
    2136           4 :             curline += ']';
    2137           4 :             if (this->m_usage_break_on_mutex ||
    2138           0 :                 curline.size() + 3 + arg_inline_usage.size() >
    2139           0 :                     this->m_usage_max_line_width) {
    2140           4 :               stream << curline << std::endl;
    2141           4 :               curline = std::string(indent_size, ' ');
    2142             :             }
    2143           4 :             curline += " [";
    2144             :           } else {
    2145          39 :             curline += '|';
    2146             :           }
    2147             :         }
    2148         558 :         cur_mutex = arg_mutex;
    2149        1051 :         if (curline.size() != indent_size &&
    2150         493 :             curline.size() + 1 + arg_inline_usage.size() >
    2151         493 :             this->m_usage_max_line_width) {
    2152         198 :           stream << curline << std::endl;
    2153         198 :           curline = std::string(indent_size, ' ');
    2154         198 :           curline += " ";
    2155         360 :         } else if (cur_mutex == nullptr) {
    2156         297 :           curline += " ";
    2157             :         }
    2158         558 :         curline += arg_inline_usage;
    2159             :       }
    2160          26 :       if (cur_mutex != nullptr) {
    2161           1 :         curline += ']';
    2162             :       }
    2163          26 :       return found_options;
    2164          21 :     };
    2165             : 
    2166          21 :     const bool found_options = deal_with_options_of_group(0);
    2167             : 
    2168          42 :     if (found_options && multiline_usage &&
    2169          21 :         !this->m_positional_arguments.empty()) {
    2170          10 :       stream << curline << std::endl;
    2171          10 :       curline = std::string(indent_size, ' ');
    2172             :     }
    2173             :     // Put positional arguments after the optionals
    2174          42 :     for (const auto &argument : this->m_positional_arguments) {
    2175          21 :       if (argument.m_is_hidden) {
    2176           0 :         continue;
    2177             :       }
    2178          21 :       const std::string pos_arg = !argument.m_metavar.empty()
    2179             :                                       ? argument.m_metavar
    2180          42 :                                       : argument.m_names.front();
    2181          21 :       if (curline.size() + 1 + pos_arg.size() > this->m_usage_max_line_width) {
    2182           4 :         stream << curline << std::endl;
    2183           4 :         curline = std::string(indent_size, ' ');
    2184             :       }
    2185          21 :       curline += " ";
    2186          24 :       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          36 :       } else if (argument.m_num_args_range.get_min() == 1 &&
    2192          18 :                  !argument.m_num_args_range.is_right_bounded()) {
    2193           4 :         curline += pos_arg;
    2194           4 :         curline += "...";
    2195             :       } else {
    2196          14 :         curline += pos_arg;
    2197             :       }
    2198             :     }
    2199             : 
    2200          21 :     if (multiline_usage) {
    2201             :       // Display options of other groups
    2202          26 :       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          21 :     stream << curline;
    2211             : 
    2212             :     // Put subcommands after positional arguments
    2213          21 :     if (!m_subparser_map.empty()) {
    2214           5 :       stream << " {";
    2215           5 :       std::size_t i{0};
    2216          28 :       for (const auto &[command, subparser] : m_subparser_map) {
    2217          23 :         if (subparser->get().m_suppress) {
    2218           0 :           continue;
    2219             :         }
    2220             : 
    2221          23 :         if (i == 0) {
    2222           5 :           stream << command;
    2223             :         } else {
    2224          18 :           stream << "," << command;
    2225             :         }
    2226          23 :         ++i;
    2227             :       }
    2228           5 :       stream << "}";
    2229             :     }
    2230             : 
    2231          42 :     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         595 :   void add_subparser(ArgumentParser &parser) {
    2244         595 :     parser.m_parser_path = m_program_name + " " + parser.m_program_name;
    2245         595 :     auto it = m_subparsers.emplace(std::cend(m_subparsers), parser);
    2246         595 :     m_subparser_map.insert_or_assign(parser.m_program_name, it);
    2247         595 :     m_subparser_used.insert_or_assign(parser.m_program_name, false);
    2248         595 :   }
    2249             : 
    2250             :   void set_suppress(bool suppress) { m_suppress = suppress; }
    2251             : 
    2252             : protected:
    2253         558 :   const MutuallyExclusiveGroup *get_belonging_mutex(const Argument *arg) const {
    2254        1849 :     for (const auto &mutex : m_mutually_exclusive_groups) {
    2255        1362 :       if (std::find(mutex.m_elements.begin(), mutex.m_elements.end(), arg) !=
    2256        2724 :           mutex.m_elements.end()) {
    2257          71 :         return &mutex;
    2258             :       }
    2259             :     }
    2260         487 :     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       11573 :   preprocess_arguments(const std::vector<std::string> &raw_arguments) const {
    2276       11573 :     std::vector<std::string> arguments{};
    2277       79861 :     for (const auto &arg : raw_arguments) {
    2278             : 
    2279             :       const auto argument_starts_with_prefix_chars =
    2280       90411 :           [this](const std::string &a) -> bool {
    2281       45207 :         if (!a.empty()) {
    2282             : 
    2283       86221 :           const auto legal_prefix = [this](char c) -> bool {
    2284       86221 :             return m_prefix_chars.find(c) != std::string::npos;
    2285       45204 :           };
    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       45204 :           const auto windows_style = legal_prefix('/');
    2292             : 
    2293       45204 :           if (windows_style) {
    2294           0 :             if (legal_prefix(a[0])) {
    2295       39601 :               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       45204 :             if (a.size() > 1) {
    2303       39601 :               return (legal_prefix(a[0]) && legal_prefix(a[1]));
    2304             :             }
    2305             :           }
    2306             :         }
    2307        5606 :         return false;
    2308       68288 :       };
    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       68288 :       auto assign_char_pos = arg.find_first_of(m_assign_chars);
    2315             : 
    2316      113495 :       if (m_argument_map.find(arg) == m_argument_map.end() &&
    2317      113495 :           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       68283 :       arguments.push_back(arg);
    2330             :     }
    2331       11573 :     return arguments;
    2332             :   }
    2333             : 
    2334             :   /*
    2335             :    * @throws std::runtime_error in case of any invalid argument
    2336             :    */
    2337        5249 :   void parse_args_internal(const std::vector<std::string> &raw_arguments) {
    2338        5281 :     auto arguments = preprocess_arguments(raw_arguments);
    2339        5249 :     if (m_program_name.empty() && !arguments.empty()) {
    2340           0 :       m_program_name = arguments.front();
    2341             :     }
    2342        5249 :     auto end = std::end(arguments);
    2343        5249 :     auto positional_argument_it = std::begin(m_positional_arguments);
    2344       17262 :     for (auto it = std::next(std::begin(arguments)); it != end;) {
    2345       12045 :       const auto &current_argument = *it;
    2346       12045 :       if (Argument::is_positional(current_argument, m_prefix_chars)) {
    2347        1315 :         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        1315 :         auto argument = positional_argument_it++;
    2401             : 
    2402             :         // Deal with the situation of <positional_arg1>... <positional_arg2>
    2403        2497 :         if (argument->m_num_args_range.get_min() == 1 &&
    2404        1288 :             argument->m_num_args_range.get_max() == (std::numeric_limits<std::size_t>::max)() &&
    2405         206 :             positional_argument_it != std::end(m_positional_arguments) &&
    2406         200 :             std::next(positional_argument_it) == std::end(m_positional_arguments) &&
    2407        2597 :             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        1315 :         it = argument->consume(it, end);
    2418        1315 :         continue;
    2419             :       }
    2420             : 
    2421       10730 :       auto arg_map_it = m_argument_map.find(current_argument);
    2422       10730 :       if (arg_map_it != m_argument_map.end()) {
    2423       10730 :         auto argument = arg_map_it->second;
    2424       10730 :         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        5217 :     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      259078 :   void index_argument(argument_it it) {
    2546      526965 :     for (const auto &name : std::as_const(it->m_names)) {
    2547      267887 :       m_argument_map.insert_or_assign(name, it);
    2548             :     }
    2549      259078 :   }
    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