1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 20:23:55 +00:00

Return string for error message in validators

This commit is contained in:
Henry Fredrick Schreiner 2017-11-24 14:04:20 -05:00 committed by Henry Schreiner
parent 715573359e
commit 85857d99e1
5 changed files with 62 additions and 57 deletions

View File

@ -148,7 +148,7 @@ class Option : public OptionBase<Option> {
bool changeable_{false}; bool changeable_{false};
/// A list of validators to run on each value parsed /// A list of validators to run on each value parsed
std::vector<std::function<bool(std::string &)>> validators_; std::vector<std::function<std::string(std::string &)>> validators_;
/// A list of options that are required with this option /// A list of options that are required with this option
std::set<Option *> requires_; std::set<Option *> requires_;
@ -220,16 +220,20 @@ class Option : public OptionBase<Option> {
} }
/// Adds a validator /// Adds a validator
Option *check(std::function<bool(const std::string &)> validator) { Option *check(std::function<std::string(const std::string &)> validator) {
validators_.emplace_back(validator); validators_.emplace_back(validator);
return this; return this;
} }
/// Adds a validator-like function that can change result /// Adds a validator-like function that can change result
Option *transform(std::function<std::string(std::string)> func) { Option *transform(std::function<std::string(std::string)> func) {
validators_.push_back([func](std::string &inout) { validators_.emplace_back([func](std::string &inout) {
inout = func(inout); try {
return true; inout = func(inout);
} catch(const ValidationError &e) {
return std::string(e.what());
}
return std::string();
}); });
return this; return this;
} }
@ -430,9 +434,11 @@ class Option : public OptionBase<Option> {
// Run the validators (can change the string) // Run the validators (can change the string)
if(!validators_.empty()) { if(!validators_.empty()) {
for(std::string &result : results_) for(std::string &result : results_)
for(const std::function<bool(std::string &)> &vali : validators_) for(const std::function<std::string(std::string &)> &vali : validators_) {
if(!vali(result)) std::string err_msg = vali(result);
throw ValidationError("Failed validation: " + get_name() + "=" + result); if(!err_msg.empty())
throw ValidationError(get_name() + ": " + err_msg);
}
} }
bool local_result; bool local_result;

View File

@ -4,6 +4,7 @@
// file LICENSE or https://github.com/CLIUtils/CLI11 for details. // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
#include "CLI/TypeTools.hpp" #include "CLI/TypeTools.hpp"
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <string> #include <string>
@ -19,64 +20,62 @@ namespace CLI {
/// @defgroup validator_group Validators /// @defgroup validator_group Validators
/// @brief Some validators that are provided /// @brief Some validators that are provided
/// ///
/// These are simple `bool(std::string&)` validators that are useful. /// These are simple `void(std::string&)` validators that are useful. They throw
/// a ValidationError if they fail (or the normally expected error if the cast fails)
/// @{ /// @{
/// Check for an existing file /// Check for an existing file
inline bool ExistingFile(const std::string &filename) { inline std::string ExistingFile(const std::string &filename) {
struct stat buffer; struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0; bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0; bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) { if(!exist) {
std::cerr << "File does not exist: " << filename << std::endl; return "File does not exist: " + filename;
return false;
} else if(is_dir) { } else if(is_dir) {
std::cerr << "File is actually a directory: " << filename << std::endl; return "File is actually a directory: " + filename;
return false;
} else {
return true;
} }
return std::string();
} }
/// Check for an existing directory /// Check for an existing directory
inline bool ExistingDirectory(const std::string &filename) { inline std::string ExistingDirectory(const std::string &filename) {
struct stat buffer; struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0; bool exist = stat(filename.c_str(), &buffer) == 0;
bool is_dir = (buffer.st_mode & S_IFDIR) != 0; bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
if(!exist) { if(!exist) {
std::cerr << "Directory does not exist: " << filename << std::endl; return "Directory does not exist: " + filename;
return false; } else if(!is_dir) {
} else if(is_dir) { return "Directory is actually a file: " + filename;
return true;
} else {
std::cerr << "Directory is actually a file: " << filename << std::endl;
return false;
} }
return std::string();
} }
/// Check for a non-existing path /// Check for a non-existing path
inline bool NonexistentPath(const std::string &filename) { inline std::string NonexistentPath(const std::string &filename) {
struct stat buffer; struct stat buffer;
bool exist = stat(filename.c_str(), &buffer) == 0; bool exist = stat(filename.c_str(), &buffer) == 0;
if(!exist) { if(exist) {
return true; return "Path already exists: " + filename;
} else {
std::cerr << "Path exists: " << filename << std::endl;
return false;
} }
return std::string();
} }
/// Produce a range validator function /// Produce a range validator function
template <typename T> std::function<bool(const std::string &)> Range(T min, T max) { template <typename T> std::function<std::string(const std::string &)> Range(T min, T max) {
return [min, max](std::string input) { return [min, max](std::string input) {
T val; T val;
detail::lexical_cast(input, val); detail::lexical_cast(input, val);
return val >= min && val <= max; if(val < min || val > max)
return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max);
return std::string();
}; };
} }
/// Range of one value is 0 to value /// Range of one value is 0 to value
template <typename T> std::function<bool(const std::string &)> Range(T max) { return Range(static_cast<T>(0), max); } template <typename T> std::function<std::string(const std::string &)> Range(T max) {
return Range(static_cast<T>(0), max);
}
/// @} /// @}

View File

@ -606,7 +606,7 @@ TEST_F(TApp, RemoveOption) {
TEST_F(TApp, FileNotExists) { TEST_F(TApp, FileNotExists) {
std::string myfile{"TestNonFileNotUsed.txt"}; std::string myfile{"TestNonFileNotUsed.txt"};
EXPECT_TRUE(CLI::NonexistentPath(myfile)); EXPECT_NO_THROW(CLI::NonexistentPath(myfile));
std::string filename; std::string filename;
app.add_option("--file", filename)->check(CLI::NonexistentPath); app.add_option("--file", filename)->check(CLI::NonexistentPath);
@ -622,12 +622,12 @@ TEST_F(TApp, FileNotExists) {
EXPECT_THROW(run(), CLI::ValidationError); EXPECT_THROW(run(), CLI::ValidationError);
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
} }
TEST_F(TApp, FileExists) { TEST_F(TApp, FileExists) {
std::string myfile{"TestNonFileNotUsed.txt"}; std::string myfile{"TestNonFileNotUsed.txt"};
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
std::string filename = "Failed"; std::string filename = "Failed";
app.add_option("--file", filename)->check(CLI::ExistingFile); app.add_option("--file", filename)->check(CLI::ExistingFile);
@ -643,7 +643,7 @@ TEST_F(TApp, FileExists) {
EXPECT_EQ(myfile, filename); EXPECT_EQ(myfile, filename);
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
} }
TEST_F(TApp, InSet) { TEST_F(TApp, InSet) {

View File

@ -87,50 +87,50 @@ TEST(Trim, TrimCopy) {
TEST(Validators, FileExists) { TEST(Validators, FileExists) {
std::string myfile{"TestFileNotUsed.txt"}; std::string myfile{"TestFileNotUsed.txt"};
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_TRUE(CLI::ExistingFile(myfile)); EXPECT_TRUE(CLI::ExistingFile(myfile).empty());
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
} }
TEST(Validators, FileNotExists) { TEST(Validators, FileNotExists) {
std::string myfile{"TestFileNotUsed.txt"}; std::string myfile{"TestFileNotUsed.txt"};
EXPECT_TRUE(CLI::NonexistentPath(myfile)); EXPECT_TRUE(CLI::NonexistentPath(myfile).empty());
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_FALSE(CLI::NonexistentPath(myfile)); EXPECT_FALSE(CLI::NonexistentPath(myfile).empty());
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_TRUE(CLI::NonexistentPath(myfile)); EXPECT_TRUE(CLI::NonexistentPath(myfile).empty());
} }
TEST(Validators, FileIsDir) { TEST(Validators, FileIsDir) {
std::string mydir{"../tests"}; std::string mydir{"../tests"};
EXPECT_FALSE(CLI::ExistingFile(mydir)); EXPECT_NE(CLI::ExistingFile(mydir), "");
} }
TEST(Validators, DirectoryExists) { TEST(Validators, DirectoryExists) {
std::string mydir{"../tests"}; std::string mydir{"../tests"};
EXPECT_TRUE(CLI::ExistingDirectory(mydir)); EXPECT_EQ(CLI::ExistingDirectory(mydir), "");
} }
TEST(Validators, DirectoryNotExists) { TEST(Validators, DirectoryNotExists) {
std::string mydir{"nondirectory"}; std::string mydir{"nondirectory"};
EXPECT_FALSE(CLI::ExistingDirectory(mydir)); EXPECT_NE(CLI::ExistingDirectory(mydir), "");
} }
TEST(Validators, DirectoryIsFile) { TEST(Validators, DirectoryIsFile) {
std::string myfile{"TestFileNotUsed.txt"}; std::string myfile{"TestFileNotUsed.txt"};
EXPECT_TRUE(CLI::NonexistentPath(myfile)); EXPECT_TRUE(CLI::NonexistentPath(myfile).empty());
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_FALSE(CLI::ExistingDirectory(myfile)); EXPECT_FALSE(CLI::ExistingDirectory(myfile).empty());
std::remove(myfile.c_str()); std::remove(myfile.c_str());
EXPECT_TRUE(CLI::NonexistentPath(myfile)); EXPECT_TRUE(CLI::NonexistentPath(myfile).empty());
} }
// Yes, this is testing an app_helper :) // Yes, this is testing an app_helper :)
@ -139,14 +139,14 @@ TEST(AppHelper, TempfileCreated) {
{ {
TempFile myfile{name}; TempFile myfile{name};
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_TRUE(CLI::ExistingFile(name)); EXPECT_TRUE(CLI::ExistingFile(name).empty());
EXPECT_THROW({ TempFile otherfile(name); }, std::runtime_error); EXPECT_THROW({ TempFile otherfile(name); }, std::runtime_error);
} }
EXPECT_FALSE(CLI::ExistingFile(name)); EXPECT_FALSE(CLI::ExistingFile(name).empty());
} }
TEST(AppHelper, TempfileNotCreated) { TEST(AppHelper, TempfileNotCreated) {
@ -154,9 +154,9 @@ TEST(AppHelper, TempfileNotCreated) {
{ {
TempFile myfile{name}; TempFile myfile{name};
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
} }
EXPECT_FALSE(CLI::ExistingFile(name)); EXPECT_FALSE(CLI::ExistingFile(name).empty());
} }
TEST(AppHelper, Ofstream) { TEST(AppHelper, Ofstream) {
@ -170,9 +170,9 @@ TEST(AppHelper, Ofstream) {
out << "this is output" << std::endl; out << "this is output" << std::endl;
} }
EXPECT_TRUE(CLI::ExistingFile(myfile)); EXPECT_TRUE(CLI::ExistingFile(myfile).empty());
} }
EXPECT_FALSE(CLI::ExistingFile(name)); EXPECT_FALSE(CLI::ExistingFile(name).empty());
} }
TEST(Split, StringList) { TEST(Split, StringList) {

View File

@ -27,7 +27,7 @@ class TempFile {
public: public:
TempFile(std::string name) : _name(name) { TempFile(std::string name) : _name(name) {
if(!CLI::NonexistentPath(_name)) if(!CLI::NonexistentPath(_name).empty())
throw std::runtime_error(_name); throw std::runtime_error(_name);
} }