1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-05-01 05:03:52 +00:00

Adding tests for inhert, a few fixes

This commit is contained in:
Henry Fredrick Schreiner 2017-11-20 16:03:19 -05:00 committed by Henry Schreiner
parent 9b5867869d
commit fa5da7deaa
3 changed files with 149 additions and 50 deletions

View File

@ -143,7 +143,7 @@ class App {
///@} ///@}
/// Special private constructor for subcommand /// Special private constructor for subcommand
App(std::string description_, App* parent) : description_(std::move(description_)), parent_(parent) { App(std::string description_, App *parent) : description_(std::move(description_)), parent_(parent) {
// Inherit if not from a nullptr // Inherit if not from a nullptr
if(parent_ != nullptr) { if(parent_ != nullptr) {
if(parent_->help_ptr_ != nullptr) if(parent_->help_ptr_ != nullptr)
@ -158,6 +158,7 @@ class App {
ignore_case_ = parent_->ignore_case_; ignore_case_ = parent_->ignore_case_;
fallthrough_ = parent_->fallthrough_; fallthrough_ = parent_->fallthrough_;
group_ = parent_->group_; group_ = parent_->group_;
footer_ = parent_->footer_;
} }
} }
@ -176,6 +177,9 @@ class App {
return this; return this;
} }
/// Get footer.
std::string get_footer() const { return footer_; }
/// Set a callback for the end of parsing. /// Set a callback for the end of parsing.
/// ///
/// Due to a bug in c++11, /// Due to a bug in c++11,
@ -193,12 +197,17 @@ class App {
return this; return this;
} }
/// Get the status of allow extras
bool get_allow_extras() const { return allow_extras_; }
/// Do not parse anything after the first unrecognised option and return /// Do not parse anything after the first unrecognised option and return
App *prefix_command(bool allow = true) { App *prefix_command(bool allow = true) {
prefix_command_ = allow; prefix_command_ = allow;
return this; return this;
} }
bool get_prefix_command() const { return prefix_command_; }
/// Ignore case. Subcommand inherit value. /// Ignore case. Subcommand inherit value.
App *ignore_case(bool value = true) { App *ignore_case(bool value = true) {
ignore_case_ = value; ignore_case_ = value;
@ -211,6 +220,8 @@ class App {
return this; return this;
} }
bool get_ignore_case() const { return ignore_case_; }
/// Check to see if this subcommand was parsed, true only if received on command line. /// Check to see if this subcommand was parsed, true only if received on command line.
bool parsed() const { return parsed_; } bool parsed() const { return parsed_; }
@ -232,6 +243,9 @@ class App {
return this; return this;
} }
/// Check the status of fallthrough
bool get_fallthrough() const { return fallthrough_; }
/// Changes the group membership /// Changes the group membership
App *group(std::string name) { App *group(std::string name) {
group_ = name; group_ = name;
@ -242,7 +256,7 @@ class App {
const std::string &get_group() const { return group_; } const std::string &get_group() const { return group_; }
/// Get the OptionDefault object, to set option defaults /// Get the OptionDefault object, to set option defaults
OptionDefaults* option_defaults() {return &option_defaults_;} OptionDefaults *option_defaults() { return &option_defaults_; }
///@} ///@}
/// @name Adding options /// @name Adding options
@ -881,7 +895,7 @@ class App {
std::set<std::string> subcmd_groups_seen; std::set<std::string> subcmd_groups_seen;
for(const App_p &com : subcommands_) { for(const App_p &com : subcommands_) {
const std::string &group_key = detail::to_lower(com->get_group()); const std::string &group_key = detail::to_lower(com->get_group());
if(group_key == "hidden" || subcmd_groups_seen.count(group_key) != 0) if(group_key == "" || subcmd_groups_seen.count(group_key) != 0)
continue; continue;
subcmd_groups_seen.insert(group_key); subcmd_groups_seen.insert(group_key);

View File

@ -26,12 +26,10 @@ class App;
using Option_p = std::unique_ptr<Option>; using Option_p = std::unique_ptr<Option>;
template<typename CRTP> template <typename CRTP> class OptionBase {
class OptionBase {
friend App; friend App;
protected: protected:
/// The group membership /// The group membership
std::string group_{"Options"}; std::string group_{"Options"};
@ -44,45 +42,48 @@ class OptionBase {
/// Only take the last argument (requires `expected_ == 1`) /// Only take the last argument (requires `expected_ == 1`)
bool last_{false}; bool last_{false};
template<typename T> template <typename T> void copy_from(const T &other) {
void copy_from(T& other) { group_ = other.get_group();
group_ = other.group_; required_ = other.get_required();
required_ = other.required_; ignore_case_ = other.get_ignore_case();
ignore_case_ = other.ignore_case_; last_ = other.get_take_last();
last_ = other.last_;
} }
public: public:
// setters
/// Changes the group membership
CRTP *group(std::string name) {
group_ = name;
return static_cast<CRTP *>(this);
;
}
/// Set the option as required /// Set the option as required
CRTP *required(bool value = true) { CRTP *required(bool value = true) {
required_ = value; required_ = value;
return static_cast<CRTP*>(this); return static_cast<CRTP *>(this);
} }
/// Support Plumbum term /// Support Plumbum term
CRTP *mandatory(bool value = true) { return required(value); } CRTP *mandatory(bool value = true) { return required(value); }
/// Changes the group membership // Getters
CRTP *group(std::string name) {
group_ = name; /// Get the group of this option
return static_cast<CRTP*>(this);; const std::string &get_group() const { return group_; }
}
/// True if this is a required option /// True if this is a required option
bool get_required() const { return required_; } bool get_required() const { return required_; }
/// The status of ignore case
bool get_ignore_case() const { return ignore_case_; }
/// The status of the take last flag /// The status of the take last flag
bool get_take_last() const { return last_; } bool get_take_last() const { return last_; }
/// The status of ignore case
bool ignore_case() const {return ignore_case_;}
/// Get the group of this option
const std::string &get_group() const { return group_; }
}; };
class OptionDefaults : public OptionBase<Option> { class OptionDefaults : public OptionBase<OptionDefaults> {
public: public:
OptionDefaults() = default; OptionDefaults() = default;
@ -146,7 +147,6 @@ class Option : public OptionBase<Option> {
/// A private setting to allow args to not be able to accept incorrect expected values /// A private setting to allow args to not be able to accept incorrect expected values
bool changeable_{false}; bool changeable_{false};
/// A list of validators to run on each value parsed /// A list of validators to run on each value parsed
std::vector<std::function<bool(std::string)>> validators_; std::vector<std::function<bool(std::string)>> validators_;
@ -226,7 +226,6 @@ class Option : public OptionBase<Option> {
return this; return this;
} }
/// Sets required options /// Sets required options
Option *requires(Option *opt) { Option *requires(Option *opt) {
auto tup = requires_.insert(opt); auto tup = requires_.insert(opt);
@ -282,9 +281,15 @@ class Option : public OptionBase<Option> {
/// You are never expected to add an argument to the template here. /// You are never expected to add an argument to the template here.
template <typename T = App> Option *ignore_case(bool value = true) { template <typename T = App> Option *ignore_case(bool value = true) {
ignore_case_ = value; ignore_case_ = value;
for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_) T *parent = dynamic_cast<T *>(parent_);
if(parent == nullptr)
throw IncorrectConstruction("This should not happen, there is always a parent!");
for(const Option_p &opt : parent->options_)
if(opt.get() != this && *opt == *this) if(opt.get() != this && *opt == *this)
throw OptionAlreadyAdded(opt->get_name()); throw OptionAlreadyAdded(opt->get_name());
return this; return this;
} }
@ -502,6 +507,8 @@ class Option : public OptionBase<Option> {
void set_custom_option(std::string typeval, int expected = 1, bool changeable = false) { void set_custom_option(std::string typeval, int expected = 1, bool changeable = false) {
typeval_ = typeval; typeval_ = typeval;
expected_ = expected; expected_ = expected;
if(expected == 0)
required_ = false;
changeable_ = changeable; changeable_ = changeable;
} }

View File

@ -262,3 +262,81 @@ TEST_F(TApp, AllSpaces) {
EXPECT_TRUE(myapp->check_sname("a")); EXPECT_TRUE(myapp->check_sname("a"));
EXPECT_TRUE(myapp->check_name("other")); EXPECT_TRUE(myapp->check_name("other"));
} }
TEST_F(TApp, OptionFromDefaults) {
app.option_defaults()->required();
// Options should remember defaults
int x;
auto opt = app.add_option("--simple", x);
EXPECT_TRUE(opt->get_required());
// Flags cannot be required
auto flag = app.add_flag("--other");
EXPECT_FALSE(flag->get_required());
app.option_defaults()->required(false);
auto opt2 = app.add_option("--simple2", x);
EXPECT_FALSE(opt2->get_required());
app.option_defaults()->required()->ignore_case();
auto opt3 = app.add_option("--simple3", x);
EXPECT_TRUE(opt3->get_required());
EXPECT_TRUE(opt3->get_ignore_case());
}
TEST_F(TApp, OptionFromDefaultsSubcommands) {
// Initial defaults
EXPECT_FALSE(app.option_defaults()->get_required());
EXPECT_FALSE(app.option_defaults()->get_take_last());
EXPECT_FALSE(app.option_defaults()->get_ignore_case());
EXPECT_EQ(app.option_defaults()->get_group(), "Options");
app.option_defaults()->required()->take_last()->ignore_case()->group("Something");
auto app2 = app.add_subcommand("app2");
EXPECT_TRUE(app2->option_defaults()->get_required());
EXPECT_TRUE(app2->option_defaults()->get_take_last());
EXPECT_TRUE(app2->option_defaults()->get_ignore_case());
EXPECT_EQ(app2->option_defaults()->get_group(), "Something");
}
TEST_F(TApp, HelpFlagFromDefaultsSubcommands) {
app.set_help_flag("--that", "Wow");
auto app2 = app.add_subcommand("app2");
EXPECT_EQ(app2->get_help_ptr()->get_name(), "--that");
EXPECT_EQ(app2->get_help_ptr()->get_description(), "Wow");
}
TEST_F(TApp, SubcommandDefaults) {
// allow_extras, prefix_command, ignore_case, fallthrough, group
// Initial defaults
EXPECT_FALSE(app.get_allow_extras());
EXPECT_FALSE(app.get_prefix_command());
EXPECT_FALSE(app.get_ignore_case());
EXPECT_FALSE(app.get_fallthrough());
EXPECT_EQ(app.get_footer(), "");
EXPECT_EQ(app.get_group(), "Subcommands");
app.allow_extras();
app.prefix_command();
app.ignore_case();
app.fallthrough();
app.set_footer("footy");
app.group("Stuff");
auto app2 = app.add_subcommand("app2");
// Initial defaults
EXPECT_TRUE(app2->get_allow_extras());
EXPECT_TRUE(app2->get_prefix_command());
EXPECT_TRUE(app2->get_ignore_case());
EXPECT_TRUE(app2->get_fallthrough());
EXPECT_EQ(app2->get_footer(), "footy");
EXPECT_EQ(app2->get_group(), "Stuff");
}