mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 04:33:53 +00:00
Add an ability to deal handle multiple config files (#494)
This commit is contained in:
parent
8ce1594eae
commit
6aa58d5828
@ -748,6 +748,14 @@ Spaces before and after the name and argument are ignored. Multiple arguments ar
|
|||||||
To print a configuration file from the passed
|
To print a configuration file from the passed
|
||||||
arguments, use `.config_to_str(default_also=false, write_description=false)`, where `default_also` will also show any defaulted arguments, and `write_description` will include the app and option descriptions. See [Config files](https://cliutils.github.io/CLI11/book/chapters/config.html) for some additional details.
|
arguments, use `.config_to_str(default_also=false, write_description=false)`, where `default_also` will also show any defaulted arguments, and `write_description` will include the app and option descriptions. See [Config files](https://cliutils.github.io/CLI11/book/chapters/config.html) for some additional details.
|
||||||
|
|
||||||
|
If it is desired that multiple configuration be allowed. Use
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
app.set_config("--config")->expected(1, X);
|
||||||
|
```
|
||||||
|
|
||||||
|
Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments.
|
||||||
|
|
||||||
### Inheriting defaults
|
### Inheriting defaults
|
||||||
|
|
||||||
Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`, `footer`,`immediate_callback` and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well.
|
Many of the defaults for subcommands and even options are inherited from their creators. The inherited default values for subcommands are `allow_extras`, `prefix_command`, `ignore_case`, `ignore_underscore`, `fallthrough`, `group`, `footer`,`immediate_callback` and maximum number of required subcommands. The help flag existence, name, and description are inherited, as well.
|
||||||
|
@ -78,6 +78,16 @@ sub.subcommand = true
|
|||||||
|
|
||||||
The main differences are in vector notation and comment character. Note: CLI11 is not a full TOML parser as it just reads values as strings. It is possible (but not recommended) to mix notation.
|
The main differences are in vector notation and comment character. Note: CLI11 is not a full TOML parser as it just reads values as strings. It is possible (but not recommended) to mix notation.
|
||||||
|
|
||||||
|
## Multiple configuration files
|
||||||
|
|
||||||
|
If it is desired that multiple configuration be allowed. Use
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
app.set_config("--config")->expected(1, X);
|
||||||
|
```
|
||||||
|
|
||||||
|
Where X is some positive integer and will allow up to `X` configuration files to be specified by separate `--config` arguments.
|
||||||
|
|
||||||
## Writing out a configure file
|
## Writing out a configure file
|
||||||
|
|
||||||
To print a configuration file from the passed arguments, use `.config_to_str(default_also=false, write_description=false)`, where `default_also` will also show any defaulted arguments, and `write_description` will include option descriptions and the App description
|
To print a configuration file from the passed arguments, use `.config_to_str(default_also=false, write_description=false)`, where `default_also` will also show any defaulted arguments, and `write_description` will include option descriptions and the App description
|
||||||
|
@ -2053,15 +2053,16 @@ class App {
|
|||||||
void _process_config_file() {
|
void _process_config_file() {
|
||||||
if(config_ptr_ != nullptr) {
|
if(config_ptr_ != nullptr) {
|
||||||
bool config_required = config_ptr_->get_required();
|
bool config_required = config_ptr_->get_required();
|
||||||
bool file_given = config_ptr_->count() > 0;
|
auto file_given = config_ptr_->count() > 0;
|
||||||
auto config_file = config_ptr_->as<std::string>();
|
auto config_files = config_ptr_->as<std::vector<std::string>>();
|
||||||
if(config_file.empty()) {
|
if(config_files.empty() || config_files.front().empty()) {
|
||||||
if(config_required) {
|
if(config_required) {
|
||||||
throw FileError::Missing("no specified config file");
|
throw FileError::Missing("no specified config file");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
for(auto rit = config_files.rbegin(); rit != config_files.rend(); ++rit) {
|
||||||
|
const auto &config_file = *rit;
|
||||||
auto path_result = detail::check_path(config_file.c_str());
|
auto path_result = detail::check_path(config_file.c_str());
|
||||||
if(path_result == detail::path_type::file) {
|
if(path_result == detail::path_type::file) {
|
||||||
try {
|
try {
|
||||||
@ -2079,6 +2080,7 @@ class App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get envname options if not yet passed. Runs on *all* subcommands.
|
/// Get envname options if not yet passed. Runs on *all* subcommands.
|
||||||
void _process_env() {
|
void _process_env() {
|
||||||
|
@ -591,6 +591,89 @@ TEST_F(TApp, IniNotRequiredNotDefault) {
|
|||||||
EXPECT_EQ(app.get_config_ptr()->as<std::string>(), tmpini2.c_str());
|
EXPECT_EQ(app.get_config_ptr()->as<std::string>(), tmpini2.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, MultiConfig) {
|
||||||
|
|
||||||
|
TempFile tmpini{"TestIniTmp.ini"};
|
||||||
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
||||||
|
|
||||||
|
app.set_config("--config")->expected(1, 3);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream out{tmpini};
|
||||||
|
out << "[default]" << std::endl;
|
||||||
|
out << "two=99" << std::endl;
|
||||||
|
out << "three=3" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream out{tmpini2};
|
||||||
|
out << "[default]" << std::endl;
|
||||||
|
out << "one=55" << std::endl;
|
||||||
|
out << "three=4" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int one{0}, two{0}, three{0};
|
||||||
|
app.add_option("--one", one);
|
||||||
|
app.add_option("--two", two);
|
||||||
|
app.add_option("--three", three);
|
||||||
|
|
||||||
|
args = {"--config", tmpini2, "--config", tmpini};
|
||||||
|
run();
|
||||||
|
|
||||||
|
EXPECT_EQ(99, two);
|
||||||
|
EXPECT_EQ(3, three);
|
||||||
|
EXPECT_EQ(55, one);
|
||||||
|
|
||||||
|
args = {"--config", tmpini, "--config", tmpini2};
|
||||||
|
run();
|
||||||
|
|
||||||
|
EXPECT_EQ(99, two);
|
||||||
|
EXPECT_EQ(4, three);
|
||||||
|
EXPECT_EQ(55, one);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, MultiConfig_single) {
|
||||||
|
|
||||||
|
TempFile tmpini{"TestIniTmp.ini"};
|
||||||
|
TempFile tmpini2{"TestIniTmp2.ini"};
|
||||||
|
|
||||||
|
app.set_config("--config")->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream out{tmpini};
|
||||||
|
out << "[default]" << std::endl;
|
||||||
|
out << "two=99" << std::endl;
|
||||||
|
out << "three=3" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream out{tmpini2};
|
||||||
|
out << "[default]" << std::endl;
|
||||||
|
out << "one=55" << std::endl;
|
||||||
|
out << "three=4" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int one{0}, two{0}, three{0};
|
||||||
|
app.add_option("--one", one);
|
||||||
|
app.add_option("--two", two);
|
||||||
|
app.add_option("--three", three);
|
||||||
|
|
||||||
|
args = {"--config", tmpini2, "--config", tmpini};
|
||||||
|
run();
|
||||||
|
|
||||||
|
EXPECT_EQ(99, two);
|
||||||
|
EXPECT_EQ(3, three);
|
||||||
|
EXPECT_EQ(0, one);
|
||||||
|
|
||||||
|
two = 0;
|
||||||
|
args = {"--config", tmpini, "--config", tmpini2};
|
||||||
|
run();
|
||||||
|
|
||||||
|
EXPECT_EQ(0, two);
|
||||||
|
EXPECT_EQ(4, three);
|
||||||
|
EXPECT_EQ(55, one);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TApp, IniRequiredNotFound) {
|
TEST_F(TApp, IniRequiredNotFound) {
|
||||||
|
|
||||||
std::string noini = "TestIniNotExist.ini";
|
std::string noini = "TestIniNotExist.ini";
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "app_helper.hpp"
|
#include "app_helper.hpp"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@ -850,6 +851,22 @@ TEST_F(TApp, AsSizeValue1000_1024) {
|
|||||||
EXPECT_EQ(value, ki_value);
|
EXPECT_EQ(value, ki_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TApp, duration_test) {
|
||||||
|
std::chrono::seconds duration{1};
|
||||||
|
|
||||||
|
app.option_defaults()->ignore_case();
|
||||||
|
app.add_option_function<std::size_t>(
|
||||||
|
"--duration",
|
||||||
|
[&](size_t a_value) { duration = std::chrono::seconds{a_value}; },
|
||||||
|
"valid units: sec, min, h, day.")
|
||||||
|
->capture_default_str()
|
||||||
|
->transform(CLI::AsNumberWithUnit(
|
||||||
|
std::map<std::string, std::size_t>{{"sec", 1}, {"min", 60}, {"h", 3600}, {"day", 24 * 3600}}));
|
||||||
|
EXPECT_NO_THROW(app.parse(std::vector<std::string>{"1 day", "--duration"}));
|
||||||
|
|
||||||
|
EXPECT_EQ(duration, std::chrono::seconds(86400));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TApp, AsSizeValue1024) {
|
TEST_F(TApp, AsSizeValue1024) {
|
||||||
std::uint64_t value{0};
|
std::uint64_t value{0};
|
||||||
app.add_option("-s", value)->transform(CLI::AsSizeValue(false));
|
app.add_option("-s", value)->transform(CLI::AsSizeValue(false));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user