1
0
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:
Henry Fredrick Schreiner 2017-02-10 17:21:19 -05:00
parent 38f746db3a
commit 67047b71f8
3 changed files with 117 additions and 10 deletions

View File

@ -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--;

View File

@ -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
View File

@ -0,0 +1,4 @@
[default]
something=7
else=seven