LCOV - code coverage report
Current view: top level - src/util - ArgParser.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 70 70 100.0 %
Date: 2025-03-25 01:19:55 Functions: 24 24 100.0 %
Branches: 39 102 38.2 %

           Branch data     Line data    Source code
       1                 :            : #include <mutable/util/ArgParser.hpp>
       2                 :            : 
       3                 :            : #include <cstdlib>
       4                 :            : #include <iomanip>
       5                 :            : #include <limits>
       6                 :            : #include <mutable/util/concepts.hpp>
       7                 :            : #include <mutable/util/macro.hpp>
       8                 :            : #include <string>
       9                 :            : #include <string_view>
      10                 :            : 
      11                 :            : 
      12                 :            : using namespace m;
      13                 :            : 
      14                 :            : 
      15                 :            : namespace {
      16                 :            : 
      17                 :            : /** Helper function to check if next argument was given. */
      18                 :         12 : void check_next_arg(const char **&argv)
      19                 :            : {
      20         [ +  - ]:         12 :     if (not *++argv) {
      21                 :            :         std::cerr << "missing argument" << std::endl;               //M_LCOV_EXCL_LINE
      22                 :            :         std::exit(EXIT_FAILURE);                                    //M_LCOV_EXCL_LINE
      23                 :            :     }
      24                 :         12 : }
      25                 :            : 
      26                 :            : /** Helper function to parse integral values. */
      27                 :            : template<typename T>
      28                 :            : requires integral<T>
      29                 :          6 : void help_parse(const char **&argv, const std::function<void(T)> &callback)
      30                 :            : {
      31                 :          6 :     check_next_arg(argv);
      32                 :            : 
      33                 :            :     T i;
      34                 :            :     try {
      35                 :            :         /*----- Signed integer types. -----*/
      36                 :            :         if constexpr (std::same_as<T, int>)
      37   [ +  -  +  - ]:          1 :             i = std::stoi(*argv);
      38                 :            :         if constexpr (std::same_as<T, long>)
      39   [ +  -  +  - ]:          1 :             i = std::stol(*argv);
      40                 :            :         if constexpr (std::same_as<T, long long>)
      41   [ +  -  +  - ]:          1 :             i = std::stoll(*argv);
      42                 :            :         /*----- Unsigned integer types. -----*/
      43                 :            :         if constexpr (std::same_as<T, unsigned>) {
      44   [ +  -  +  - ]:          1 :             const unsigned long v = std::stoul(*argv);
      45         [ +  - ]:          1 :             if (v > std::numeric_limits<unsigned>::max())
      46                 :            :                 throw std::out_of_range("input exceeds range of type unsigned int");  //M_LCOV_EXCL_LINE
      47                 :          1 :             i = unsigned(v);
      48                 :            :         }
      49                 :            :         if constexpr (std::same_as<T, unsigned long>)
      50   [ +  -  +  - ]:          1 :             i = std::stoul(*argv);
      51                 :            :         if constexpr (std::same_as<T, unsigned long long>)
      52   [ +  -  +  - ]:          1 :             i = std::stoull(*argv);
      53   [ #  #  #  #  :          6 :     }
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      54                 :            : 
      55                 :            :     M_LCOV_EXCL_START
      56                 :            :     catch(std::invalid_argument ex) {
      57                 :            :         std::cerr << "not a valid integer" << std::endl;
      58                 :            :         std::exit(EXIT_FAILURE);
      59                 :            :     } catch (std::out_of_range ex) {
      60                 :            :         std::cerr << "value out of range" << std::endl;
      61                 :            :         std::exit(EXIT_FAILURE);
      62                 :            :     }
      63                 :            :     M_LCOV_EXCL_STOP
      64                 :            : 
      65                 :          6 :     callback(i);
      66                 :          6 : }
      67                 :            : 
      68                 :            : /** Helper function to parse floating-point values. */
      69                 :            : template<typename T>
      70                 :            : requires std::floating_point<T>
      71                 :          3 : void help_parse(const char **&argv, const std::function<void(T)> &callback)
      72                 :            : {
      73                 :          3 :     check_next_arg(argv);
      74                 :          1 : 
      75                 :            :     T fp;
      76                 :            :     try {
      77                 :            :         if constexpr (std::same_as<T, float>)
      78   [ +  -  +  - ]:          1 :             fp = std::stof(*argv);
      79                 :            :         if constexpr (std::same_as<T, double>)
      80   [ +  -  +  - ]:          1 :             fp = std::stod(*argv);
      81                 :            :         if constexpr (std::same_as<T, long double>)
      82   [ +  -  +  - ]:          1 :             fp = std::stold(*argv);
      83   [ #  #  #  #  :          3 :     }
          #  #  #  #  #  
                #  #  # ]
      84                 :            : 
      85                 :            :     M_LCOV_EXCL_START
      86                 :            :     catch (std::invalid_argument) {
      87                 :            :         std::cerr << "not a valid floating-point number" << std::endl;
      88                 :            :         std::exit(EXIT_FAILURE);
      89                 :            :     } catch (std::out_of_range) {
      90                 :            :         std::cerr << "value out of range" << std::endl;
      91                 :            :         std::exit(EXIT_FAILURE);
      92                 :            :     }
      93                 :            :     M_LCOV_EXCL_STOP
      94                 :            : 
      95                 :          3 :     callback(fp);
      96                 :          3 : }
      97                 :            : 
      98                 :            : }
      99                 :            : 
     100                 :            : #define PARSE(TYPE) \
     101                 :            : template<> void ArgParser::OptionImpl<TYPE>::parse(const char **&argv) const { help_parse<TYPE>(argv, callback); }
     102                 :            : 
     103                 :            : /*----- Boolean ------------------------------------------------------------------------------------------------------*/
     104                 :          3 : template<> void ArgParser::OptionImpl<bool>::parse(const char **&) const { callback(true); }
     105                 :            : 
     106                 :            : /*----- Integral -----------------------------------------------------------------------------------------------------*/
     107                 :          1 : PARSE(int);
     108                 :          1 : PARSE(long);
     109                 :          1 : PARSE(long long);
     110                 :          1 : PARSE(unsigned);
     111                 :          1 : PARSE(unsigned long);
     112                 :          1 : PARSE(unsigned long long);
     113                 :            : 
     114                 :            : /*----- Floating point -----------------------------------------------------------------------------------------------*/
     115                 :          1 : PARSE(float);
     116                 :          1 : PARSE(double);
     117                 :          1 : PARSE(long double);
     118                 :            : 
     119                 :            : /*----- String -------------------------------------------------------------------------------------------------------*/
     120                 :            : template<>
     121                 :          1 : void ArgParser::OptionImpl<const char*>::parse(const char **&argv) const
     122                 :            : {
     123                 :          1 :     check_next_arg(argv);
     124                 :          1 :     callback(*argv);
     125                 :          1 : }
     126                 :            : 
     127                 :            : /*----- List of String -----------------------------------------------------------------------------------------------*/
     128                 :            : template<>
     129                 :          2 : void ArgParser::OptionImpl<std::vector<std::string_view>>::parse(const char **&argv) const
     130                 :            : {
     131                 :          2 :     check_next_arg(argv);
     132                 :            : 
     133                 :          2 :     std::vector<std::string_view> args;
     134                 :          2 :     std::string_view sv(*argv);
     135                 :            : 
     136         [ +  + ]:          2 :     if (sv.empty()) {
     137         [ +  - ]:          1 :         callback(std::move(args));
     138                 :          1 :         return;
     139                 :            :     }
     140                 :            : 
     141                 :          1 :     std::string_view::size_type begin = 0;
     142                 :          4 :     for (;;) {
     143                 :          4 :         auto end = sv.find(',', begin);
     144   [ +  -  +  - ]:          4 :         args.emplace_back(sv.substr(begin, end - begin));
     145         [ +  + ]:          4 :         if (end == std::string_view::npos)
     146                 :          1 :             break;
     147                 :          3 :         begin = end + 1; // skip comma ','
     148                 :            :     }
     149                 :            : 
     150         [ +  - ]:          1 :     callback(std::move(args));
     151         [ -  + ]:          2 : }
     152                 :            : 
     153                 :            : #undef PARSE
     154                 :            : 
     155                 :            : //----------------------------------------------------------------------------------------------------------------------
     156                 :            : 
     157                 :            : M_LCOV_EXCL_START
     158                 :            : void ArgParser::print_args(std::ostream &out) const
     159                 :            : {
     160                 :            :     auto print = [this, &out](const char *Short, const char *Long, const char *Descr) {
     161                 :            :         out << "    "
     162                 :            :             << Short << std::setw(short_len_ - strlen(Short)) << ""
     163                 :            :             << "  "
     164                 :            :             << Long << std::setw(long_len_ - strlen(Long)) << ""
     165                 :            :             << "    -    "
     166                 :            :             << Descr
     167                 :            :             << '\n';
     168                 :            :     };
     169                 :            : 
     170                 :            :     out << "General:\n";
     171                 :            :     for (auto &opt : general_options_)
     172                 :            :         print(opt->short_name.has_value() ? *opt->short_name : "",
     173                 :            :               opt->long_name.has_value()  ? *opt->long_name : "",
     174                 :            :               opt->description);
     175                 :            : 
     176                 :            :     for (auto &grp : grouped_options_) {
     177                 :            :         out << grp.first << ":\n";
     178                 :            :         for (auto &opt : grp.second)
     179                 :            :             print(opt->short_name.has_value() ? *opt->short_name : "",
     180                 :            :                   opt->long_name.has_value()  ? *opt->long_name : "",
     181                 :            :                   opt->description);
     182                 :            :     }
     183                 :            : }
     184                 :            : M_LCOV_EXCL_STOP
     185                 :            : 
     186                 :         16 : void ArgParser::parse_args(int, const char **argv) {
     187         [ +  + ]:         34 :     for (++argv; *argv; ++argv) {
     188         [ +  + ]:         19 :         if (streq(*argv, "--"))
     189                 :          1 :             goto positional;
     190         [ +  - ]:         18 :         auto it = key_map_.find(pool_(*argv));
     191         [ +  + ]:         18 :         if (it != key_map_.end()) {
     192                 :         15 :             it->second.get().parse(argv); // option
     193                 :         15 :         } else {
     194         [ -  + ]:          3 :             if (strneq(*argv, "--", 2))
     195                 :            :                 std::cerr << "warning: ignore unknown option " << *argv << std::endl;   //M_LCOV_EXCL_LINE
     196                 :            :             else
     197                 :          3 :                 args_.emplace_back(*argv); // positional argument
     198                 :            :         }
     199                 :         18 :     }
     200                 :         15 :     return;
     201                 :            : 
     202                 :            :     /* Read all following arguments as positional arguments. */
     203                 :            : positional:
     204         [ +  + ]:          3 :     for (++argv; *argv; ++argv)
     205                 :          2 :         args_.emplace_back(*argv);
     206                 :         16 : }

Generated by: LCOV version 1.16