Commit 15c6ee5f3db9bf83ebf738143f85fc8a7e79370d

Authored by Henry Fredrick Schreiner
1 parent 0c1aa2ab

Added Range and ValidationError, Refactor throwing errors to Option instead of App for Validation

include/CLI/App.hpp
... ... @@ -550,8 +550,7 @@ protected:
550 550 // Process callbacks
551 551 for(const Option_p& opt : options) {
552 552 if (opt->count() > 0) {
553   - if(!opt->run_callback())
554   - throw ConversionError(opt->get_name() + "=" + detail::join(opt->flatten_results()));
  553 + opt->run_callback();
555 554 }
556 555 }
557 556  
... ...
include/CLI/Error.hpp
... ... @@ -70,6 +70,11 @@ struct ConversionError : public ParseError {
70 70 ConversionError(std::string name) : ParseError("ConversionError", name, 2) {}
71 71 };
72 72  
  73 +/// Thrown when validation of results fails
  74 +struct ValidationError : public ParseError {
  75 + ValidationError(std::string name) : ParseError("ValidationError", name, 2) {}
  76 +};
  77 +
73 78 /// Thrown when a required option is missing
74 79 struct RequiredError : public ParseError {
75 80 RequiredError(std::string name) : ParseError("RequiredError", name, 5) {}
... ...
include/CLI/Option.hpp
... ... @@ -196,14 +196,15 @@ public:
196 196  
197 197  
198 198 /// Process the callback
199   - bool run_callback() const {
  199 + void run_callback() const {
  200 + if(!callback(results))
  201 + throw ConversionError(get_name() + "=" + detail::join(flatten_results()));
200 202 if(_validators.size()>0) {
201 203 for(const std::string & result : flatten_results())
202 204 for(const std::function<bool(std::string)> &vali : _validators)
203 205 if(!vali(result))
204   - return false;
  206 + throw ValidationError(get_name() + "=" + result);
205 207 }
206   - return callback(results);
207 208 }
208 209  
209 210 /// If options share any of the same names, they are equal (not counting positional)
... ...
include/CLI/Validators.hpp
... ... @@ -5,7 +5,8 @@
5 5  
6 6 #include <string>
7 7 #include <iostream>
8   -
  8 +#include <functional>
  9 +#include "CLI/TypeTools.hpp"
9 10  
10 11 // C standard library
11 12 // Only needed for existence checking
... ... @@ -61,5 +62,20 @@ bool NonexistentPath(std::string filename) {
61 62 }
62 63 }
63 64  
  65 +/// Produce a range validator function
  66 +template<typename T>
  67 +std::function<bool(std::string)> Range(T min, T max) {
  68 + return [min, max](std::string input){
  69 + T val;
  70 + detail::lexical_cast(input, val);
  71 + return val >= min && val <= max;
  72 + };
  73 +}
  74 +
  75 +/// Range of one value is 0 to value
  76 +template<typename T>
  77 +std::function<bool(std::string)> Range(T max) {
  78 + return Range((T) 0, max);
  79 +}
64 80  
65 81 }
... ...
tests/AppTest.cpp
... ... @@ -282,7 +282,7 @@ TEST_F(TApp, FileNotExists) {
282 282  
283 283 bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
284 284 EXPECT_TRUE(ok);
285   - EXPECT_THROW(run(), CLI::ConversionError);
  285 + EXPECT_THROW(run(), CLI::ValidationError);
286 286  
287 287 std::remove(myfile.c_str());
288 288 EXPECT_FALSE(CLI::ExistingFile(myfile));
... ... @@ -296,8 +296,7 @@ TEST_F(TApp, FileExists) {
296 296 app.add_option("--file", filename)->check(CLI::ExistingFile);
297 297 args = {"--file", myfile};
298 298  
299   - EXPECT_THROW(run(), CLI::ConversionError);
300   - EXPECT_EQ("Failed", filename);
  299 + EXPECT_THROW(run(), CLI::ValidationError);
301 300  
302 301 app.reset();
303 302  
... ... @@ -509,3 +508,53 @@ TEST_F(TApp, Env) {
509 508 EXPECT_THROW(run(), CLI::RequiredError);
510 509 }
511 510  
  511 +TEST_F(TApp, RangeInt) {
  512 + int x=0;
  513 + app.add_option("--one", x)->check(CLI::Range(3,6));
  514 +
  515 + args = {"--one=1"};
  516 + EXPECT_THROW(run(), CLI::ValidationError);
  517 +
  518 + app.reset();
  519 + args = {"--one=7"};
  520 + EXPECT_THROW(run(), CLI::ValidationError);
  521 +
  522 + app.reset();
  523 + args = {"--one=3"};
  524 + EXPECT_NO_THROW(run());
  525 +
  526 + app.reset();
  527 + args = {"--one=5"};
  528 + EXPECT_NO_THROW(run());
  529 +
  530 + app.reset();
  531 + args = {"--one=6"};
  532 + EXPECT_NO_THROW(run());
  533 +}
  534 +
  535 +TEST_F(TApp, RangeDouble) {
  536 +
  537 + double x=0;
  538 + /// Note that this must be a double in Range, too
  539 + app.add_option("--one", x)->check(CLI::Range(3.0,6.0));
  540 +
  541 + args = {"--one=1"};
  542 + EXPECT_THROW(run(), CLI::ValidationError);
  543 +
  544 + app.reset();
  545 + args = {"--one=7"};
  546 + EXPECT_THROW(run(), CLI::ValidationError);
  547 +
  548 + app.reset();
  549 + args = {"--one=3"};
  550 + EXPECT_NO_THROW(run());
  551 +
  552 + app.reset();
  553 + args = {"--one=5"};
  554 + EXPECT_NO_THROW(run());
  555 +
  556 + app.reset();
  557 + args = {"--one=6"};
  558 + EXPECT_NO_THROW(run());
  559 +}
  560 +
... ...