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:
parent
715573359e
commit
85857d99e1
@ -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) {
|
||||||
|
try {
|
||||||
inout = func(inout);
|
inout = func(inout);
|
||||||
return true;
|
} 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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user