Skip to content

Commit 0659ebd

Browse files
committed
Add an ability to deal handle multiple config files
1 parent 7cdd95e commit 0659ebd

File tree

3 files changed

+118
-16
lines changed

3 files changed

+118
-16
lines changed

include/CLI/App.hpp

+18-16
Original file line numberDiff line numberDiff line change
@@ -2053,29 +2053,31 @@ class App {
20532053
void _process_config_file() {
20542054
if(config_ptr_ != nullptr) {
20552055
bool config_required = config_ptr_->get_required();
2056-
bool file_given = config_ptr_->count() > 0;
2057-
auto config_file = config_ptr_->as<std::string>();
2058-
if(config_file.empty()) {
2056+
auto file_given = config_ptr_->count() > 0;
2057+
auto config_files = config_ptr_->as<std::vector<std::string>>();
2058+
if(config_files.empty() || config_files.front().empty()) {
20592059
if(config_required) {
20602060
throw FileError::Missing("no specified config file");
20612061
}
20622062
return;
20632063
}
2064-
2065-
auto path_result = detail::check_path(config_file.c_str());
2066-
if(path_result == detail::path_type::file) {
2067-
try {
2068-
std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
2069-
_parse_config(values);
2070-
if(!file_given) {
2071-
config_ptr_->add_result(config_file);
2064+
for(auto rit = config_files.rbegin(); rit != config_files.rend(); ++rit) {
2065+
const auto &config_file = *rit;
2066+
auto path_result = detail::check_path(config_file.c_str());
2067+
if(path_result == detail::path_type::file) {
2068+
try {
2069+
std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
2070+
_parse_config(values);
2071+
if(!file_given) {
2072+
config_ptr_->add_result(config_file);
2073+
}
2074+
} catch(const FileError &) {
2075+
if(config_required || file_given)
2076+
throw;
20722077
}
2073-
} catch(const FileError &) {
2074-
if(config_required || file_given)
2075-
throw;
2078+
} else if(config_required || file_given) {
2079+
throw FileError::Missing(config_file);
20762080
}
2077-
} else if(config_required || file_given) {
2078-
throw FileError::Missing(config_file);
20792081
}
20802082
}
20812083
}

tests/ConfigFileTest.cpp

+83
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,89 @@ TEST_F(TApp, IniNotRequiredNotDefault) {
591591
EXPECT_EQ(app.get_config_ptr()->as<std::string>(), tmpini2.c_str());
592592
}
593593

594+
TEST_F(TApp, MultiConfig) {
595+
596+
TempFile tmpini{"TestIniTmp.ini"};
597+
TempFile tmpini2{"TestIniTmp2.ini"};
598+
599+
app.set_config("--config")->expected(1, 3);
600+
601+
{
602+
std::ofstream out{tmpini};
603+
out << "[default]" << std::endl;
604+
out << "two=99" << std::endl;
605+
out << "three=3" << std::endl;
606+
}
607+
608+
{
609+
std::ofstream out{tmpini2};
610+
out << "[default]" << std::endl;
611+
out << "one=55" << std::endl;
612+
out << "three=4" << std::endl;
613+
}
614+
615+
int one{0}, two{0}, three{0};
616+
app.add_option("--one", one);
617+
app.add_option("--two", two);
618+
app.add_option("--three", three);
619+
620+
args = {"--config", tmpini2, "--config", tmpini};
621+
run();
622+
623+
EXPECT_EQ(99, two);
624+
EXPECT_EQ(3, three);
625+
EXPECT_EQ(55, one);
626+
627+
args = {"--config", tmpini, "--config", tmpini2};
628+
run();
629+
630+
EXPECT_EQ(99, two);
631+
EXPECT_EQ(4, three);
632+
EXPECT_EQ(55, one);
633+
}
634+
635+
TEST_F(TApp, MultiConfig_single) {
636+
637+
TempFile tmpini{"TestIniTmp.ini"};
638+
TempFile tmpini2{"TestIniTmp2.ini"};
639+
640+
app.set_config("--config")->multi_option_policy(CLI::MultiOptionPolicy::TakeLast);
641+
642+
{
643+
std::ofstream out{tmpini};
644+
out << "[default]" << std::endl;
645+
out << "two=99" << std::endl;
646+
out << "three=3" << std::endl;
647+
}
648+
649+
{
650+
std::ofstream out{tmpini2};
651+
out << "[default]" << std::endl;
652+
out << "one=55" << std::endl;
653+
out << "three=4" << std::endl;
654+
}
655+
656+
int one{0}, two{0}, three{0};
657+
app.add_option("--one", one);
658+
app.add_option("--two", two);
659+
app.add_option("--three", three);
660+
661+
args = {"--config", tmpini2, "--config", tmpini};
662+
run();
663+
664+
EXPECT_EQ(99, two);
665+
EXPECT_EQ(3, three);
666+
EXPECT_EQ(0, one);
667+
668+
two = 0;
669+
args = {"--config", tmpini, "--config", tmpini2};
670+
run();
671+
672+
EXPECT_EQ(0, two);
673+
EXPECT_EQ(4, three);
674+
EXPECT_EQ(55, one);
675+
}
676+
594677
TEST_F(TApp, IniRequiredNotFound) {
595678

596679
std::string noini = "TestIniNotExist.ini";

tests/TransformTest.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "app_helper.hpp"
88

99
#include <array>
10+
#include <chrono>
1011
#include <cstdint>
1112
#include <unordered_map>
1213

@@ -850,6 +851,22 @@ TEST_F(TApp, AsSizeValue1000_1024) {
850851
EXPECT_EQ(value, ki_value);
851852
}
852853

854+
TEST_F(TApp, duration_test) {
855+
std::chrono::seconds duration{1};
856+
857+
app.option_defaults()->ignore_case();
858+
app.add_option_function<std::size_t>(
859+
"--duration",
860+
[&](auto &&a_value) { duration = std::chrono::seconds{a_value}; },
861+
"valid units: sec, min, h, day.")
862+
->capture_default_str()
863+
->transform(CLI::AsNumberWithUnit(
864+
std::map<std::string, std::size_t>{{"sec", 1}, {"min", 60}, {"h", 3600}, {"day", 24 * 3600}}));
865+
EXPECT_NO_THROW(app.parse(std::vector<std::string>{"1 day", "--duration"}));
866+
867+
EXPECT_EQ(duration, std::chrono::seconds(86400));
868+
}
869+
853870
TEST_F(TApp, AsSizeValue1024) {
854871
std::uint64_t value{0};
855872
app.add_option("-s", value)->transform(CLI::AsSizeValue(false));

0 commit comments

Comments
 (0)