Support for comments in config definition

pull/1186/head
Stephen Shelton 4 years ago
parent 1653b73ee5
commit 18ee23c2a3
No known key found for this signature in database
GPG Key ID: EE4BADACCE8B631C

@ -112,6 +112,21 @@ Configuration::acceptAllOptions()
});
}
void
Configuration::addSectionComment(const std::string& section,
std::string comment)
{
m_sectionComments[section].push_back(std::move(comment));
}
void
Configuration::addOptionComment(const std::string& section,
const std::string& name,
std::string comment)
{
m_definitionComments[section][name].push_back(std::move(comment));
}
std::string
Configuration::generateINIConfig(bool useValues)
{
@ -123,9 +138,25 @@ Configuration::generateINIConfig(bool useValues)
if (sectionsVisited > 0)
oss << "\n";
// TODO: this will create empty objects as a side effect of map's operator[]
// TODO: this also won't handle sections which have no definition
for (const std::string& comment : m_sectionComments[section])
{
oss << "# " << comment << "\n";
}
oss << "[" << section << "]\n";
visitDefinitions(section, [&](const std::string& name, const ConfigDefinition_ptr& def) {
// TODO: as above, this will create empty objects
// TODO: as above (but more important): this won't handle definitions with no entries
// (i.e. those handled by UndeclaredValueHandler's)
for (const std::string& comment : m_definitionComments[section][name])
{
oss << "# " << comment << "\n";
}
if (useValues and def->numFound > 0)
{
oss << name << "=" << def->valueAsString(false) << "\n";

@ -299,6 +299,24 @@ namespace llarp
void
acceptAllOptions();
/// Add comments for a given section. Comments are replayed in-order during config file
/// generation. A proper comment prefix will automatically be applied, and the entire comment
/// will otherwise be used verbatim (no automatic line separation, etc.).
///
/// @param section
/// @param comment
void
addSectionComment(const std::string& section, std::string comment);
/// Add comments for a given option. Similar to addSectionComment, but applies to a specific
/// [section]:name pair.
///
/// @param section
/// @param name
/// @param comment
void
addOptionComment(const std::string& section, const std::string& name, std::string comment);
/// Generate a config string from the current config definition, optionally using overridden
/// values. The generated config will preserve insertion order of both sections and their
/// definitions.
@ -331,6 +349,12 @@ namespace llarp
// track insertion order
std::vector<std::string> m_sectionOrdering;
std::unordered_map<std::string, std::vector<std::string>> m_definitionOrdering;
// comments for config file generation
using CommentList = std::vector<std::string>;
using CommentsMap = std::unordered_map<std::string, CommentList>;
CommentsMap m_sectionComments;
std::unordered_map<std::string, CommentsMap> m_definitionComments;
};
} // namespace llarp

@ -54,3 +54,103 @@ TEST_CASE("Configuration useValue test", "[config]")
CHECK(config.generateINIConfig(true) == expectedWhenValueProvided);
}
TEST_CASE("Configuration section comments test")
{
llarp::Configuration config;
config.addSectionComment("foo", "test comment");
config.addSectionComment("foo", "test comment 2");
config.defineOption(std::make_unique<llarp::ConfigDefinition<int>>(
"foo", "bar", true, 1));
std::string output = config.generateINIConfig();
CHECK(output == R"raw(# test comment
# test comment 2
[foo]
bar=1
)raw");
}
TEST_CASE("Configuration option comments test")
{
llarp::Configuration config;
config.addOptionComment("foo", "bar", "test comment 1");
config.addOptionComment("foo", "bar", "test comment 2");
config.defineOption(std::make_unique<llarp::ConfigDefinition<int>>(
"foo", "bar", true, 1));
std::string output = config.generateINIConfig();
CHECK(output == R"raw([foo]
# test comment 1
# test comment 2
bar=1
)raw");
}
TEST_CASE("Configuration empty comments test")
{
llarp::Configuration config;
config.addSectionComment("foo", "section comment");
config.addSectionComment("foo", "");
config.addOptionComment("foo", "bar", "option comment");
config.addOptionComment("foo", "bar", "");
config.defineOption(std::make_unique<llarp::ConfigDefinition<int>>(
"foo", "bar", true, 1));
std::string output = config.generateINIConfig();
CHECK(output == R"raw(# section comment
#
[foo]
# option comment
#
bar=1
)raw");
}
TEST_CASE("Configuration multi option comments")
{
llarp::Configuration config;
config.addSectionComment("foo", "foo section comment");
config.addOptionComment("foo", "bar", "foo bar option comment");
config.defineOption(std::make_unique<llarp::ConfigDefinition<int>>(
"foo", "bar", true, 1));
config.addOptionComment("foo", "baz", "foo baz option comment");
config.defineOption(std::make_unique<llarp::ConfigDefinition<int>>(
"foo", "baz", true, 1));
std::string output = config.generateINIConfig();
CHECK(output == R"raw(# foo section comment
[foo]
# foo bar option comment
bar=1
# foo baz option comment
baz=1
)raw");
}
TEST_CASE("Configuration should print comments for missing keys")
{
// TODO: this currently fails: how to implement?
llarp::Configuration config;
config.addSectionComment("foo", "foo section comment");
config.addOptionComment("foo", "bar", "foo bar option comment");
std::string output = config.generateINIConfig();
CHECK(output == R"raw(# foo section comment
[foo]
# foo bar option comment
bar=
)raw");
}

Loading…
Cancel
Save