Commit 2c976808a95706eb4b153dce0c234a55325b59be

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent bcbd8c86

Inherit subcommand values, adding getters and tests

README.md
@@ -191,21 +191,26 @@ everything after that is positional only. @@ -191,21 +191,26 @@ everything after that is positional only.
191 191
192 Subcommands are supported, and can be nested infinitely. To add a subcommand, call the `add_subcommand` method with a name and an optional description. This gives a pointer to an `App` that behaves just like the main app, and can take options or further subcommands. Add `->ignore_case()` to a subcommand to allow any variation of caps to also be accepted. Children inherit the current setting from the parent. You cannot add multiple matching subcommand names at the same level (including ignore 192 Subcommands are supported, and can be nested infinitely. To add a subcommand, call the `add_subcommand` method with a name and an optional description. This gives a pointer to an `App` that behaves just like the main app, and can take options or further subcommands. Add `->ignore_case()` to a subcommand to allow any variation of caps to also be accepted. Children inherit the current setting from the parent. You cannot add multiple matching subcommand names at the same level (including ignore
193 case). 193 case).
194 -If you want to require at least one subcommand is given, use `.require_subcommand()` on the parent app. You can optionally give an exact number of subcommands to require, as well. 194 +
  195 +If you want to require that at least one subcommand is given, use `.require_subcommand()` on the parent app. You can optionally give an exact number of subcommands to require, as well. If you give two arguments, that sets the min and max number allowed.
  196 +0 for the max number allowed will allow an unlimited number of subcommands. As a handy shortcut, a single negative value N will set "up to N" values. Limiting the maximimum number allows you to keep arguments that match a previous
  197 +subcommand name from matching.
195 198
196 If an `App` (main or subcommand) has been parsed on the command line, `->parsed` will be true (or convert directly to bool). 199 If an `App` (main or subcommand) has been parsed on the command line, `->parsed` will be true (or convert directly to bool).
197 All `App`s have a `get_subcommands()` method, which returns a list of pointers to the subcommands passed on the command line. A `got_subcommand(App_or_name)` method is also provided that will check to see if an `App` pointer or a string name was collected on the command line. 200 All `App`s have a `get_subcommands()` method, which returns a list of pointers to the subcommands passed on the command line. A `got_subcommand(App_or_name)` method is also provided that will check to see if an `App` pointer or a string name was collected on the command line.
198 201
199 For many cases, however, using an app's callback may be easier. Every app executes a callback function after it parses; just use a lambda function (with capture to get parsed values) to `.set_callback`. If you throw `CLI::Success` or `CLI::RuntimeError(return_value)`, you can 202 For many cases, however, using an app's callback may be easier. Every app executes a callback function after it parses; just use a lambda function (with capture to get parsed values) to `.set_callback`. If you throw `CLI::Success` or `CLI::RuntimeError(return_value)`, you can
200 even exit the program through the callback. The main `App` has a callback slot, as well, but it is generally not as useful. 203 even exit the program through the callback. The main `App` has a callback slot, as well, but it is generally not as useful.
201 -If you want only one, use `app.require_subcommand(1)`. You are allowed to throw `CLI::Success` in the callbacks. 204 +You are allowed to throw `CLI::Success` in the callbacks.
202 Multiple subcommands are allowed, to allow [`Click`][Click] like series of commands (order is preserved). 205 Multiple subcommands are allowed, to allow [`Click`][Click] like series of commands (order is preserved).
203 206
204 There are several options that are supported on the main app and subcommands. These are: 207 There are several options that are supported on the main app and subcommands. These are:
205 208
206 * `.ignore_case()`: Ignore the case of this subcommand. Inherited by added subcommands, so is usually used on the main `App`. 209 * `.ignore_case()`: Ignore the case of this subcommand. Inherited by added subcommands, so is usually used on the main `App`.
207 * `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent command. Subcommands always are allowed to fall through. 210 * `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent command. Subcommands always are allowed to fall through.
208 -* `.require_subcommand()`: Require 1 or more subcommands. Accepts an integer argument to require an exact number of subcommands. 211 +* `.require_subcommand()`: Require 1 or more subcommands.
  212 +* `.require_subcommand(N)`: Require `N` subcommands if `N`>0, or up to `N` if `N`<0. N=0 resets to the default 0 or more.
  213 +* `.require_subcommand(min, max)`: Explicilty set min and max allowed subcommands. Setting `max` to 0 is unlimited.
209 * `.add_subcommand(name, description="")` Add a subcommand, returns a pointer to the internally stored subcommand. 214 * `.add_subcommand(name, description="")` Add a subcommand, returns a pointer to the internally stored subcommand.
210 * `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line 215 * `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line
211 * `.get_subcommands()`: The list of subcommands given on the command line 216 * `.get_subcommands()`: The list of subcommands given on the command line
@@ -249,7 +254,7 @@ arguments, use `.config_to_str(default_also=false)`, where `default_also` will a @@ -249,7 +254,7 @@ arguments, use `.config_to_str(default_also=false)`, where `default_also` will a
249 254
250 ## Inheriting defaults 255 ## Inheriting defaults
251 256
252 -Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, `fallthrough`, `group`, and `footer`. The help flag existence, name, and description are inherited, as well. 257 +Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, `fallthrough`, `group`, `footer`, and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well.
253 258
254 Options have defaults for `group`, `required`, `take_last`, and `ignore_case`. To set these defaults, you should set the `option_defauts()` object, for example: 259 Options have defaults for `group`, `required`, `take_last`, and `ignore_case`. To set these defaults, you should set the `option_defauts()` object, for example:
255 260
@@ -258,7 +263,7 @@ app.option_defauts()-&gt;required(); @@ -258,7 +263,7 @@ app.option_defauts()-&gt;required();
258 // All future options will be required 263 // All future options will be required
259 ``` 264 ```
260 265
261 -The default settings for options are inherited to subcommands, as well. 266 +The default settings for options are inherited to subcommands, as well.
262 267
263 ## Subclassing 268 ## Subclassing
264 269
include/CLI/App.hpp
@@ -136,7 +136,7 @@ class App { @@ -136,7 +136,7 @@ class App {
136 /// Minimum required subcommands 136 /// Minimum required subcommands
137 size_t require_subcommand_min_ = 0; 137 size_t require_subcommand_min_ = 0;
138 138
139 - /// Max number of subcommands allowed (parsing stops after this number). 0 is unlimited 139 + /// Max number of subcommands allowed (parsing stops after this number). 0 is unlimited INHERITABLE
140 size_t require_subcommand_max_ = 0; 140 size_t require_subcommand_max_ = 0;
141 141
142 /// The group membership INHERITABLE 142 /// The group membership INHERITABLE
@@ -175,6 +175,7 @@ class App { @@ -175,6 +175,7 @@ class App {
175 fallthrough_ = parent_->fallthrough_; 175 fallthrough_ = parent_->fallthrough_;
176 group_ = parent_->group_; 176 group_ = parent_->group_;
177 footer_ = parent_->footer_; 177 footer_ = parent_->footer_;
  178 + require_subcommand_max_ = parent_->require_subcommand_max_;
178 } 179 }
179 } 180 }
180 181
@@ -254,7 +255,7 @@ class App { @@ -254,7 +255,7 @@ class App {
254 255
255 /// Require a subcommand to be given (does not affect help call) 256 /// Require a subcommand to be given (does not affect help call)
256 /// The number required can be given. Negative values indicate maximum 257 /// The number required can be given. Negative values indicate maximum
257 - /// number allowed (0 for any number). 258 + /// number allowed (0 for any number). Max number inheritable.
258 App *require_subcommand(int value) { 259 App *require_subcommand(int value) {
259 if(value < 0) { 260 if(value < 0) {
260 require_subcommand_min_ = 0; 261 require_subcommand_min_ = 0;
@@ -267,13 +268,19 @@ class App { @@ -267,13 +268,19 @@ class App {
267 } 268 }
268 269
269 /// Explicitly control the number of subcommands required. Setting 0 270 /// Explicitly control the number of subcommands required. Setting 0
270 - /// for the max means unlimited number allowed 271 + /// for the max means unlimited number allowed. Max number inheritable.
271 App *require_subcommand(size_t min, size_t max) { 272 App *require_subcommand(size_t min, size_t max) {
272 require_subcommand_min_ = min; 273 require_subcommand_min_ = min;
273 require_subcommand_max_ = max; 274 require_subcommand_max_ = max;
274 return this; 275 return this;
275 } 276 }
276 277
  278 + /// Get the required min subcommand value
  279 + size_t get_require_subcommand_min() const { return require_subcommand_min_; }
  280 +
  281 + /// Get the required max subcommand value
  282 + size_t get_require_subcommand_max() const { return require_subcommand_max_; }
  283 +
277 /// Stop subcommand fallthrough, so that parent commands cannot collect commands after subcommand. 284 /// Stop subcommand fallthrough, so that parent commands cannot collect commands after subcommand.
278 /// Default from parent, usually set on parent. 285 /// Default from parent, usually set on parent.
279 App *fallthrough(bool value = true) { 286 App *fallthrough(bool value = true) {
tests/CreationTest.cpp
@@ -322,7 +322,7 @@ TEST_F(TApp, HelpFlagFromDefaultsSubcommands) { @@ -322,7 +322,7 @@ TEST_F(TApp, HelpFlagFromDefaultsSubcommands) {
322 } 322 }
323 323
324 TEST_F(TApp, SubcommandDefaults) { 324 TEST_F(TApp, SubcommandDefaults) {
325 - // allow_extras, prefix_command, ignore_case, fallthrough, group 325 + // allow_extras, prefix_command, ignore_case, fallthrough, group, min/max subcommand
326 326
327 // Initial defaults 327 // Initial defaults
328 EXPECT_FALSE(app.get_allow_extras()); 328 EXPECT_FALSE(app.get_allow_extras());
@@ -331,6 +331,8 @@ TEST_F(TApp, SubcommandDefaults) { @@ -331,6 +331,8 @@ TEST_F(TApp, SubcommandDefaults) {
331 EXPECT_FALSE(app.get_fallthrough()); 331 EXPECT_FALSE(app.get_fallthrough());
332 EXPECT_EQ(app.get_footer(), ""); 332 EXPECT_EQ(app.get_footer(), "");
333 EXPECT_EQ(app.get_group(), "Subcommands"); 333 EXPECT_EQ(app.get_group(), "Subcommands");
  334 + EXPECT_EQ(app.get_require_subcommand_min(), (size_t)0);
  335 + EXPECT_EQ(app.get_require_subcommand_max(), (size_t)0);
334 336
335 app.allow_extras(); 337 app.allow_extras();
336 app.prefix_command(); 338 app.prefix_command();
@@ -338,6 +340,7 @@ TEST_F(TApp, SubcommandDefaults) { @@ -338,6 +340,7 @@ TEST_F(TApp, SubcommandDefaults) {
338 app.fallthrough(); 340 app.fallthrough();
339 app.set_footer("footy"); 341 app.set_footer("footy");
340 app.group("Stuff"); 342 app.group("Stuff");
  343 + app.require_subcommand(2, 3);
341 344
342 auto app2 = app.add_subcommand("app2"); 345 auto app2 = app.add_subcommand("app2");
343 346
@@ -348,4 +351,37 @@ TEST_F(TApp, SubcommandDefaults) { @@ -348,4 +351,37 @@ TEST_F(TApp, SubcommandDefaults) {
348 EXPECT_TRUE(app2->get_fallthrough()); 351 EXPECT_TRUE(app2->get_fallthrough());
349 EXPECT_EQ(app2->get_footer(), "footy"); 352 EXPECT_EQ(app2->get_footer(), "footy");
350 EXPECT_EQ(app2->get_group(), "Stuff"); 353 EXPECT_EQ(app2->get_group(), "Stuff");
  354 + EXPECT_EQ(app2->get_require_subcommand_min(), (size_t)0);
  355 + EXPECT_EQ(app2->get_require_subcommand_max(), (size_t)3);
  356 +}
  357 +
  358 +TEST_F(TApp, SubcommandMinMax) {
  359 +
  360 + EXPECT_EQ(app.get_require_subcommand_min(), (size_t)0);
  361 + EXPECT_EQ(app.get_require_subcommand_max(), (size_t)0);
  362 +
  363 + app.require_subcommand();
  364 +
  365 + EXPECT_EQ(app.get_require_subcommand_min(), (size_t)1);
  366 + EXPECT_EQ(app.get_require_subcommand_max(), (size_t)0);
  367 +
  368 + app.require_subcommand(2);
  369 +
  370 + EXPECT_EQ(app.get_require_subcommand_min(), (size_t)2);
  371 + EXPECT_EQ(app.get_require_subcommand_max(), (size_t)2);
  372 +
  373 + app.require_subcommand(0);
  374 +
  375 + EXPECT_EQ(app.get_require_subcommand_min(), (size_t)0);
  376 + EXPECT_EQ(app.get_require_subcommand_max(), (size_t)0);
  377 +
  378 + app.require_subcommand(-2);
  379 +
  380 + EXPECT_EQ(app.get_require_subcommand_min(), (size_t)0);
  381 + EXPECT_EQ(app.get_require_subcommand_max(), (size_t)2);
  382 +
  383 + app.require_subcommand(3, 7);
  384 +
  385 + EXPECT_EQ(app.get_require_subcommand_min(), (size_t)3);
  386 + EXPECT_EQ(app.get_require_subcommand_max(), (size_t)7);
351 } 387 }