1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 04:33:53 +00:00

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

This commit is contained in:
Henry Fredrick Schreiner 2017-02-13 16:04:45 -05:00
parent 0c1aa2abc3
commit 15c6ee5f3d
5 changed files with 79 additions and 9 deletions

View File

@ -550,8 +550,7 @@ protected:
// Process callbacks
for(const Option_p& opt : options) {
if (opt->count() > 0) {
if(!opt->run_callback())
throw ConversionError(opt->get_name() + "=" + detail::join(opt->flatten_results()));
opt->run_callback();
}
}

View File

@ -70,6 +70,11 @@ struct ConversionError : public ParseError {
ConversionError(std::string name) : ParseError("ConversionError", name, 2) {}
};
/// Thrown when validation of results fails
struct ValidationError : public ParseError {
ValidationError(std::string name) : ParseError("ValidationError", name, 2) {}
};
/// Thrown when a required option is missing
struct RequiredError : public ParseError {
RequiredError(std::string name) : ParseError("RequiredError", name, 5) {}

View File

@ -196,14 +196,15 @@ public:
/// Process the callback
bool run_callback() const {
void run_callback() const {
if(!callback(results))
throw ConversionError(get_name() + "=" + detail::join(flatten_results()));
if(_validators.size()>0) {
for(const std::string & result : flatten_results())
for(const std::function<bool(std::string)> &vali : _validators)
if(!vali(result))
return false;
throw ValidationError(get_name() + "=" + result);
}
return callback(results);
}
/// If options share any of the same names, they are equal (not counting positional)

View File

@ -5,7 +5,8 @@
#include <string>
#include <iostream>
#include <functional>
#include "CLI/TypeTools.hpp"
// C standard library
// Only needed for existence checking
@ -61,5 +62,20 @@ bool NonexistentPath(std::string filename) {
}
}
/// Produce a range validator function
template<typename T>
std::function<bool(std::string)> Range(T min, T max) {
return [min, max](std::string input){
T val;
detail::lexical_cast(input, val);
return val >= min && val <= max;
};
}
/// Range of one value is 0 to value
template<typename T>
std::function<bool(std::string)> Range(T max) {
return Range((T) 0, max);
}
}

View File

@ -282,7 +282,7 @@ TEST_F(TApp, FileNotExists) {
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok);
EXPECT_THROW(run(), CLI::ConversionError);
EXPECT_THROW(run(), CLI::ValidationError);
std::remove(myfile.c_str());
EXPECT_FALSE(CLI::ExistingFile(myfile));
@ -296,8 +296,7 @@ TEST_F(TApp, FileExists) {
app.add_option("--file", filename)->check(CLI::ExistingFile);
args = {"--file", myfile};
EXPECT_THROW(run(), CLI::ConversionError);
EXPECT_EQ("Failed", filename);
EXPECT_THROW(run(), CLI::ValidationError);
app.reset();
@ -509,3 +508,53 @@ TEST_F(TApp, Env) {
EXPECT_THROW(run(), CLI::RequiredError);
}
TEST_F(TApp, RangeInt) {
int x=0;
app.add_option("--one", x)->check(CLI::Range(3,6));
args = {"--one=1"};
EXPECT_THROW(run(), CLI::ValidationError);
app.reset();
args = {"--one=7"};
EXPECT_THROW(run(), CLI::ValidationError);
app.reset();
args = {"--one=3"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--one=5"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--one=6"};
EXPECT_NO_THROW(run());
}
TEST_F(TApp, RangeDouble) {
double x=0;
/// Note that this must be a double in Range, too
app.add_option("--one", x)->check(CLI::Range(3.0,6.0));
args = {"--one=1"};
EXPECT_THROW(run(), CLI::ValidationError);
app.reset();
args = {"--one=7"};
EXPECT_THROW(run(), CLI::ValidationError);
app.reset();
args = {"--one=3"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--one=5"};
EXPECT_NO_THROW(run());
app.reset();
args = {"--one=6"};
EXPECT_NO_THROW(run());
}