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 : }