mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
Adding first draft of ini support for App
This commit is contained in:
parent
38f746db3a
commit
67047b71f8
@ -21,6 +21,7 @@
|
||||
#include "CLI/StringTools.hpp"
|
||||
#include "CLI/Split.hpp"
|
||||
#include "CLI/Option.hpp"
|
||||
#include "CLI/Ini.hpp"
|
||||
|
||||
namespace CLI {
|
||||
|
||||
@ -44,13 +45,17 @@ protected:
|
||||
std::vector<std::string> missing_options;
|
||||
std::deque<std::string> positionals;
|
||||
std::vector<App_p> subcommands;
|
||||
bool parsed{false};
|
||||
App* subcommand{nullptr};
|
||||
std::string progname{"program"};
|
||||
bool parsed {false};
|
||||
App* subcommand {nullptr};
|
||||
std::string progname {"program"};
|
||||
Option* help_flag {nullptr};
|
||||
|
||||
std::function<void()> app_callback;
|
||||
|
||||
std::string ini_file;
|
||||
bool ini_required {false};
|
||||
Option* ini_setting {nullptr};
|
||||
|
||||
public:
|
||||
|
||||
/// Set a callback for the end of parsing. Due to a bug in c++11,
|
||||
@ -284,6 +289,25 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/// Add a configuration ini file option
|
||||
void add_config(std::string name="--config",
|
||||
std::string default_filename="",
|
||||
std::string help="Read an ini file",
|
||||
bool required=false) {
|
||||
|
||||
// Remove existing config if present
|
||||
if(ini_setting != nullptr) {
|
||||
auto iterator = std::find_if(std::begin(options), std::end(options),
|
||||
[this](const Option_p &v){return v.get() == ini_setting;});
|
||||
if (iterator != std::end(options)) {
|
||||
options.erase(iterator);
|
||||
}
|
||||
}
|
||||
ini_file = default_filename;
|
||||
ini_required = required;
|
||||
ini_setting = add_option(name, ini_file, help, default_filename!="");
|
||||
}
|
||||
|
||||
/// This allows subclasses to inject code before callbacks but after parse
|
||||
virtual void pre_callback() {}
|
||||
|
||||
@ -297,7 +321,7 @@ public:
|
||||
}
|
||||
|
||||
/// The real work is done here. Expects a reversed vector
|
||||
void parse(std::vector<std::string> & args) {
|
||||
void parse(std::vector<std::string> & args, bool first_parse=true) {
|
||||
parsed = true;
|
||||
|
||||
bool positional_only = false;
|
||||
@ -331,21 +355,36 @@ public:
|
||||
}
|
||||
|
||||
|
||||
|
||||
for(const Option_p& opt : options) {
|
||||
while (opt->get_positional() && opt->count() < opt->get_expected() && positionals.size() > 0) {
|
||||
opt->get_new();
|
||||
opt->add_result(0, positionals.front());
|
||||
positionals.pop_front();
|
||||
}
|
||||
if (opt->get_required() && opt->count() < opt->get_expected())
|
||||
throw RequiredError(opt->get_name());
|
||||
if (opt->count() > 0) {
|
||||
if(!opt->run_callback())
|
||||
throw ConversionError(opt->get_name());
|
||||
throw ConversionError(opt->get_name() + "=" + detail::join(opt->flatten_results()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (first_parse && ini_setting != nullptr && ini_file != "") {
|
||||
try {
|
||||
std::vector<std::string> values = detail::parse_ini(ini_file);
|
||||
std::reverse(std::begin(values), std::end(values));
|
||||
|
||||
values.insert(std::begin(values), std::begin(positionals), std::end(positionals));
|
||||
return parse(values, false);
|
||||
} catch (const FileError &e) {
|
||||
if(ini_required)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
for(const Option_p& opt : options) {
|
||||
if (opt->get_required() && opt->count() < opt->get_expected())
|
||||
throw RequiredError(opt->get_name());
|
||||
}
|
||||
|
||||
if(positionals.size()>0)
|
||||
throw PositionalError("[" + detail::join(positionals) + "]");
|
||||
|
||||
@ -401,7 +440,6 @@ public:
|
||||
while(args.size()>0 && _recognize(args.back()) == Classifer::NONE) {
|
||||
op->add_result(vnum, args.back());
|
||||
args.pop_back();
|
||||
|
||||
}
|
||||
} else while(num>0 && args.size() > 0) {
|
||||
num--;
|
||||
|
@ -91,6 +91,16 @@ TEST_F(TApp, OneString) {
|
||||
EXPECT_EQ(str, "mystring");
|
||||
}
|
||||
|
||||
TEST_F(TApp, OneStringEqualVersion) {
|
||||
std::string str;
|
||||
app.add_option("-s,--string", str);
|
||||
args = {"--string=mystring"};
|
||||
EXPECT_NO_THROW(run());
|
||||
EXPECT_EQ(1, app.count("-s"));
|
||||
EXPECT_EQ(1, app.count("--string"));
|
||||
EXPECT_EQ(str, "mystring");
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TApp, TogetherInt) {
|
||||
int i;
|
||||
@ -383,8 +393,63 @@ TEST_F(TApp, VectorFancyOpts) {
|
||||
EXPECT_THROW(run(), CLI::ParseError);
|
||||
}
|
||||
|
||||
struct TIni : public TApp {
|
||||
|
||||
std::ofstream f{"IniParseSimple.ini"};
|
||||
|
||||
void run() {
|
||||
f.close();
|
||||
TApp::run();
|
||||
}
|
||||
|
||||
~TIni() {
|
||||
f.close();
|
||||
std::remove("IniParseSimple.ini");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
TEST_F(TIni, IniParseSimple) {
|
||||
|
||||
int x;
|
||||
std::string y;
|
||||
|
||||
app.add_option("--something", x);
|
||||
app.add_option("--else", y);
|
||||
|
||||
app.add_config("--config","", "", true);
|
||||
|
||||
args = {"--config=IniParseSimple.ini"};
|
||||
|
||||
|
||||
ASSERT_TRUE(f.good());
|
||||
|
||||
f << "[default]" << std::endl;
|
||||
f << "" << std::endl;
|
||||
f << "something=7" << std::endl;
|
||||
f << "else=seven" << std::endl;
|
||||
|
||||
//EXPECT_NO_THROW
|
||||
(run());
|
||||
|
||||
EXPECT_EQ(7, x);
|
||||
EXPECT_EQ("seven", y);
|
||||
}
|
||||
|
||||
|
||||
TEST(Ini, IniDoubleAdd) {
|
||||
|
||||
CLI::App app;
|
||||
|
||||
app.add_config("--first");
|
||||
app.add_config("--second");
|
||||
|
||||
EXPECT_NO_THROW(app.count("--second"));
|
||||
EXPECT_THROW(app.count("--first"), CLI::OptionNotFound);
|
||||
|
||||
}
|
||||
TEST_F(TApp, BasicSubcommands) {
|
||||
auto sub1 = app.add_subcommand("sub1");
|
||||
auto sub2 = app.add_subcommand("sub2");
|
||||
|
4
tests/IniParseSimple.ini
Normal file
4
tests/IniParseSimple.ini
Normal file
@ -0,0 +1,4 @@
|
||||
[default]
|
||||
|
||||
something=7
|
||||
else=seven
|
Loading…
x
Reference in New Issue
Block a user