mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 04:33:53 +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/StringTools.hpp"
|
||||||
#include "CLI/Split.hpp"
|
#include "CLI/Split.hpp"
|
||||||
#include "CLI/Option.hpp"
|
#include "CLI/Option.hpp"
|
||||||
|
#include "CLI/Ini.hpp"
|
||||||
|
|
||||||
namespace CLI {
|
namespace CLI {
|
||||||
|
|
||||||
@ -51,6 +52,10 @@ protected:
|
|||||||
|
|
||||||
std::function<void()> app_callback;
|
std::function<void()> app_callback;
|
||||||
|
|
||||||
|
std::string ini_file;
|
||||||
|
bool ini_required {false};
|
||||||
|
Option* ini_setting {nullptr};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Set a callback for the end of parsing. Due to a bug in c++11,
|
/// 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
|
/// This allows subclasses to inject code before callbacks but after parse
|
||||||
virtual void pre_callback() {}
|
virtual void pre_callback() {}
|
||||||
|
|
||||||
@ -297,7 +321,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The real work is done here. Expects a reversed vector
|
/// 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;
|
parsed = true;
|
||||||
|
|
||||||
bool positional_only = false;
|
bool positional_only = false;
|
||||||
@ -331,21 +355,36 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for(const Option_p& opt : options) {
|
for(const Option_p& opt : options) {
|
||||||
while (opt->get_positional() && opt->count() < opt->get_expected() && positionals.size() > 0) {
|
while (opt->get_positional() && opt->count() < opt->get_expected() && positionals.size() > 0) {
|
||||||
opt->get_new();
|
opt->get_new();
|
||||||
opt->add_result(0, positionals.front());
|
opt->add_result(0, positionals.front());
|
||||||
positionals.pop_front();
|
positionals.pop_front();
|
||||||
}
|
}
|
||||||
if (opt->get_required() && opt->count() < opt->get_expected())
|
|
||||||
throw RequiredError(opt->get_name());
|
|
||||||
if (opt->count() > 0) {
|
if (opt->count() > 0) {
|
||||||
if(!opt->run_callback())
|
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)
|
if(positionals.size()>0)
|
||||||
throw PositionalError("[" + detail::join(positionals) + "]");
|
throw PositionalError("[" + detail::join(positionals) + "]");
|
||||||
|
|
||||||
@ -401,7 +440,6 @@ public:
|
|||||||
while(args.size()>0 && _recognize(args.back()) == Classifer::NONE) {
|
while(args.size()>0 && _recognize(args.back()) == Classifer::NONE) {
|
||||||
op->add_result(vnum, args.back());
|
op->add_result(vnum, args.back());
|
||||||
args.pop_back();
|
args.pop_back();
|
||||||
|
|
||||||
}
|
}
|
||||||
} else while(num>0 && args.size() > 0) {
|
} else while(num>0 && args.size() > 0) {
|
||||||
num--;
|
num--;
|
||||||
|
@ -91,6 +91,16 @@ TEST_F(TApp, OneString) {
|
|||||||
EXPECT_EQ(str, "mystring");
|
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) {
|
TEST_F(TApp, TogetherInt) {
|
||||||
int i;
|
int i;
|
||||||
@ -383,8 +393,63 @@ TEST_F(TApp, VectorFancyOpts) {
|
|||||||
EXPECT_THROW(run(), CLI::ParseError);
|
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) {
|
TEST_F(TApp, BasicSubcommands) {
|
||||||
auto sub1 = app.add_subcommand("sub1");
|
auto sub1 = app.add_subcommand("sub1");
|
||||||
auto sub2 = app.add_subcommand("sub2");
|
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