Commit fa5da7deaa1ae9df258bef7c9741525a752e4330

Authored by Henry Fredrick Schreiner
Committed by Henry Schreiner
1 parent 9b586786

Adding tests for inhert, a few fixes

include/CLI/App.hpp
... ... @@ -78,7 +78,7 @@ class App {
78 78 ///@}
79 79 /// @name Options
80 80 ///@{
81   -
  81 +
82 82 /// The default values for options, customizable and changeable INHERITABLE
83 83 OptionDefaults option_defaults_;
84 84  
... ... @@ -143,21 +143,22 @@ class App {
143 143 ///@}
144 144  
145 145 /// Special private constructor for subcommand
146   - App(std::string description_, App* parent) : description_(std::move(description_)), parent_(parent) {
  146 + App(std::string description_, App *parent) : description_(std::move(description_)), parent_(parent) {
147 147 // Inherit if not from a nullptr
148 148 if(parent_ != nullptr) {
149 149 if(parent_->help_ptr_ != nullptr)
150 150 set_help_flag(parent_->help_ptr_->get_name(), parent_->help_ptr_->get_description());
151   -
  151 +
152 152 /// OptionDefaults
153 153 option_defaults_ = parent_->option_defaults_;
154   -
  154 +
155 155 // INHERITABLE
156 156 allow_extras_ = parent_->allow_extras_;
157 157 prefix_command_ = parent_->prefix_command_;
158 158 ignore_case_ = parent_->ignore_case_;
159 159 fallthrough_ = parent_->fallthrough_;
160 160 group_ = parent_->group_;
  161 + footer_ = parent_->footer_;
161 162 }
162 163 }
163 164  
... ... @@ -176,6 +177,9 @@ class App {
176 177 return this;
177 178 }
178 179  
  180 + /// Get footer.
  181 + std::string get_footer() const { return footer_; }
  182 +
179 183 /// Set a callback for the end of parsing.
180 184 ///
181 185 /// Due to a bug in c++11,
... ... @@ -193,12 +197,17 @@ class App {
193 197 return this;
194 198 }
195 199  
  200 + /// Get the status of allow extras
  201 + bool get_allow_extras() const { return allow_extras_; }
  202 +
196 203 /// Do not parse anything after the first unrecognised option and return
197 204 App *prefix_command(bool allow = true) {
198 205 prefix_command_ = allow;
199 206 return this;
200 207 }
201 208  
  209 + bool get_prefix_command() const { return prefix_command_; }
  210 +
202 211 /// Ignore case. Subcommand inherit value.
203 212 App *ignore_case(bool value = true) {
204 213 ignore_case_ = value;
... ... @@ -211,6 +220,8 @@ class App {
211 220 return this;
212 221 }
213 222  
  223 + bool get_ignore_case() const { return ignore_case_; }
  224 +
214 225 /// Check to see if this subcommand was parsed, true only if received on command line.
215 226 bool parsed() const { return parsed_; }
216 227  
... ... @@ -232,6 +243,9 @@ class App {
232 243 return this;
233 244 }
234 245  
  246 + /// Check the status of fallthrough
  247 + bool get_fallthrough() const { return fallthrough_; }
  248 +
235 249 /// Changes the group membership
236 250 App *group(std::string name) {
237 251 group_ = name;
... ... @@ -240,9 +254,9 @@ class App {
240 254  
241 255 /// Get the group of this subcommand
242 256 const std::string &get_group() const { return group_; }
243   -
  257 +
244 258 /// Get the OptionDefault object, to set option defaults
245   - OptionDefaults* option_defaults() {return &option_defaults_;}
  259 + OptionDefaults *option_defaults() { return &option_defaults_; }
246 260  
247 261 ///@}
248 262 /// @name Adding options
... ... @@ -881,7 +895,7 @@ class App {
881 895 std::set<std::string> subcmd_groups_seen;
882 896 for(const App_p &com : subcommands_) {
883 897 const std::string &group_key = detail::to_lower(com->get_group());
884   - if(group_key == "hidden" || subcmd_groups_seen.count(group_key) != 0)
  898 + if(group_key == "" || subcmd_groups_seen.count(group_key) != 0)
885 899 continue;
886 900  
887 901 subcmd_groups_seen.insert(group_key);
... ...
include/CLI/Option.hpp
... ... @@ -26,81 +26,82 @@ class App;
26 26  
27 27 using Option_p = std::unique_ptr<Option>;
28 28  
29   -template<typename CRTP>
30   -class OptionBase {
31   - friend App;
32   -
  29 +template <typename CRTP> class OptionBase {
  30 + friend App;
  31 +
33 32 protected:
34   -
35 33 /// The group membership
36 34 std::string group_{"Options"};
37 35  
38 36 /// True if this is a required option
39 37 bool required_{false};
40   -
  38 +
41 39 /// Ignore the case when matching (option, not value)
42 40 bool ignore_case_{false};
43   -
  41 +
44 42 /// Only take the last argument (requires `expected_ == 1`)
45 43 bool last_{false};
46   -
47   - template<typename T>
48   - void copy_from(T& other) {
49   - group_ = other.group_;
50   - required_ = other.required_;
51   - ignore_case_ = other.ignore_case_;
52   - last_ = other.last_;
  44 +
  45 + template <typename T> void copy_from(const T &other) {
  46 + group_ = other.get_group();
  47 + required_ = other.get_required();
  48 + ignore_case_ = other.get_ignore_case();
  49 + last_ = other.get_take_last();
53 50 }
54   -
  51 +
55 52 public:
56   -
  53 + // setters
  54 +
  55 + /// Changes the group membership
  56 + CRTP *group(std::string name) {
  57 + group_ = name;
  58 + return static_cast<CRTP *>(this);
  59 + ;
  60 + }
  61 +
57 62 /// Set the option as required
58 63 CRTP *required(bool value = true) {
59 64 required_ = value;
60   - return static_cast<CRTP*>(this);
  65 + return static_cast<CRTP *>(this);
61 66 }
62   -
  67 +
63 68 /// Support Plumbum term
64 69 CRTP *mandatory(bool value = true) { return required(value); }
65   -
66   - /// Changes the group membership
67   - CRTP *group(std::string name) {
68   - group_ = name;
69   - return static_cast<CRTP*>(this);;
70   - }
71   -
  70 +
  71 + // Getters
  72 +
  73 + /// Get the group of this option
  74 + const std::string &get_group() const { return group_; }
  75 +
72 76 /// True if this is a required option
73 77 bool get_required() const { return required_; }
74 78  
  79 + /// The status of ignore case
  80 + bool get_ignore_case() const { return ignore_case_; }
  81 +
75 82 /// The status of the take last flag
76 83 bool get_take_last() const { return last_; }
77   -
78   - /// The status of ignore case
79   - bool ignore_case() const {return ignore_case_;}
80   -
81   - /// Get the group of this option
82   - const std::string &get_group() const { return group_; }
83 84 };
84   -
85   -class OptionDefaults : public OptionBase<Option> {
  85 +
  86 +class OptionDefaults : public OptionBase<OptionDefaults> {
86 87 public:
87 88 OptionDefaults() = default;
88   -
  89 +
89 90 // Methods here need a different implementation if they are Option vs. OptionDefault
90   -
  91 +
91 92 /// Take the last argument if given multiple times
92 93 OptionDefaults *take_last(bool value = true) {
93 94 last_ = value;
94 95 return this;
95 96 }
96   -
  97 +
97 98 /// Ignore the case of the option name
98 99 OptionDefaults *ignore_case(bool value = true) {
99 100 ignore_case_ = value;
100 101 return this;
101 102 }
102 103 };
103   -
  104 +
104 105 class Option : public OptionBase<Option> {
105 106 friend App;
106 107  
... ... @@ -146,7 +147,6 @@ class Option : public OptionBase&lt;Option&gt; {
146 147 /// A private setting to allow args to not be able to accept incorrect expected values
147 148 bool changeable_{false};
148 149  
149   -
150 150 /// A list of validators to run on each value parsed
151 151 std::vector<std::function<bool(std::string)>> validators_;
152 152  
... ... @@ -226,7 +226,6 @@ class Option : public OptionBase&lt;Option&gt; {
226 226 return this;
227 227 }
228 228  
229   -
230 229 /// Sets required options
231 230 Option *requires(Option *opt) {
232 231 auto tup = requires_.insert(opt);
... ... @@ -282,12 +281,18 @@ class Option : public OptionBase&lt;Option&gt; {
282 281 /// You are never expected to add an argument to the template here.
283 282 template <typename T = App> Option *ignore_case(bool value = true) {
284 283 ignore_case_ = value;
285   - for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
  284 + T *parent = dynamic_cast<T *>(parent_);
  285 +
  286 + if(parent == nullptr)
  287 + throw IncorrectConstruction("This should not happen, there is always a parent!");
  288 +
  289 + for(const Option_p &opt : parent->options_)
286 290 if(opt.get() != this && *opt == *this)
287 291 throw OptionAlreadyAdded(opt->get_name());
  292 +
288 293 return this;
289 294 }
290   -
  295 +
291 296 /// Take the last argument if given multiple times
292 297 Option *take_last(bool value = true) {
293 298 if(get_expected() != 0 && get_expected() != 1)
... ... @@ -502,6 +507,8 @@ class Option : public OptionBase&lt;Option&gt; {
502 507 void set_custom_option(std::string typeval, int expected = 1, bool changeable = false) {
503 508 typeval_ = typeval;
504 509 expected_ = expected;
  510 + if(expected == 0)
  511 + required_ = false;
505 512 changeable_ = changeable;
506 513 }
507 514  
... ...
tests/CreationTest.cpp
... ... @@ -262,3 +262,81 @@ TEST_F(TApp, AllSpaces) {
262 262 EXPECT_TRUE(myapp->check_sname("a"));
263 263 EXPECT_TRUE(myapp->check_name("other"));
264 264 }
  265 +
  266 +TEST_F(TApp, OptionFromDefaults) {
  267 + app.option_defaults()->required();
  268 +
  269 + // Options should remember defaults
  270 + int x;
  271 + auto opt = app.add_option("--simple", x);
  272 + EXPECT_TRUE(opt->get_required());
  273 +
  274 + // Flags cannot be required
  275 + auto flag = app.add_flag("--other");
  276 + EXPECT_FALSE(flag->get_required());
  277 +
  278 + app.option_defaults()->required(false);
  279 + auto opt2 = app.add_option("--simple2", x);
  280 + EXPECT_FALSE(opt2->get_required());
  281 +
  282 + app.option_defaults()->required()->ignore_case();
  283 +
  284 + auto opt3 = app.add_option("--simple3", x);
  285 + EXPECT_TRUE(opt3->get_required());
  286 + EXPECT_TRUE(opt3->get_ignore_case());
  287 +}
  288 +
  289 +TEST_F(TApp, OptionFromDefaultsSubcommands) {
  290 + // Initial defaults
  291 + EXPECT_FALSE(app.option_defaults()->get_required());
  292 + EXPECT_FALSE(app.option_defaults()->get_take_last());
  293 + EXPECT_FALSE(app.option_defaults()->get_ignore_case());
  294 + EXPECT_EQ(app.option_defaults()->get_group(), "Options");
  295 +
  296 + app.option_defaults()->required()->take_last()->ignore_case()->group("Something");
  297 +
  298 + auto app2 = app.add_subcommand("app2");
  299 +
  300 + EXPECT_TRUE(app2->option_defaults()->get_required());
  301 + EXPECT_TRUE(app2->option_defaults()->get_take_last());
  302 + EXPECT_TRUE(app2->option_defaults()->get_ignore_case());
  303 + EXPECT_EQ(app2->option_defaults()->get_group(), "Something");
  304 +}
  305 +
  306 +TEST_F(TApp, HelpFlagFromDefaultsSubcommands) {
  307 + app.set_help_flag("--that", "Wow");
  308 +
  309 + auto app2 = app.add_subcommand("app2");
  310 +
  311 + EXPECT_EQ(app2->get_help_ptr()->get_name(), "--that");
  312 + EXPECT_EQ(app2->get_help_ptr()->get_description(), "Wow");
  313 +}
  314 +
  315 +TEST_F(TApp, SubcommandDefaults) {
  316 + // allow_extras, prefix_command, ignore_case, fallthrough, group
  317 +
  318 + // Initial defaults
  319 + EXPECT_FALSE(app.get_allow_extras());
  320 + EXPECT_FALSE(app.get_prefix_command());
  321 + EXPECT_FALSE(app.get_ignore_case());
  322 + EXPECT_FALSE(app.get_fallthrough());
  323 + EXPECT_EQ(app.get_footer(), "");
  324 + EXPECT_EQ(app.get_group(), "Subcommands");
  325 +
  326 + app.allow_extras();
  327 + app.prefix_command();
  328 + app.ignore_case();
  329 + app.fallthrough();
  330 + app.set_footer("footy");
  331 + app.group("Stuff");
  332 +
  333 + auto app2 = app.add_subcommand("app2");
  334 +
  335 + // Initial defaults
  336 + EXPECT_TRUE(app2->get_allow_extras());
  337 + EXPECT_TRUE(app2->get_prefix_command());
  338 + EXPECT_TRUE(app2->get_ignore_case());
  339 + EXPECT_TRUE(app2->get_fallthrough());
  340 + EXPECT_EQ(app2->get_footer(), "footy");
  341 + EXPECT_EQ(app2->get_group(), "Stuff");
  342 +}
... ...