mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
Version add (#452)
* Add a dedicated version option to CLI11 to facilitate use of version flags, similar to help flags * add some test for the version flag * update errors and formatting * clear up gcc 4.8 warnings * add a few more tests * fix compiler error * fix a few comments, and change default version flag to only use "--version" * remove `version` calls and tests * formatting and add `std::string version()` back in.
This commit is contained in:
parent
fff3350254
commit
41a9c294d0
@ -54,6 +54,9 @@ set_property(TEST simple_all PROPERTY PASS_REGULAR_EXPRESSION
|
||||
"Received flag: 2 (2) times"
|
||||
"Some value: 1.2")
|
||||
|
||||
add_test(NAME simple_version COMMAND simple --version)
|
||||
set_property(TEST simple_version PROPERTY PASS_REGULAR_EXPRESSION
|
||||
"${CLI11_VERSION}")
|
||||
|
||||
add_cli_exe(subcommands subcommands.cpp)
|
||||
add_test(NAME subcommands_none COMMAND subcommands)
|
||||
|
@ -11,7 +11,8 @@
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
CLI::App app("K3Pi goofit fitter");
|
||||
|
||||
// add version output
|
||||
app.set_version_flag("--version", std::string(CLI11_VERSION));
|
||||
std::string file;
|
||||
CLI::Option *opt = app.add_option("-f,--file,file", file, "File name");
|
||||
|
||||
|
@ -139,6 +139,9 @@ class App {
|
||||
/// A pointer to the help all flag if there is one INHERITABLE
|
||||
Option *help_all_ptr_{nullptr};
|
||||
|
||||
/// A pointer to a version flag if there is one
|
||||
Option *version_ptr_{nullptr};
|
||||
|
||||
/// This is the formatter for help printing. Default provided. INHERITABLE (same pointer)
|
||||
std::shared_ptr<FormatterBase> formatter_{new Formatter()};
|
||||
|
||||
@ -703,6 +706,45 @@ class App {
|
||||
return help_all_ptr_;
|
||||
}
|
||||
|
||||
/// Set a version flag and version display string, replace the existing one if present
|
||||
Option *set_version_flag(std::string flag_name = "", const std::string &versionString = "") {
|
||||
// take flag_description by const reference otherwise add_flag tries to assign to version_description
|
||||
if(version_ptr_ != nullptr) {
|
||||
remove_option(version_ptr_);
|
||||
version_ptr_ = nullptr;
|
||||
}
|
||||
|
||||
// Empty name will simply remove the version flag
|
||||
if(!flag_name.empty()) {
|
||||
version_ptr_ = add_flag_callback(
|
||||
flag_name,
|
||||
[versionString]() { throw(CLI::CallForVersion(versionString, 0)); },
|
||||
"display program version information and exit");
|
||||
version_ptr_->configurable(false);
|
||||
}
|
||||
|
||||
return version_ptr_;
|
||||
}
|
||||
/// Generate the version string through a callback function
|
||||
Option *set_version_flag(std::string flag_name, std::function<std::string()> vfunc) {
|
||||
// take flag_description by const reference otherwise add_flag tries to assign to version_description
|
||||
if(version_ptr_ != nullptr) {
|
||||
remove_option(version_ptr_);
|
||||
version_ptr_ = nullptr;
|
||||
}
|
||||
|
||||
// Empty name will simply remove the version flag
|
||||
if(!flag_name.empty()) {
|
||||
version_ptr_ = add_flag_callback(
|
||||
flag_name,
|
||||
[vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); },
|
||||
"display program version information and exit");
|
||||
version_ptr_->configurable(false);
|
||||
}
|
||||
|
||||
return version_ptr_;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Internal function for adding a flag
|
||||
Option *_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description) {
|
||||
@ -1345,6 +1387,11 @@ class App {
|
||||
return e.get_exit_code();
|
||||
}
|
||||
|
||||
if(dynamic_cast<const CLI::CallForVersion *>(&e) != nullptr) {
|
||||
out << e.what() << std::endl;
|
||||
return e.get_exit_code();
|
||||
}
|
||||
|
||||
if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {
|
||||
if(failure_message_)
|
||||
err << failure_message_(this, e) << std::flush;
|
||||
@ -1530,6 +1577,23 @@ class App {
|
||||
return formatter_->make_help(this, prev, mode);
|
||||
}
|
||||
|
||||
/// Displays a version string
|
||||
std::string version() const {
|
||||
std::string val;
|
||||
if(version_ptr_ != nullptr) {
|
||||
auto rv = version_ptr_->results();
|
||||
version_ptr_->clear();
|
||||
version_ptr_->add_result("true");
|
||||
try {
|
||||
version_ptr_->run_callback();
|
||||
} catch(const CLI::CallForVersion &cfv) {
|
||||
val = cfv.what();
|
||||
}
|
||||
version_ptr_->clear();
|
||||
version_ptr_->add_result(rv);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
///@}
|
||||
/// @name Getters
|
||||
///@{
|
||||
@ -1726,6 +1790,12 @@ class App {
|
||||
/// Get a pointer to the config option. (const)
|
||||
const Option *get_config_ptr() const { return config_ptr_; }
|
||||
|
||||
/// Get a pointer to the version option.
|
||||
Option *get_version_ptr() { return version_ptr_; }
|
||||
|
||||
/// Get a pointer to the version option. (const)
|
||||
const Option *get_version_ptr() const { return version_ptr_; }
|
||||
|
||||
/// Get the parent of this subcommand (or nullptr if master app)
|
||||
App *get_parent() { return parent_; }
|
||||
|
||||
|
@ -157,18 +157,25 @@ class Success : public ParseError {
|
||||
};
|
||||
|
||||
/// -h or --help on command line
|
||||
class CallForHelp : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, CallForHelp)
|
||||
class CallForHelp : public Success {
|
||||
CLI11_ERROR_DEF(Success, CallForHelp)
|
||||
CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
|
||||
};
|
||||
|
||||
/// Usually something like --help-all on command line
|
||||
class CallForAllHelp : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, CallForAllHelp)
|
||||
class CallForAllHelp : public Success {
|
||||
CLI11_ERROR_DEF(Success, CallForAllHelp)
|
||||
CallForAllHelp()
|
||||
: CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
|
||||
};
|
||||
|
||||
/// -v or --version on command line
|
||||
class CallForVersion : public Success {
|
||||
CLI11_ERROR_DEF(Success, CallForVersion)
|
||||
CallForVersion()
|
||||
: CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {}
|
||||
};
|
||||
|
||||
/// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code.
|
||||
class RuntimeError : public ParseError {
|
||||
CLI11_ERROR_DEF(ParseError, RuntimeError)
|
||||
|
@ -190,7 +190,7 @@ inline bool valid_name_string(const std::string &str) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// check if a string is a container segment separator (empty or "%%"
|
||||
/// check if a string is a container segment separator (empty or "%%")
|
||||
inline bool is_separator(const std::string &str) {
|
||||
static const std::string sep("%%");
|
||||
return (str.empty() || str == sep);
|
||||
|
@ -232,7 +232,7 @@ struct is_mutable_container<
|
||||
// check to see if an object is a mutable container (fail by default)
|
||||
template <typename T, typename _ = void> struct is_readable_container : std::false_type {};
|
||||
|
||||
/// type trait to test if a type is a container meaning it has a value_type, it has an iterator, a clear, and an en end
|
||||
/// type trait to test if a type is a container meaning it has a value_type, it has an iterator, a clear, and an end
|
||||
/// methods and an insert function. And for our purposes we exclude std::string and types that can be constructed from
|
||||
/// a std::string
|
||||
template <typename T>
|
||||
@ -244,7 +244,7 @@ struct is_readable_container<
|
||||
// check to see if an object is a wrapper (fail by default)
|
||||
template <typename T, typename _ = void> struct is_wrapper : std::false_type {};
|
||||
|
||||
// check if an object is a is a wrapper (it has a value_type defined)
|
||||
// check if an object is a wrapper (it has a value_type defined)
|
||||
template <typename T>
|
||||
struct is_wrapper<T, conditional_t<false, void_t<typename T::value_type>, void>> : public std::true_type {};
|
||||
|
||||
@ -344,7 +344,7 @@ auto value_string(const T &value) -> decltype(to_string(value)) {
|
||||
return to_string(value);
|
||||
}
|
||||
|
||||
/// temple to get the underlying value type if it exists or use a default
|
||||
/// template to get the underlying value type if it exists or use a default
|
||||
template <typename T, typename def, typename Enable = void> struct wrapped_type { using type = def; };
|
||||
|
||||
/// Type size for regular object types that do not look like a tuple
|
||||
|
@ -1165,3 +1165,53 @@ TEST(THelp, FunctionDefaultString) {
|
||||
|
||||
EXPECT_THAT(help, HasSubstr("INT=Powerful"));
|
||||
}
|
||||
|
||||
TEST(TVersion, simple_flag) {
|
||||
|
||||
CLI::App app;
|
||||
|
||||
app.set_version_flag("-v,--version", "VERSION " CLI11_VERSION);
|
||||
|
||||
auto vers = app.version();
|
||||
EXPECT_THAT(vers, HasSubstr("VERSION"));
|
||||
|
||||
app.set_version_flag();
|
||||
EXPECT_TRUE(app.version().empty());
|
||||
}
|
||||
|
||||
TEST(TVersion, callback_flag) {
|
||||
|
||||
CLI::App app;
|
||||
|
||||
app.set_version_flag("-v,--version", []() { return std::string("VERSION " CLI11_VERSION); });
|
||||
|
||||
auto vers = app.version();
|
||||
EXPECT_THAT(vers, HasSubstr("VERSION"));
|
||||
|
||||
app.set_version_flag("-v", []() { return std::string("VERSION2 " CLI11_VERSION); });
|
||||
vers = app.version();
|
||||
EXPECT_THAT(vers, HasSubstr("VERSION"));
|
||||
}
|
||||
|
||||
TEST(TVersion, parse_throw) {
|
||||
|
||||
CLI::App app;
|
||||
|
||||
app.set_version_flag("--version", CLI11_VERSION);
|
||||
|
||||
EXPECT_THROW(app.parse("--version"), CLI::CallForVersion);
|
||||
EXPECT_THROW(app.parse("--version --arg2 5"), CLI::CallForVersion);
|
||||
|
||||
auto ptr = app.get_version_ptr();
|
||||
|
||||
ptr->ignore_case();
|
||||
try {
|
||||
app.parse("--Version");
|
||||
} catch(const CLI::CallForVersion &v) {
|
||||
EXPECT_STREQ(v.what(), CLI11_VERSION);
|
||||
EXPECT_EQ(v.get_exit_code(), 0);
|
||||
const auto &appc = app;
|
||||
auto cptr = appc.get_version_ptr();
|
||||
EXPECT_EQ(cptr->count(), 1U);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user