Commit c1fb53f00d75ff5ef32dbfa7f73268f04ef78a9c

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent 04e01d2b

Moving error messages definitions to Error.h

include/CLI/App.hpp
... ... @@ -369,7 +369,7 @@ class App {
369 369  
370 370 Option *opt = add_option(name, fun, description, false);
371 371 if(opt->get_positional())
372   - throw IncorrectConstruction("Flags cannot be positional");
  372 + throw IncorrectConstruction::PositionalFlag(name);
373 373 opt->set_custom_option("", 0);
374 374 return opt;
375 375 }
... ... @@ -389,7 +389,7 @@ class App {
389 389  
390 390 Option *opt = add_option(name, fun, description, false);
391 391 if(opt->get_positional())
392   - throw IncorrectConstruction("Flags cannot be positional");
  392 + throw IncorrectConstruction::PositionalFlag(name);
393 393 opt->set_custom_option("", 0);
394 394 return opt;
395 395 }
... ... @@ -409,7 +409,7 @@ class App {
409 409  
410 410 Option *opt = add_option(name, fun, description, false);
411 411 if(opt->get_positional())
412   - throw IncorrectConstruction("Flags cannot be positional");
  412 + throw IncorrectConstruction::PositionalFlag(name);
413 413 opt->set_custom_option("", 0);
414 414 opt->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
415 415 return opt;
... ... @@ -428,7 +428,7 @@ class App {
428 428  
429 429 Option *opt = add_option(name, fun, description, false);
430 430 if(opt->get_positional())
431   - throw IncorrectConstruction("Flags cannot be positional");
  431 + throw IncorrectConstruction::PositionalFlag(name);
432 432 opt->set_custom_option("", 0);
433 433 return opt;
434 434 }
... ... @@ -1100,7 +1100,7 @@ class App {
1100 1100 std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_);
1101 1101 while(!values.empty()) {
1102 1102 if(!_parse_ini(values)) {
1103   - throw ExtrasINIError(values.back().fullname);
  1103 + throw INIError::Extras(values.back().fullname);
1104 1104 }
1105 1105 }
1106 1106 } catch(const FileError &) {
... ... @@ -1149,8 +1149,7 @@ class App {
1149 1149 if(opt->get_required() || opt->count() != 0) {
1150 1150 // Make sure enough -N arguments parsed (+N is already handled in parsing function)
1151 1151 if(opt->get_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_expected()))
1152   - throw ArgumentMismatch(opt->single_name() + ": At least " + std::to_string(-opt->get_expected()) +
1153   - " required");
  1152 + throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_expected());
1154 1153  
1155 1154 // Required but empty
1156 1155 if(opt->get_required() && opt->count() == 0)
... ... @@ -1167,10 +1166,8 @@ class App {
1167 1166 }
1168 1167  
1169 1168 auto selected_subcommands = get_subcommands();
1170   - if(require_subcommand_min_ > 0 && selected_subcommands.empty())
1171   - throw RequiredError("Subcommand required");
1172   - else if(require_subcommand_min_ > selected_subcommands.size())
1173   - throw RequiredError("Requires at least " + std::to_string(require_subcommand_min_) + " subcommands");
  1169 + if(require_subcommand_min_ > selected_subcommands.size())
  1170 + throw RequiredError::Subcommand(require_subcommand_min_);
1174 1171  
1175 1172 // Convert missing (pairs) to extras (string only)
1176 1173 if(!(allow_extras_ || prefix_command_)) {
... ... @@ -1210,6 +1207,9 @@ class App {
1210 1207 // Let's not go crazy with pointer syntax
1211 1208 Option_p &op = *op_ptr;
1212 1209  
  1210 + if(!op->get_configurable())
  1211 + throw INIError::NotConfigurable(current.fullname);
  1212 +
1213 1213 if(op->results_.empty()) {
1214 1214 // Flag parsing
1215 1215 if(op->get_expected() == 0) {
... ... @@ -1226,10 +1226,10 @@ class App {
1226 1226 for(size_t i = 0; i < ui; i++)
1227 1227 op->results_.emplace_back("");
1228 1228 } catch(const std::invalid_argument &) {
1229   - throw ConversionError(current.fullname + ": Should be true/false or a number");
  1229 + throw ConversionError::TrueFalse(current.fullname);
1230 1230 }
1231 1231 } else
1232   - throw ConversionError(current.fullname + ": too many inputs for a flag");
  1232 + throw ConversionError::TooManyInputsFlag(current.fullname);
1233 1233 } else {
1234 1234 op->results_ = current.inputs;
1235 1235 op->run_callback();
... ... @@ -1424,8 +1424,7 @@ class App {
1424 1424 }
1425 1425  
1426 1426 if(num > 0) {
1427   - throw ArgumentMismatch(op->single_name() + ": " + std::to_string(num) + " required " +
1428   - op->get_type_name() + " missing");
  1427 + throw ArgumentMismatch::TypedAtLeast(op->single_name(), num, op->get_type_name());
1429 1428 }
1430 1429 }
1431 1430  
... ...
include/CLI/Error.hpp
... ... @@ -35,14 +35,14 @@ enum class ExitCodes {
35 35 IncorrectConstruction = 100,
36 36 BadNameString,
37 37 OptionAlreadyAdded,
38   - File,
  38 + FileError,
39 39 ConversionError,
40 40 ValidationError,
41 41 RequiredError,
42 42 RequiresError,
43 43 ExcludesError,
44 44 ExtrasError,
45   - ExtrasINIError,
  45 + INIError,
46 46 InvalidError,
47 47 HorribleError,
48 48 OptionNotFound,
... ... @@ -85,18 +85,50 @@ class ConstructionError : public Error {
85 85 class IncorrectConstruction : public ConstructionError {
86 86 CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction)
87 87 CLI11_ERROR_SIMPLE(IncorrectConstruction)
  88 + static IncorrectConstruction PositionalFlag(std::string name) {
  89 + return IncorrectConstruction(name + ": Flags cannot be positional");}
  90 + static IncorrectConstruction Set0Opt(std::string name) {
  91 + return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");}
  92 + static IncorrectConstruction ChangeNotVector(std::string name) {
  93 + return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");}
  94 + static IncorrectConstruction AfterMultiOpt(std::string name) {
  95 + return IncorrectConstruction(name + ": You can't change expected arguments after you've changed the multi option policy!");}
  96 + static IncorrectConstruction MissingOption(std::string name) {
  97 + return IncorrectConstruction("Option " + name + " is not defined");}
  98 + static IncorrectConstruction MultiOptionPolicy(std::string name) {
  99 + return IncorrectConstruction(name + ": multi_option_policy only works for flags and single value options");}
  100 +
88 101 };
89 102  
90 103 /// Thrown on construction of a bad name
91 104 class BadNameString : public ConstructionError {
92 105 CLI11_ERROR_DEF(ConstructionError, BadNameString)
93 106 CLI11_ERROR_SIMPLE(BadNameString)
  107 + static BadNameString OneCharName(std::string name) {
  108 + return BadNameString("Invalid one char name: " + name);
  109 + }
  110 + static BadNameString BadLongName(std::string name) {
  111 + return BadNameString("Bad long name: " + name);
  112 + }
  113 + static BadNameString DashesOnly(std::string name) {
  114 + return BadNameString("Must have a name, not just dashes: " + name);
  115 + }
  116 + static BadNameString MultiPositionalNames(std::string name) {
  117 + return BadNameString("Only one positional name allowed, remove: " + name);
  118 + }
94 119 };
95 120  
96 121 /// Thrown when an option already exists
97 122 class OptionAlreadyAdded : public ConstructionError {
98 123 CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)
99   - CLI11_ERROR_SIMPLE(OptionAlreadyAdded)
  124 + OptionAlreadyAdded(std::string name)
  125 + : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
  126 + static OptionAlreadyAdded Requires(std::string name, std::string other) {
  127 + return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded);
  128 + }
  129 + static OptionAlreadyAdded Excludes(std::string name, std::string other) {
  130 + return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded);
  131 + }
100 132 };
101 133  
102 134 // Parsing errors
... ... @@ -129,7 +161,8 @@ class RuntimeError : public ParseError {
129 161 /// Thrown when parsing an INI file and it is missing
130 162 class FileError : public ParseError {
131 163 CLI11_ERROR_DEF(ParseError, FileError)
132   - FileError(std::string name) : FileError(name + " was not readable (missing?)", ExitCodes::File) {}
  164 + CLI11_ERROR_SIMPLE(FileError)
  165 + static FileError Missing(std::string name) {return FileError(name + " was not readable (missing?)");}
133 166 };
134 167  
135 168 /// Thrown when conversion call back fails, such as when an int fails to coerce to a string
... ... @@ -138,6 +171,12 @@ class ConversionError : public ParseError {
138 171 CLI11_ERROR_SIMPLE(ConversionError)
139 172 ConversionError(std::string member, std::string name)
140 173 : ConversionError("The value " + member + "is not an allowed value for " + name) {}
  174 + ConversionError(std::string name, std::vector<std::string> results)
  175 + : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
  176 + static ConversionError TooManyInputsFlag(std::string name) {
  177 + return ConversionError(name + ": too many inputs for a flag");}
  178 + static ConversionError TrueFalse(std::string name) {
  179 + return ConversionError(name + ": Should be true/false or a number");}
141 180 };
142 181  
143 182 /// Thrown when validation of results fails
... ... @@ -150,7 +189,15 @@ class ValidationError : public ParseError {
150 189 /// Thrown when a required option is missing
151 190 class RequiredError : public ParseError {
152 191 CLI11_ERROR_DEF(ParseError, RequiredError)
153   - CLI11_ERROR_SIMPLE(RequiredError)
  192 + RequiredError(std::string name) : RequiredError(name + " is required") {}
  193 + static RequiredError Subcommand(size_t min_subcom) {
  194 + if(min_subcom == 1)
  195 + return RequiredError("A subcommand");
  196 + else
  197 + return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError);
  198 + }
  199 +
  200 +
154 201 };
155 202  
156 203 /// Thrown when the wrong number of arguments has been received
... ... @@ -163,6 +210,13 @@ class ArgumentMismatch : public ParseError {
163 210 : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
164 211 ", got " + std::to_string(recieved)),
165 212 ExitCodes::ArgumentMismatch) {}
  213 +
  214 + static ArgumentMismatch AtLeast(std::string name, int num) {
  215 + return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required");}
  216 + static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
  217 + return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");}
  218 +
  219 +
166 220 };
167 221  
168 222 /// Thrown when a requires option is missing
... ... @@ -190,9 +244,12 @@ class ExtrasError : public ParseError {
190 244 };
191 245  
192 246 /// Thrown when extra values are found in an INI file
193   -class ExtrasINIError : public ParseError {
194   - CLI11_ERROR_DEF(ParseError, ExtrasINIError)
195   - ExtrasINIError(std::string item) : ExtrasINIError("INI was not able to parse " + item, ExitCodes::ExtrasINIError) {}
  247 +class INIError : public ParseError {
  248 + CLI11_ERROR_DEF(ParseError, INIError)
  249 + CLI11_ERROR_SIMPLE(INIError)
  250 + static INIError Extras(std::string item) {return INIError("INI was not able to parse " + item);}
  251 + static INIError NotConfigurable(std::string item) {return INIError(item + ": This option is not allowed in a configuration file");}
  252 +
196 253 };
197 254  
198 255 /// Thrown when validation fails before parsing
... ... @@ -204,6 +261,7 @@ class InvalidError : public ParseError {
204 261 };
205 262  
206 263 /// This is just a safety check to verify selection and parsing match - you should not ever see it
  264 +/// Strings are directly added to this error, but again, it should never be seen.
207 265 class HorribleError : public ParseError {
208 266 CLI11_ERROR_DEF(ParseError, HorribleError)
209 267 CLI11_ERROR_SIMPLE(HorribleError)
... ...
include/CLI/Ini.hpp
... ... @@ -106,7 +106,7 @@ inline std::vector&lt;ini_ret_t&gt; parse_ini(const std::string &amp;name) {
106 106  
107 107 std::ifstream input{name};
108 108 if(!input.good())
109   - throw FileError(name);
  109 + throw FileError::Missing(name);
110 110  
111 111 return parse_ini(input);
112 112 }
... ...
include/CLI/Option.hpp
... ... @@ -41,8 +41,12 @@ template &lt;typename CRTP&gt; class OptionBase {
41 41 /// Ignore the case when matching (option, not value)
42 42 bool ignore_case_{false};
43 43  
  44 + /// Allow this option to be given in a configuration file
  45 + bool configurable_{true};
  46 +
44 47 /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too)
45 48 MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
  49 +
46 50  
47 51 template <typename T> void copy_to(T *other) const {
48 52 other->group(group_);
... ... @@ -80,6 +84,9 @@ template &lt;typename CRTP&gt; class OptionBase {
80 84  
81 85 /// The status of ignore case
82 86 bool get_ignore_case() const { return ignore_case_; }
  87 +
  88 + /// The status of configurable
  89 + bool get_configurable() const { return configurable_; }
83 90  
84 91 /// The status of the multi option policy
85 92 MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }
... ... @@ -106,6 +113,12 @@ template &lt;typename CRTP&gt; class OptionBase {
106 113 self->multi_option_policy(MultiOptionPolicy::Join);
107 114 return self;
108 115 }
  116 +
  117 + /// Allow in a configuration file
  118 + CRTP *configurable(bool value = true) {
  119 + configurable_ = value;
  120 + return static_cast<CRTP *>(this);
  121 + }
109 122 };
110 123  
111 124 class OptionDefaults : public OptionBase<OptionDefaults> {
... ... @@ -235,12 +248,11 @@ class Option : public OptionBase&lt;Option&gt; {
235 248 if(expected_ == value)
236 249 return this;
237 250 else if(value == 0)
238   - throw IncorrectConstruction("Cannot set 0 expected, use a flag instead");
  251 + throw IncorrectConstruction::Set0Opt(single_name());
239 252 else if(!changeable_)
240   - throw IncorrectConstruction("You can only change the expected arguments for vectors");
  253 + throw IncorrectConstruction::ChangeNotVector(single_name());
241 254 else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
242   - throw IncorrectConstruction(
243   - "You can't change expected arguments after you've changed the multi option policy!");
  255 + throw IncorrectConstruction::AfterMultiOpt(single_name());
244 256  
245 257 expected_ = value;
246 258 return this;
... ... @@ -269,7 +281,7 @@ class Option : public OptionBase&lt;Option&gt; {
269 281 Option *requires(Option *opt) {
270 282 auto tup = requires_.insert(opt);
271 283 if(!tup.second)
272   - throw OptionAlreadyAdded(get_name() + " requires " + opt->get_name());
  284 + throw OptionAlreadyAdded::Requires(get_name(), opt->get_name());
273 285 return this;
274 286 }
275 287  
... ... @@ -278,7 +290,7 @@ class Option : public OptionBase&lt;Option&gt; {
278 290 for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
279 291 if(opt.get() != this && opt->check_name(opt_name))
280 292 return requires(opt.get());
281   - throw IncorrectConstruction("Option " + opt_name + " is not defined");
  293 + throw IncorrectConstruction::MissingOption(opt_name);
282 294 }
283 295  
284 296 /// Any number supported, any mix of string and Opt
... ... @@ -291,7 +303,7 @@ class Option : public OptionBase&lt;Option&gt; {
291 303 Option *excludes(Option *opt) {
292 304 auto tup = excludes_.insert(opt);
293 305 if(!tup.second)
294   - throw OptionAlreadyAdded(get_name() + " excludes " + opt->get_name());
  306 + throw OptionAlreadyAdded::Excludes(get_name(), opt->get_name());
295 307 return this;
296 308 }
297 309  
... ... @@ -300,7 +312,7 @@ class Option : public OptionBase&lt;Option&gt; {
300 312 for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
301 313 if(opt.get() != this && opt->check_name(opt_name))
302 314 return excludes(opt.get());
303   - throw IncorrectConstruction("Option " + opt_name + " is not defined");
  315 + throw IncorrectConstruction::MissingOption(opt_name);
304 316 }
305 317 /// Any number supported, any mix of string and Opt
306 318 template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {
... ... @@ -324,7 +336,7 @@ class Option : public OptionBase&lt;Option&gt; {
324 336  
325 337 for(const Option_p &opt : parent->options_)
326 338 if(opt.get() != this && *opt == *this)
327   - throw OptionAlreadyAdded(opt->get_name() + " is already added");
  339 + throw OptionAlreadyAdded(opt->get_name());
328 340  
329 341 return this;
330 342 }
... ... @@ -332,7 +344,7 @@ class Option : public OptionBase&lt;Option&gt; {
332 344 /// Take the last argument if given multiple times
333 345 Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
334 346 if(get_expected() != 0 && get_expected() != 1)
335   - throw IncorrectConstruction("multi_option_policy only works for flags and single value options!");
  347 + throw IncorrectConstruction::MultiOptionPolicy(single_name());
336 348 multi_option_policy_ = value;
337 349 return this;
338 350 }
... ... @@ -480,7 +492,7 @@ class Option : public OptionBase&lt;Option&gt; {
480 492 }
481 493  
482 494 if(local_result)
483   - throw ConversionError("Could not convert: " + get_name() + "=" + detail::join(results_));
  495 + throw ConversionError(get_name(), results_);
484 496 }
485 497  
486 498 /// If options share any of the same names, they are equal (not counting positional)
... ...
include/CLI/Split.hpp
... ... @@ -66,18 +66,18 @@ get_names(const std::vector&lt;std::string&gt; &amp;input) {
66 66 if(name.length() == 2 && valid_first_char(name[1]))
67 67 short_names.emplace_back(1, name[1]);
68 68 else
69   - throw BadNameString("Invalid one char name: " + name);
  69 + throw BadNameString::OneCharName(name);
70 70 } else if(name.length() > 2 && name.substr(0, 2) == "--") {
71 71 name = name.substr(2);
72 72 if(valid_name_string(name))
73 73 long_names.push_back(name);
74 74 else
75   - throw BadNameString("Bad long name: " + name);
  75 + throw BadNameString::BadLongName(name);
76 76 } else if(name == "-" || name == "--") {
77   - throw BadNameString("Must have a name, not just dashes");
  77 + throw BadNameString::DashesOnly(name);
78 78 } else {
79 79 if(pos_name.length() > 0)
80   - throw BadNameString("Only one positional name allowed, remove: " + name);
  80 + throw BadNameString::MultiPositionalNames(name);
81 81 pos_name = name;
82 82 }
83 83 }
... ...
tests/IniTest.cpp
... ... @@ -376,7 +376,7 @@ TEST_F(TApp, IniFailure) {
376 376 out << "val=1" << std::endl;
377 377 }
378 378  
379   - EXPECT_THROW(run(), CLI::ExtrasINIError);
  379 + EXPECT_THROW(run(), CLI::INIError);
380 380 }
381 381  
382 382 TEST_F(TApp, IniSubFailure) {
... ... @@ -392,7 +392,7 @@ TEST_F(TApp, IniSubFailure) {
392 392 out << "val=1" << std::endl;
393 393 }
394 394  
395   - EXPECT_THROW(run(), CLI::ExtrasINIError);
  395 + EXPECT_THROW(run(), CLI::INIError);
396 396 }
397 397  
398 398 TEST_F(TApp, IniNoSubFailure) {
... ... @@ -407,7 +407,7 @@ TEST_F(TApp, IniNoSubFailure) {
407 407 out << "val=1" << std::endl;
408 408 }
409 409  
410   - EXPECT_THROW(run(), CLI::ExtrasINIError);
  410 + EXPECT_THROW(run(), CLI::INIError);
411 411 }
412 412  
413 413 TEST_F(TApp, IniFlagConvertFailure) {
... ...