Commit 4dac11c025cf0782b298e2ba461c956d2c2e6ffe
Committed by
Henry Schreiner
1 parent
c1fb53f0
Finishing addition of ->configurable()
Showing
5 changed files
with
75 additions
and
33 deletions
include/CLI/App.hpp
| @@ -1209,7 +1209,7 @@ class App { | @@ -1209,7 +1209,7 @@ class App { | ||
| 1209 | 1209 | ||
| 1210 | if(!op->get_configurable()) | 1210 | if(!op->get_configurable()) |
| 1211 | throw INIError::NotConfigurable(current.fullname); | 1211 | throw INIError::NotConfigurable(current.fullname); |
| 1212 | - | 1212 | + |
| 1213 | if(op->results_.empty()) { | 1213 | if(op->results_.empty()) { |
| 1214 | // Flag parsing | 1214 | // Flag parsing |
| 1215 | if(op->get_expected() == 0) { | 1215 | if(op->get_expected() == 0) { |
include/CLI/Error.hpp
| @@ -86,30 +86,32 @@ class IncorrectConstruction : public ConstructionError { | @@ -86,30 +86,32 @@ class IncorrectConstruction : public ConstructionError { | ||
| 86 | CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) | 86 | CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) |
| 87 | CLI11_ERROR_SIMPLE(IncorrectConstruction) | 87 | CLI11_ERROR_SIMPLE(IncorrectConstruction) |
| 88 | static IncorrectConstruction PositionalFlag(std::string name) { | 88 | static IncorrectConstruction PositionalFlag(std::string name) { |
| 89 | - return IncorrectConstruction(name + ": Flags cannot be positional");} | 89 | + return IncorrectConstruction(name + ": Flags cannot be positional"); |
| 90 | + } | ||
| 90 | static IncorrectConstruction Set0Opt(std::string name) { | 91 | static IncorrectConstruction Set0Opt(std::string name) { |
| 91 | - return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");} | 92 | + return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); |
| 93 | + } | ||
| 92 | static IncorrectConstruction ChangeNotVector(std::string name) { | 94 | static IncorrectConstruction ChangeNotVector(std::string name) { |
| 93 | - return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");} | 95 | + return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); |
| 96 | + } | ||
| 94 | static IncorrectConstruction AfterMultiOpt(std::string name) { | 97 | static IncorrectConstruction AfterMultiOpt(std::string name) { |
| 95 | - return IncorrectConstruction(name + ": You can't change expected arguments after you've changed the multi option policy!");} | 98 | + return IncorrectConstruction( |
| 99 | + name + ": You can't change expected arguments after you've changed the multi option policy!"); | ||
| 100 | + } | ||
| 96 | static IncorrectConstruction MissingOption(std::string name) { | 101 | static IncorrectConstruction MissingOption(std::string name) { |
| 97 | - return IncorrectConstruction("Option " + name + " is not defined");} | 102 | + return IncorrectConstruction("Option " + name + " is not defined"); |
| 103 | + } | ||
| 98 | static IncorrectConstruction MultiOptionPolicy(std::string name) { | 104 | static IncorrectConstruction MultiOptionPolicy(std::string name) { |
| 99 | - return IncorrectConstruction(name + ": multi_option_policy only works for flags and single value options");} | ||
| 100 | - | 105 | + return IncorrectConstruction(name + ": multi_option_policy only works for flags and single value options"); |
| 106 | + } | ||
| 101 | }; | 107 | }; |
| 102 | 108 | ||
| 103 | /// Thrown on construction of a bad name | 109 | /// Thrown on construction of a bad name |
| 104 | class BadNameString : public ConstructionError { | 110 | class BadNameString : public ConstructionError { |
| 105 | CLI11_ERROR_DEF(ConstructionError, BadNameString) | 111 | CLI11_ERROR_DEF(ConstructionError, BadNameString) |
| 106 | CLI11_ERROR_SIMPLE(BadNameString) | 112 | CLI11_ERROR_SIMPLE(BadNameString) |
| 107 | - static BadNameString OneCharName(std::string name) { | ||
| 108 | - return BadNameString("Invalid one char name: " + name); | ||
| 109 | - } | ||
| 110 | - static BadNameString BadLongName(std::string name) { | ||
| 111 | - return BadNameString("Bad long name: " + name); | ||
| 112 | - } | 113 | + static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } |
| 114 | + static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } | ||
| 113 | static BadNameString DashesOnly(std::string name) { | 115 | static BadNameString DashesOnly(std::string name) { |
| 114 | return BadNameString("Must have a name, not just dashes: " + name); | 116 | return BadNameString("Must have a name, not just dashes: " + name); |
| 115 | } | 117 | } |
| @@ -162,7 +164,7 @@ class RuntimeError : public ParseError { | @@ -162,7 +164,7 @@ class RuntimeError : public ParseError { | ||
| 162 | class FileError : public ParseError { | 164 | class FileError : public ParseError { |
| 163 | CLI11_ERROR_DEF(ParseError, FileError) | 165 | CLI11_ERROR_DEF(ParseError, FileError) |
| 164 | CLI11_ERROR_SIMPLE(FileError) | 166 | CLI11_ERROR_SIMPLE(FileError) |
| 165 | - static FileError Missing(std::string name) {return FileError(name + " was not readable (missing?)");} | 167 | + static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } |
| 166 | }; | 168 | }; |
| 167 | 169 | ||
| 168 | /// Thrown when conversion call back fails, such as when an int fails to coerce to a string | 170 | /// Thrown when conversion call back fails, such as when an int fails to coerce to a string |
| @@ -174,9 +176,11 @@ class ConversionError : public ParseError { | @@ -174,9 +176,11 @@ class ConversionError : public ParseError { | ||
| 174 | ConversionError(std::string name, std::vector<std::string> results) | 176 | ConversionError(std::string name, std::vector<std::string> results) |
| 175 | : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} | 177 | : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} |
| 176 | static ConversionError TooManyInputsFlag(std::string name) { | 178 | static ConversionError TooManyInputsFlag(std::string name) { |
| 177 | - return ConversionError(name + ": too many inputs for a flag");} | 179 | + return ConversionError(name + ": too many inputs for a flag"); |
| 180 | + } | ||
| 178 | static ConversionError TrueFalse(std::string name) { | 181 | static ConversionError TrueFalse(std::string name) { |
| 179 | - return ConversionError(name + ": Should be true/false or a number");} | 182 | + return ConversionError(name + ": Should be true/false or a number"); |
| 183 | + } | ||
| 180 | }; | 184 | }; |
| 181 | 185 | ||
| 182 | /// Thrown when validation of results fails | 186 | /// Thrown when validation of results fails |
| @@ -189,15 +193,14 @@ class ValidationError : public ParseError { | @@ -189,15 +193,14 @@ class ValidationError : public ParseError { | ||
| 189 | /// Thrown when a required option is missing | 193 | /// Thrown when a required option is missing |
| 190 | class RequiredError : public ParseError { | 194 | class RequiredError : public ParseError { |
| 191 | CLI11_ERROR_DEF(ParseError, RequiredError) | 195 | CLI11_ERROR_DEF(ParseError, RequiredError) |
| 192 | - RequiredError(std::string name) : RequiredError(name + " is required") {} | 196 | + RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} |
| 193 | static RequiredError Subcommand(size_t min_subcom) { | 197 | static RequiredError Subcommand(size_t min_subcom) { |
| 194 | if(min_subcom == 1) | 198 | if(min_subcom == 1) |
| 195 | return RequiredError("A subcommand"); | 199 | return RequiredError("A subcommand"); |
| 196 | else | 200 | else |
| 197 | - return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError); | 201 | + return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", |
| 202 | + ExitCodes::RequiredError); | ||
| 198 | } | 203 | } |
| 199 | - | ||
| 200 | - | ||
| 201 | }; | 204 | }; |
| 202 | 205 | ||
| 203 | /// Thrown when the wrong number of arguments has been received | 206 | /// Thrown when the wrong number of arguments has been received |
| @@ -210,13 +213,13 @@ class ArgumentMismatch : public ParseError { | @@ -210,13 +213,13 @@ class ArgumentMismatch : public ParseError { | ||
| 210 | : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + | 213 | : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + |
| 211 | ", got " + std::to_string(recieved)), | 214 | ", got " + std::to_string(recieved)), |
| 212 | ExitCodes::ArgumentMismatch) {} | 215 | ExitCodes::ArgumentMismatch) {} |
| 213 | - | 216 | + |
| 214 | static ArgumentMismatch AtLeast(std::string name, int num) { | 217 | static ArgumentMismatch AtLeast(std::string name, int num) { |
| 215 | - return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required");} | 218 | + return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required"); |
| 219 | + } | ||
| 216 | static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { | 220 | static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { |
| 217 | - return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");} | ||
| 218 | - | ||
| 219 | - | 221 | + return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); |
| 222 | + } | ||
| 220 | }; | 223 | }; |
| 221 | 224 | ||
| 222 | /// Thrown when a requires option is missing | 225 | /// Thrown when a requires option is missing |
| @@ -247,9 +250,10 @@ class ExtrasError : public ParseError { | @@ -247,9 +250,10 @@ class ExtrasError : public ParseError { | ||
| 247 | class INIError : public ParseError { | 250 | class INIError : public ParseError { |
| 248 | CLI11_ERROR_DEF(ParseError, INIError) | 251 | CLI11_ERROR_DEF(ParseError, INIError) |
| 249 | CLI11_ERROR_SIMPLE(INIError) | 252 | CLI11_ERROR_SIMPLE(INIError) |
| 250 | - static INIError Extras(std::string item) {return INIError("INI was not able to parse " + item);} | ||
| 251 | - static INIError NotConfigurable(std::string item) {return INIError(item + ": This option is not allowed in a configuration file");} | ||
| 252 | - | 253 | + static INIError Extras(std::string item) { return INIError("INI was not able to parse " + item); } |
| 254 | + static INIError NotConfigurable(std::string item) { | ||
| 255 | + return INIError(item + ": This option is not allowed in a configuration file"); | ||
| 256 | + } | ||
| 253 | }; | 257 | }; |
| 254 | 258 | ||
| 255 | /// Thrown when validation fails before parsing | 259 | /// Thrown when validation fails before parsing |
include/CLI/Option.hpp
| @@ -43,15 +43,15 @@ template <typename CRTP> class OptionBase { | @@ -43,15 +43,15 @@ template <typename CRTP> class OptionBase { | ||
| 43 | 43 | ||
| 44 | /// Allow this option to be given in a configuration file | 44 | /// Allow this option to be given in a configuration file |
| 45 | bool configurable_{true}; | 45 | bool configurable_{true}; |
| 46 | - | 46 | + |
| 47 | /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too) | 47 | /// Policy for multiple arguments when `expected_ == 1` (can be set on bool flags, too) |
| 48 | MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw}; | 48 | MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw}; |
| 49 | - | ||
| 50 | 49 | ||
| 51 | template <typename T> void copy_to(T *other) const { | 50 | template <typename T> void copy_to(T *other) const { |
| 52 | other->group(group_); | 51 | other->group(group_); |
| 53 | other->required(required_); | 52 | other->required(required_); |
| 54 | other->ignore_case(ignore_case_); | 53 | other->ignore_case(ignore_case_); |
| 54 | + other->configurable(configurable_); | ||
| 55 | other->multi_option_policy(multi_option_policy_); | 55 | other->multi_option_policy(multi_option_policy_); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| @@ -84,7 +84,7 @@ template <typename CRTP> class OptionBase { | @@ -84,7 +84,7 @@ template <typename CRTP> class OptionBase { | ||
| 84 | 84 | ||
| 85 | /// The status of ignore case | 85 | /// The status of ignore case |
| 86 | bool get_ignore_case() const { return ignore_case_; } | 86 | bool get_ignore_case() const { return ignore_case_; } |
| 87 | - | 87 | + |
| 88 | /// The status of configurable | 88 | /// The status of configurable |
| 89 | bool get_configurable() const { return configurable_; } | 89 | bool get_configurable() const { return configurable_; } |
| 90 | 90 | ||
| @@ -113,7 +113,7 @@ template <typename CRTP> class OptionBase { | @@ -113,7 +113,7 @@ template <typename CRTP> class OptionBase { | ||
| 113 | self->multi_option_policy(MultiOptionPolicy::Join); | 113 | self->multi_option_policy(MultiOptionPolicy::Join); |
| 114 | return self; | 114 | return self; |
| 115 | } | 115 | } |
| 116 | - | 116 | + |
| 117 | /// Allow in a configuration file | 117 | /// Allow in a configuration file |
| 118 | CRTP *configurable(bool value = true) { | 118 | CRTP *configurable(bool value = true) { |
| 119 | configurable_ = value; | 119 | configurable_ = value; |
tests/CreationTest.cpp
| @@ -303,12 +303,14 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) { | @@ -303,12 +303,14 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) { | ||
| 303 | EXPECT_FALSE(app.option_defaults()->get_required()); | 303 | EXPECT_FALSE(app.option_defaults()->get_required()); |
| 304 | EXPECT_EQ(app.option_defaults()->get_multi_option_policy(), CLI::MultiOptionPolicy::Throw); | 304 | EXPECT_EQ(app.option_defaults()->get_multi_option_policy(), CLI::MultiOptionPolicy::Throw); |
| 305 | EXPECT_FALSE(app.option_defaults()->get_ignore_case()); | 305 | EXPECT_FALSE(app.option_defaults()->get_ignore_case()); |
| 306 | + EXPECT_TRUE(app.option_defaults()->get_configurable()); | ||
| 306 | EXPECT_EQ(app.option_defaults()->get_group(), "Options"); | 307 | EXPECT_EQ(app.option_defaults()->get_group(), "Options"); |
| 307 | 308 | ||
| 308 | app.option_defaults() | 309 | app.option_defaults() |
| 309 | ->required() | 310 | ->required() |
| 310 | ->multi_option_policy(CLI::MultiOptionPolicy::TakeLast) | 311 | ->multi_option_policy(CLI::MultiOptionPolicy::TakeLast) |
| 311 | ->ignore_case() | 312 | ->ignore_case() |
| 313 | + ->configurable(false) | ||
| 312 | ->group("Something"); | 314 | ->group("Something"); |
| 313 | 315 | ||
| 314 | auto app2 = app.add_subcommand("app2"); | 316 | auto app2 = app.add_subcommand("app2"); |
| @@ -316,6 +318,7 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) { | @@ -316,6 +318,7 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) { | ||
| 316 | EXPECT_TRUE(app2->option_defaults()->get_required()); | 318 | EXPECT_TRUE(app2->option_defaults()->get_required()); |
| 317 | EXPECT_EQ(app2->option_defaults()->get_multi_option_policy(), CLI::MultiOptionPolicy::TakeLast); | 319 | EXPECT_EQ(app2->option_defaults()->get_multi_option_policy(), CLI::MultiOptionPolicy::TakeLast); |
| 318 | EXPECT_TRUE(app2->option_defaults()->get_ignore_case()); | 320 | EXPECT_TRUE(app2->option_defaults()->get_ignore_case()); |
| 321 | + EXPECT_FALSE(app2->option_defaults()->get_configurable()); | ||
| 319 | EXPECT_EQ(app2->option_defaults()->get_group(), "Something"); | 322 | EXPECT_EQ(app2->option_defaults()->get_group(), "Something"); |
| 320 | } | 323 | } |
| 321 | 324 |
tests/IniTest.cpp
| @@ -379,6 +379,41 @@ TEST_F(TApp, IniFailure) { | @@ -379,6 +379,41 @@ TEST_F(TApp, IniFailure) { | ||
| 379 | EXPECT_THROW(run(), CLI::INIError); | 379 | EXPECT_THROW(run(), CLI::INIError); |
| 380 | } | 380 | } |
| 381 | 381 | ||
| 382 | +TEST_F(TApp, IniConfigurable) { | ||
| 383 | + | ||
| 384 | + TempFile tmpini{"TestIniTmp.ini"}; | ||
| 385 | + | ||
| 386 | + app.add_config("--config", tmpini); | ||
| 387 | + bool value; | ||
| 388 | + app.add_flag("--val", value)->configurable(true); | ||
| 389 | + | ||
| 390 | + { | ||
| 391 | + std::ofstream out{tmpini}; | ||
| 392 | + out << "[default]" << std::endl; | ||
| 393 | + out << "val=1" << std::endl; | ||
| 394 | + } | ||
| 395 | + | ||
| 396 | + EXPECT_NO_THROW(run()); | ||
| 397 | + EXPECT_TRUE(value); | ||
| 398 | +} | ||
| 399 | + | ||
| 400 | +TEST_F(TApp, IniNotConfigurable) { | ||
| 401 | + | ||
| 402 | + TempFile tmpini{"TestIniTmp.ini"}; | ||
| 403 | + | ||
| 404 | + app.add_config("--config", tmpini); | ||
| 405 | + bool value; | ||
| 406 | + app.add_flag("--val", value)->configurable(false); | ||
| 407 | + | ||
| 408 | + { | ||
| 409 | + std::ofstream out{tmpini}; | ||
| 410 | + out << "[default]" << std::endl; | ||
| 411 | + out << "val=1" << std::endl; | ||
| 412 | + } | ||
| 413 | + | ||
| 414 | + EXPECT_THROW(run(), CLI::INIError); | ||
| 415 | +} | ||
| 416 | + | ||
| 382 | TEST_F(TApp, IniSubFailure) { | 417 | TEST_F(TApp, IniSubFailure) { |
| 383 | 418 | ||
| 384 | TempFile tmpini{"TestIniTmp.ini"}; | 419 | TempFile tmpini{"TestIniTmp.ini"}; |