feat(config): Add directive for file inclusion

This commit is contained in:
Michael Carlberg 2017-01-26 20:10:33 +01:00
parent febe6997b6
commit 874e6b0d6c
5 changed files with 66 additions and 22 deletions

View File

@ -74,7 +74,7 @@ class config {
T get(const string& section, const string& key) const {
auto it = m_sections.find(section);
if (it == m_sections.end() || it->second.find(key) == it->second.end()) {
throw key_error("Missing parameter [" + section + "." + key + "]");
throw key_error("Missing parameter \"" + section + "." + key + "\"");
}
return dereference<T>(section, key, it->second.at(key), convert<T>(string{it->second.at(key)}));
}
@ -125,7 +125,7 @@ class config {
}
if (results.empty()) {
throw key_error("Missing parameter [" + section + "." + key + "-0]");
throw key_error("Missing parameter \"" + section + "." + key + "-0\"");
}
return results;
@ -220,7 +220,7 @@ class config {
} else if ((pos = path.find(".")) != string::npos) {
return dereference_local<T>(path.substr(0, pos), path.substr(pos + 1), section);
} else {
throw value_error("Invalid reference defined at [" + section + "." + key + "]");
throw value_error("Invalid reference defined at \"" + section + "." + key + "\"");
}
}
@ -251,7 +251,8 @@ class config {
size_t pos;
if ((pos = key.find(':')) != string::npos) {
string fallback = key.substr(pos + 1);
m_log.info("The reference ${%s.%s} does not exist, using defined fallback value \"%s\"", section, key.substr(0, pos), fallback);
m_log.info("The reference ${%s.%s} does not exist, using defined fallback value \"%s\"", section,
key.substr(0, pos), fallback);
return convert<T>(move(fallback));
}
throw value_error("The reference ${" + section + "." + key + "} does not exist (no fallback set)");

View File

@ -77,9 +77,9 @@ namespace string_util {
string strip(const string& haystack, char needle);
string strip_trailing_newline(const string& haystack);
string ltrim(string&& value, const char& needle);
string rtrim(string&& value, const char& needle);
string trim(string&& value, const char& needle);
string ltrim(string&& value, const char& needle = ' ');
string rtrim(string&& value, const char& needle = ' ');
string trim(string&& value, const char& needle = ' ');
string join(const vector<string>& strs, const string& delim);
vector<string>& split_into(const string& s, char delim, vector<string>& container);

View File

@ -77,21 +77,58 @@ void config::warn_deprecated(const string& section, const string& key, string re
* Parse key/value pairs from the configuration file
*/
void config::parse_file() {
std::ifstream in(m_file);
string line;
string section;
unsigned int lineno{0};
while (std::getline(in, line)) {
lineno++;
line = string_util::replace_all(line, "\t", "");
vector<pair<int, string>> lines;
vector<string> files{m_file};
std::function<void(int, string&&)> pushline = [&](int lineno, string&& line) {
// Ignore empty lines and comments
if (line.empty() || line[0] == ';' || line[0] == '#') {
continue;
return;
}
string key, value;
string::size_type pos;
// Filter lines by:
// - key/value pairs
// - section headers
if ((pos = line.find('=')) != string::npos) {
key = forward<string>(string_util::trim(forward<string>(line.substr(0, pos))));
value = forward<string>(string_util::trim(line.substr(pos + 1)));
} else if (line[0] != '[' || line[line.length() - 1] != ']') {
return;
}
if (key == "include-file") {
if (value.empty() || !file_util::exists(value)) {
throw value_error("Invalid include file \"" + value + "\" defined on line " + to_string(lineno));
}
if (std::find(files.begin(), files.end(), value) != files.end()) {
throw value_error("Recursive include file \"" + value + "\"");
}
files.push_back(value);
m_log.trace("config: Including file \"%s\"", value);
for (auto&& l : string_util::split(file_util::contents(value), '\n')) {
pushline(lineno, forward<string>(l));
}
files.pop_back();
} else {
lines.emplace_back(make_pair(lineno, line));
}
};
int lineno{0};
string line;
std::ifstream in(m_file);
while (std::getline(in, line)) {
pushline(++lineno, string_util::replace_all(line, "\t", ""));
}
string section;
for (auto&& l : lines) {
auto& lineno = l.first;
auto& line = l.second;
// New section
if (line[0] == '[' && line[line.length() - 1] == ']') {
section = line.substr(1, line.length() - 2);
@ -107,7 +144,7 @@ void config::parse_file() {
continue;
}
string key{forward<string>(string_util::trim(forward<string>(line.substr(0, equal_pos)), ' '))};
string key{forward<string>(string_util::trim(forward<string>(line.substr(0, equal_pos))))};
string value;
auto it = m_sections[section].find(key);
@ -116,7 +153,7 @@ void config::parse_file() {
}
if (equal_pos + 1 < line.size()) {
value = forward<string>(string_util::trim(line.substr(equal_pos + 1), ' '));
value = forward<string>(string_util::trim(line.substr(equal_pos + 1)));
size_t len{value.size()};
if (len > 2 && value[0] == '"' && value[len - 1] == '"') {
value.erase(len - 1, 1).erase(0, 1);
@ -149,13 +186,14 @@ void config::copy_inherited() {
// Get name of base section
auto inherit = param.second;
if ((inherit = dereference<string>(section.first, param.first, inherit, inherit)).empty()) {
throw value_error("[" + section.first + "." + KEY_INHERIT + "] requires a value");
throw value_error("Invalid section \"\" defined for \"" + section.first + "." + KEY_INHERIT + "\"");
}
// Find and validate base section
auto base_section = m_sections.find(inherit);
if (base_section == m_sections.end()) {
throw value_error("[" + section.first + "." + KEY_INHERIT + "] invalid reference \"" + inherit + "\"");
throw value_error(
"Invalid section \"" + inherit + "\" defined for \"" + section.first + "." + KEY_INHERIT + "\"");
}
m_log.trace("config: Copying missing params (sub=\"%s\", base=\"%s\")", section.first, inherit);

View File

@ -190,7 +190,11 @@ namespace file_util {
string contents(const string& filename) {
try {
string contents;
std::getline(std::ifstream(filename, std::ifstream::in), contents);
string line;
std::ifstream in(filename, std::ifstream::in);
while (std::getline(in, line)) {
contents += line + '\n';
}
return contents;
} catch (const std::ifstream::failure& e) {
return "";

View File

@ -43,6 +43,7 @@ int main() {
};
"trim"_test = [] {
expect(string_util::trim(" x x ") == "x x");
expect(string_util::ltrim("xxtestxx", 'x') == "testxx");
expect(string_util::rtrim("xxtestxx", 'x') == "xxtest");
expect(string_util::trim("xxtestxx", 'x') == "test");