mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 12:13:52 +00:00
Add a ! operator to Validators for checking a false condition (#230)
This commit is contained in:
parent
546d5ec237
commit
571fb07cfb
@ -274,7 +274,7 @@ Before parsing, you can set the following options:
|
||||
- `->configurable(false)`: Disable this option from being in a configuration file.
|
||||
|
||||
|
||||
These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results. Validate can also be a subclass of `CLI::Validator`, in which case it can also set the type name and can be combined with `&` and `|` (all built-in validators are this sort).
|
||||
These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results. Validate can also be a subclass of `CLI::Validator`, in which case it can also set the type name and can be combined with `&` and `|` (all built-in validators are this sort). Validators can also be inverted with `!` such as `->check(!CLI::ExistingFile)` which would check that a file doesn't exist.
|
||||
|
||||
The `IsMember` validator lets you specify a set of predefined options. You can pass any container or copyable pointer (including `std::shared_ptr`) to a container to this validator; the container just needs to be iterable and have a `::value_type`. The type should be convertible from a string. You can use an initializer list directly if you like. If you need to modify the set later, the pointer form lets you do that; the type message and check will correctly refer to the current version of the set.
|
||||
After specifying a set of options, you can also specify "filter" functions of the form `T(T)`, where `T` is the type of the values. The most common choices probably will be `CLI::ignore_case` an `CLI::ignore_underscore`.
|
||||
|
@ -292,7 +292,7 @@ class App {
|
||||
return this;
|
||||
}
|
||||
|
||||
/// specify that the positional arguments are only at the end of the sequence
|
||||
/// Specify that the positional arguments are only at the end of the sequence
|
||||
App *positionals_at_end(bool value = true) {
|
||||
positionals_at_end_ = value;
|
||||
return this;
|
||||
|
@ -100,6 +100,29 @@ class Validator {
|
||||
};
|
||||
return newval;
|
||||
}
|
||||
|
||||
/// Create a validator that fails when a given validator succeeds
|
||||
Validator operator!() const {
|
||||
Validator newval;
|
||||
std::string typestring = tname;
|
||||
if(tname.empty()) {
|
||||
typestring = tname_function();
|
||||
}
|
||||
newval.tname = "NOT " + typestring;
|
||||
|
||||
std::string failString = "check " + typestring + " succeeded improperly";
|
||||
// Give references (will make a copy in lambda function)
|
||||
const std::function<std::string(std::string & res)> &f1 = func;
|
||||
|
||||
newval.func = [f1, failString](std::string &test) -> std::string {
|
||||
std::string s1 = f1(test);
|
||||
if(s1.empty())
|
||||
return failString;
|
||||
else
|
||||
return std::string();
|
||||
};
|
||||
return newval;
|
||||
}
|
||||
};
|
||||
|
||||
// The implementation of the built in validators is using the Validator class;
|
||||
|
@ -1317,6 +1317,24 @@ TEST_F(TApp, FileExists) {
|
||||
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
|
||||
}
|
||||
|
||||
TEST_F(TApp, NotFileExists) {
|
||||
std::string myfile{"TestNonFileNotUsed.txt"};
|
||||
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
|
||||
|
||||
std::string filename = "Failed";
|
||||
app.add_option("--file", filename)->check(!CLI::ExistingFile);
|
||||
args = {"--file", myfile};
|
||||
|
||||
EXPECT_NO_THROW(run());
|
||||
|
||||
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
|
||||
EXPECT_TRUE(ok);
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
std::remove(myfile.c_str());
|
||||
EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
|
||||
}
|
||||
|
||||
TEST_F(TApp, VectorFixedString) {
|
||||
std::vector<std::string> strvec;
|
||||
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
|
||||
|
@ -414,6 +414,20 @@ TEST_F(TApp, InSetIgnoreCasePointer) {
|
||||
EXPECT_THROW(run(), CLI::ArgumentMismatch);
|
||||
}
|
||||
|
||||
TEST_F(TApp, NotInSetIgnoreCasePointer) {
|
||||
|
||||
std::set<std::string> *options = new std::set<std::string>{"one", "Two", "THREE"};
|
||||
std::string choice;
|
||||
app.add_option("-q,--quick", choice)->check(!CLI::IsMember(*options, CLI::ignore_case));
|
||||
|
||||
args = {"--quick", "One"};
|
||||
EXPECT_THROW(run(), CLI::ValidationError);
|
||||
|
||||
args = {"--quick", "four"};
|
||||
run();
|
||||
EXPECT_EQ(choice, "four");
|
||||
}
|
||||
|
||||
TEST_F(TApp, InSetIgnoreUnderscore) {
|
||||
|
||||
std::string choice;
|
||||
|
Loading…
x
Reference in New Issue
Block a user