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 191  
192 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 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 199 If an `App` (main or subcommand) has been parsed on the command line, `->parsed` will be true (or convert directly to bool).
197 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 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 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 205 Multiple subcommands are allowed, to allow [`Click`][Click] like series of commands (order is preserved).
203 206  
204 207 There are several options that are supported on the main app and subcommands. These are:
205 208  
206 209 * `.ignore_case()`: Ignore the case of this subcommand. Inherited by added subcommands, so is usually used on the main `App`.
207 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 214 * `.add_subcommand(name, description="")` Add a subcommand, returns a pointer to the internally stored subcommand.
210 215 * `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line
211 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 254  
250 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 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 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 268 ## Subclassing
264 269  
... ...
include/CLI/App.hpp
... ... @@ -136,7 +136,7 @@ class App {
136 136 /// Minimum required subcommands
137 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 140 size_t require_subcommand_max_ = 0;
141 141  
142 142 /// The group membership INHERITABLE
... ... @@ -175,6 +175,7 @@ class App {
175 175 fallthrough_ = parent_->fallthrough_;
176 176 group_ = parent_->group_;
177 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 255  
255 256 /// Require a subcommand to be given (does not affect help call)
256 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 259 App *require_subcommand(int value) {
259 260 if(value < 0) {
260 261 require_subcommand_min_ = 0;
... ... @@ -267,13 +268,19 @@ class App {
267 268 }
268 269  
269 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 272 App *require_subcommand(size_t min, size_t max) {
272 273 require_subcommand_min_ = min;
273 274 require_subcommand_max_ = max;
274 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 284 /// Stop subcommand fallthrough, so that parent commands cannot collect commands after subcommand.
278 285 /// Default from parent, usually set on parent.
279 286 App *fallthrough(bool value = true) {
... ...
tests/CreationTest.cpp
... ... @@ -322,7 +322,7 @@ TEST_F(TApp, HelpFlagFromDefaultsSubcommands) {
322 322 }
323 323  
324 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 327 // Initial defaults
328 328 EXPECT_FALSE(app.get_allow_extras());
... ... @@ -331,6 +331,8 @@ TEST_F(TApp, SubcommandDefaults) {
331 331 EXPECT_FALSE(app.get_fallthrough());
332 332 EXPECT_EQ(app.get_footer(), "");
333 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 337 app.allow_extras();
336 338 app.prefix_command();
... ... @@ -338,6 +340,7 @@ TEST_F(TApp, SubcommandDefaults) {
338 340 app.fallthrough();
339 341 app.set_footer("footy");
340 342 app.group("Stuff");
  343 + app.require_subcommand(2, 3);
341 344  
342 345 auto app2 = app.add_subcommand("app2");
343 346  
... ... @@ -348,4 +351,37 @@ TEST_F(TApp, SubcommandDefaults) {
348 351 EXPECT_TRUE(app2->get_fallthrough());
349 352 EXPECT_EQ(app2->get_footer(), "footy");
350 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 }
... ...