Add SimpleConfig.
https://godocs.io/git.wntrmute.dev/kyle/goutils/config is one of the more useful Go packages in my standard go library that gets used for building services. I needed something similar for another Shimmering Clarity project, and thus I figured I'd add it into SCSL.
This commit is contained in:
parent
9a8dc08a4f
commit
c5308dedba
|
@ -42,6 +42,7 @@ set(HEADER_FILES
|
||||||
include/scsl/Commander.h
|
include/scsl/Commander.h
|
||||||
include/scsl/Dictionary.h
|
include/scsl/Dictionary.h
|
||||||
include/scsl/Flags.h
|
include/scsl/Flags.h
|
||||||
|
include/scsl/SimpleConfig.h
|
||||||
include/scsl/StringUtil.h
|
include/scsl/StringUtil.h
|
||||||
include/scsl/TLV.h
|
include/scsl/TLV.h
|
||||||
|
|
||||||
|
@ -72,6 +73,7 @@ set(SOURCE_FILES
|
||||||
src/sl/Dictionary.cc
|
src/sl/Dictionary.cc
|
||||||
src/test/Exceptions.cc
|
src/test/Exceptions.cc
|
||||||
src/sl/Flags.cc
|
src/sl/Flags.cc
|
||||||
|
src/sl/SimpleConfig.cc
|
||||||
src/sl/StringUtil.cc
|
src/sl/StringUtil.cc
|
||||||
src/sl/TLV.cc
|
src/sl/TLV.cc
|
||||||
|
|
||||||
|
@ -123,7 +125,6 @@ generate_test(orientation)
|
||||||
generate_test(quaternion)
|
generate_test(quaternion)
|
||||||
generate_test(vector)
|
generate_test(vector)
|
||||||
|
|
||||||
|
|
||||||
# test tooling
|
# test tooling
|
||||||
add_executable(flags-demo test/flags.cc)
|
add_executable(flags-demo test/flags.cc)
|
||||||
target_link_libraries(flags-demo ${PROJECT_NAME})
|
target_link_libraries(flags-demo ${PROJECT_NAME})
|
||||||
|
@ -131,6 +132,9 @@ target_link_libraries(flags-demo ${PROJECT_NAME})
|
||||||
add_executable(simple-test-demo test/simple_suite_example.cc)
|
add_executable(simple-test-demo test/simple_suite_example.cc)
|
||||||
target_link_libraries(simple-test-demo ${PROJECT_NAME})
|
target_link_libraries(simple-test-demo ${PROJECT_NAME})
|
||||||
|
|
||||||
|
add_executable(config-explorer test/config-explorer.cc)
|
||||||
|
target_link_libraries(config-explorer ${PROJECT_NAME})
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
write_basic_package_version_file(
|
write_basic_package_version_file(
|
||||||
scslConfig.cmake
|
scslConfig.cmake
|
||||||
|
|
|
@ -245,6 +245,17 @@ public:
|
||||||
std::string defaultValue,
|
std::string defaultValue,
|
||||||
std::string fDescription);
|
std::string fDescription);
|
||||||
|
|
||||||
|
/// Register a new string command line flag with a default value.
|
||||||
|
///
|
||||||
|
/// \param fName The name of the flag, including a leading dash.
|
||||||
|
/// \param defaultValue The default value for the flag.
|
||||||
|
/// \param fDescription A short description of the flag.
|
||||||
|
/// \return True if the flag was registered, false if the flag could
|
||||||
|
/// not be registered (e.g. a duplicate flag was registered).
|
||||||
|
bool Register(std::string fName,
|
||||||
|
const char *defaultValue,
|
||||||
|
std::string fDescription);
|
||||||
|
|
||||||
/// Return the number of registered flags.
|
/// Return the number of registered flags.
|
||||||
size_t Size();
|
size_t Size();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
///
|
||||||
|
/// \file include/scsl/SimpleConfig.h
|
||||||
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2023-10-21
|
||||||
|
/// \brief Simple project configuration.
|
||||||
|
///
|
||||||
|
/// This is an implementation of a simple global configuration system
|
||||||
|
/// for projects based on a Go version I've used successfully in
|
||||||
|
/// several projects.
|
||||||
|
///
|
||||||
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||||
|
///
|
||||||
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
|
///
|
||||||
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef SCSL_SIMPLECONFIG_H
|
||||||
|
#define SCSL_SIMPLECONFIG_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace scsl {
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief SimpleConfig is a basic configuration for projects.
|
||||||
|
///
|
||||||
|
/// SimpleConfig is a basic key-value system. It can optionally load
|
||||||
|
/// key-value pairs from a file, which should consist of key = value
|
||||||
|
/// lines. Comments may be added by starting the line with a '#'; these
|
||||||
|
/// lines will be skipped. Comments may have leading whitespace. Any
|
||||||
|
/// empty lines or lines consisting solely of whitespace will also be
|
||||||
|
/// skipped.
|
||||||
|
///
|
||||||
|
/// When values are retrieved by one of the variants on Get, they are
|
||||||
|
/// looked up in the following order, assuming `key` and an optional
|
||||||
|
/// `prefix` set on the config:
|
||||||
|
///
|
||||||
|
/// 1. Any cached key-value pairs. Loading a file caches the key-value
|
||||||
|
/// pairs in the file. The file is not used again, unless another
|
||||||
|
/// call to Load is made. If the cache has a name for `key`, it will
|
||||||
|
/// be returned.
|
||||||
|
/// 2. The value is looked up from the environment. An optional prefix
|
||||||
|
/// can be set; if so, if there is an environment named
|
||||||
|
/// `{prefix}{key}`, the value will be cached and it will be
|
||||||
|
/// returned.
|
||||||
|
/// 3. If a default value has been provided, it will be cached and
|
||||||
|
/// returned.
|
||||||
|
/// 4. If none of the previous steps has provided a value, an empty
|
||||||
|
/// string will be returned.
|
||||||
|
///
|
||||||
|
/// In Go projects, I've used the global config to great success.
|
||||||
|
/// However, callers may set up an explicit configuration instance.
|
||||||
|
class SimpleConfig {
|
||||||
|
public:
|
||||||
|
#if defined(SCSL_DESKTOP_BUILD)
|
||||||
|
/// \brief Load key-value pairs from a file.
|
||||||
|
///
|
||||||
|
/// \note This operates on the global config.
|
||||||
|
///
|
||||||
|
/// \param path The path to a config file.
|
||||||
|
/// \return 0 if the file was loaded successfully.
|
||||||
|
static int LoadGlobal(const char *path);
|
||||||
|
|
||||||
|
/// \brief Load key-value pairs from a file.
|
||||||
|
///
|
||||||
|
/// \note This operates on the global config.
|
||||||
|
///
|
||||||
|
/// \param path The path to a config file.
|
||||||
|
/// \return 0 if the file was loaded successfully.
|
||||||
|
static int LoadGlobal(std::string &path);
|
||||||
|
|
||||||
|
/// \brief Set the prefix in use by the config.
|
||||||
|
///
|
||||||
|
/// \note This operates on the global config.
|
||||||
|
///
|
||||||
|
/// \param prefix The prefix to prepend to the key when looking
|
||||||
|
/// up values from the environment.
|
||||||
|
static void SetPrefixGlobal(const std::string prefix);
|
||||||
|
|
||||||
|
/// \brief Return the keys cached in the config.
|
||||||
|
///
|
||||||
|
/// Note that this won't returned any non-cached environment
|
||||||
|
/// values.
|
||||||
|
///
|
||||||
|
/// \note This operates on the global config.
|
||||||
|
///
|
||||||
|
/// \return A list of keys stored under the config.
|
||||||
|
static std::vector<std::string> KeyListGlobal();
|
||||||
|
|
||||||
|
/// \brief Get the value stored for the key from the config.
|
||||||
|
///
|
||||||
|
/// \note This operates on the global config.
|
||||||
|
///
|
||||||
|
/// \param key The key to look up. See the class documentation
|
||||||
|
/// for information on how this is used.
|
||||||
|
/// \return The value stored under the key, or an empty string.
|
||||||
|
static std::string GetGlobal(std::string key);
|
||||||
|
|
||||||
|
/// \brief Get the value stored for the key from the config.
|
||||||
|
///
|
||||||
|
/// \note This operates on the global config.
|
||||||
|
///
|
||||||
|
/// \param key The key to look up. See the class documentation
|
||||||
|
/// for information on how this is used.
|
||||||
|
/// \param defaultValue A default value to cache and use if no
|
||||||
|
/// value is stored under the key.
|
||||||
|
/// \return The value stored under the key, or the default
|
||||||
|
/// value.
|
||||||
|
static std::string GetGlobal(std::string key, std::string defaultValue);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// \brief The constructor doesn't need any initialisation.
|
||||||
|
SimpleConfig();
|
||||||
|
|
||||||
|
/// \brief The constructor can explicitly set the environment
|
||||||
|
/// prefix.
|
||||||
|
SimpleConfig(std::string prefix);
|
||||||
|
|
||||||
|
/// \brief Load key-value pairs from a file.
|
||||||
|
///
|
||||||
|
/// \param path The path to a config file.
|
||||||
|
/// \return 0 if the file was loaded successfully.
|
||||||
|
int Load(const char *path);
|
||||||
|
|
||||||
|
/// \brief Load key-value pairs from a file.
|
||||||
|
///
|
||||||
|
/// \param path The path to a config file.
|
||||||
|
/// \return 0 if the file was loaded successfully.
|
||||||
|
int Load(std::string& path);
|
||||||
|
|
||||||
|
/// \brief Set the prefix in use by the config.
|
||||||
|
///
|
||||||
|
/// \param prefix The prefix to prepend to the key when looking
|
||||||
|
/// up values from the environment.
|
||||||
|
void SetPrefix(const std::string prefix);
|
||||||
|
|
||||||
|
/// \brief Return the keys cached in the config.
|
||||||
|
///
|
||||||
|
/// Note that this won't returned any non-cached environment
|
||||||
|
/// values.
|
||||||
|
///
|
||||||
|
/// \return A list of keys stored under the config.
|
||||||
|
std::vector<std::string> KeyList();
|
||||||
|
|
||||||
|
/// \brief Get the value stored for the key from the config.
|
||||||
|
///
|
||||||
|
/// \param key The key to look up. See the class documentation
|
||||||
|
/// for information on how this is used.
|
||||||
|
/// \return The value stored under the key, or an empty string.
|
||||||
|
std::string Get(std::string key);
|
||||||
|
|
||||||
|
/// \brief Get the value stored for the key from the config.
|
||||||
|
///
|
||||||
|
/// \param key The key to look up. See the class documentation
|
||||||
|
/// for information on how this is used.
|
||||||
|
/// \param defaultValue A default value to cache and use if no
|
||||||
|
/// value is stored under the key.
|
||||||
|
/// \return The value stored under the key, or the default
|
||||||
|
/// value.
|
||||||
|
std::string Get(std::string key, std::string defaultValue);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string envPrefix;
|
||||||
|
std::map<std::string, std::string> vars;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace scsl
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SCSL_SIMPLECONFIG_H
|
|
@ -33,7 +33,7 @@
|
||||||
namespace scsl {
|
namespace scsl {
|
||||||
|
|
||||||
/// String-related utility functions.
|
/// String-related utility functions.
|
||||||
namespace string {
|
namespace scstring {
|
||||||
|
|
||||||
|
|
||||||
/// Remove any whitespace At the beginning of the string. The string
|
/// Remove any whitespace At the beginning of the string. The string
|
||||||
|
|
|
@ -80,7 +80,7 @@ hasKey(std::vector<std::string> argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << "not found\n";
|
cout << "not found\n";
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
/// PERFORMANCE OF THIS SOFTWARE.
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
@ -172,6 +171,18 @@ Flags::Register(std::string fName, std::string defaultValue, std::string fDescri
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
Flags::Register(std::string fName, const char *defaultValue, std::string fDescription)
|
||||||
|
{
|
||||||
|
if (!this->Register(fName, FlagType::String, std::move(fDescription))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->flags[fName]->Value.s = new std::string(defaultValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
Flags::Size()
|
Flags::Size()
|
||||||
{
|
{
|
||||||
|
@ -206,7 +217,7 @@ Flags::ParseStatus
|
||||||
Flags::parseArg(int argc, char **argv, int &index)
|
Flags::parseArg(int argc, char **argv, int &index)
|
||||||
{
|
{
|
||||||
std::string arg(argv[index]);
|
std::string arg(argv[index]);
|
||||||
string::TrimWhitespace(arg);
|
scstring::TrimWhitespace(arg);
|
||||||
|
|
||||||
if (!std::regex_search(arg, isFlag)) {
|
if (!std::regex_search(arg, isFlag)) {
|
||||||
return ParseStatus::EndOfFlags;
|
return ParseStatus::EndOfFlags;
|
||||||
|
@ -302,7 +313,7 @@ Flags::Usage(std::ostream &os, int exitCode)
|
||||||
os << this->name << ":\t";
|
os << this->name << ":\t";
|
||||||
auto indent = this->name.size() + 7;
|
auto indent = this->name.size() + 7;
|
||||||
|
|
||||||
string::WriteTabIndented(os, description, 72 - indent, indent / 8, false);
|
scstring::WriteTabIndented(os, description, 72 - indent, indent / 8, false);
|
||||||
os << "\n\n";
|
os << "\n\n";
|
||||||
|
|
||||||
for (const auto &pair : this->flags) {
|
for (const auto &pair : this->flags) {
|
||||||
|
@ -336,7 +347,7 @@ Flags::Usage(std::ostream &os, int exitCode)
|
||||||
|
|
||||||
os << argLine;
|
os << argLine;
|
||||||
indent = argLine.size();
|
indent = argLine.size();
|
||||||
string::WriteTabIndented(os, pair.second->Description,
|
scstring::WriteTabIndented(os, pair.second->Description,
|
||||||
72-indent, (indent/8)+2, false);
|
72-indent, (indent/8)+2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
///
|
||||||
|
/// \file src/sl/SimpleConfig.cc
|
||||||
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2023-10-21
|
||||||
|
/// \brief Simple project configuration.
|
||||||
|
///
|
||||||
|
/// This is an implementation of a simple global configuration system
|
||||||
|
/// for projects based on a Go version I've used successfully in
|
||||||
|
/// several projects.
|
||||||
|
///
|
||||||
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||||
|
///
|
||||||
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
|
///
|
||||||
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include <scsl/SimpleConfig.h>
|
||||||
|
#include <scsl/StringUtil.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace scsl {
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(SCSL_DESKTOP_BUILD)
|
||||||
|
static SimpleConfig globalConfig;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr auto regexOpts = std::regex_constants::nosubs |
|
||||||
|
std::regex_constants::optimize |
|
||||||
|
std::regex_constants::ECMAScript;
|
||||||
|
|
||||||
|
static const std::regex commentLine("^\\s*#.*$", regexOpts);
|
||||||
|
static const std::regex keyValueLine("^\\s*\\w+\\s*=\\s*\\w+", regexOpts);
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
SimpleConfig::LoadGlobal(const char *path)
|
||||||
|
{
|
||||||
|
return globalConfig.Load(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
SimpleConfig::LoadGlobal(std::string &path)
|
||||||
|
{
|
||||||
|
return globalConfig.Load(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SimpleConfig::SetPrefixGlobal(const std::string prefix)
|
||||||
|
{
|
||||||
|
globalConfig.SetPrefix(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
SimpleConfig::KeyListGlobal()
|
||||||
|
{
|
||||||
|
return globalConfig.KeyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
SimpleConfig::GetGlobal(std::string key)
|
||||||
|
{
|
||||||
|
return globalConfig.Get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
SimpleConfig::GetGlobal(std::string key, std::string defaultValue)
|
||||||
|
{
|
||||||
|
return globalConfig.Get(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SimpleConfig::SimpleConfig()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SimpleConfig::SimpleConfig(std::string prefix)
|
||||||
|
: envPrefix(prefix)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
SimpleConfig::Load(const char *path)
|
||||||
|
{
|
||||||
|
auto spath = std::string(path);
|
||||||
|
return this->Load(spath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
SimpleConfig::Load(std::string &path)
|
||||||
|
{
|
||||||
|
std::ifstream configFile(path);
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(configFile, line)) {
|
||||||
|
scstring::TrimWhitespace(line);
|
||||||
|
if (line.size() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::regex_search(line, commentLine)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::regex_search(line, keyValueLine)) {
|
||||||
|
auto pair = scstring::SplitKeyValuePair(line, "=");
|
||||||
|
if (pair.size() < 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->vars[pair[0]] = pair[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SimpleConfig::SetPrefix(const std::string prefix)
|
||||||
|
{
|
||||||
|
this->envPrefix = std::move(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
SimpleConfig::Get(std::string key)
|
||||||
|
{
|
||||||
|
return this->Get(key, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
SimpleConfig::Get(std::string key, std::string defaultValue)
|
||||||
|
{
|
||||||
|
if (this->vars.count(key)) {
|
||||||
|
return this->vars[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto envKey = this->envPrefix + key;
|
||||||
|
|
||||||
|
const char *envValue = getenv(envKey.c_str());
|
||||||
|
if (envValue != nullptr) {
|
||||||
|
this->vars[key] = std::string(envValue);
|
||||||
|
return this->Get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->vars[key] = defaultValue;
|
||||||
|
return this->Get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string>
|
||||||
|
SimpleConfig::KeyList()
|
||||||
|
{
|
||||||
|
std::vector<std::string> keyList;
|
||||||
|
|
||||||
|
for (auto &entry : this->vars) {
|
||||||
|
keyList.push_back(entry.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace SimpleConfig
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
|
|
||||||
namespace scsl {
|
namespace scsl {
|
||||||
namespace string {
|
namespace scstring {
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string>
|
std::vector<std::string>
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
///
|
||||||
|
/// \file test/config-explorer.cc
|
||||||
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2023-10-21
|
||||||
|
/// \brief Commandline tools for interacting with simple configurations.
|
||||||
|
///
|
||||||
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||||
|
///
|
||||||
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
|
///
|
||||||
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <scsl/Flags.h>
|
||||||
|
#include <scsl/SimpleConfig.h>
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
int retc = 1;
|
||||||
|
bool listKeys;
|
||||||
|
std::string fileName;
|
||||||
|
std::string prefix;
|
||||||
|
std::string defaultValue;
|
||||||
|
|
||||||
|
auto *flags = new scsl::Flags("config-explorer",
|
||||||
|
"interact with a simple configuration system");
|
||||||
|
flags->Register("-d", "", "set a default value");
|
||||||
|
flags->Register("-f", scsl::FlagType::String, "path to a configuration file");
|
||||||
|
flags->Register("-l", false, "list cached keys at the end");
|
||||||
|
flags->Register("-p", "CX_",
|
||||||
|
"prefix for configuration environment variables");
|
||||||
|
auto parsed = flags->Parse(argc, argv);
|
||||||
|
if (parsed != scsl::Flags::ParseStatus::OK) {
|
||||||
|
std::cerr << "Failed to parse flags: "
|
||||||
|
<< scsl::Flags::ParseStatusToString(parsed) << "\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
flags->GetString("-d", defaultValue);
|
||||||
|
flags->GetString("-f", fileName);
|
||||||
|
flags->GetBool("-l", listKeys);
|
||||||
|
flags->GetString("-p", prefix);
|
||||||
|
scsl::SimpleConfig::SetPrefixGlobal(prefix);
|
||||||
|
|
||||||
|
if (!fileName.empty()) {
|
||||||
|
if (scsl::SimpleConfig::LoadGlobal(fileName) != 0) {
|
||||||
|
std::cerr << "[!] failed to load " << fileName << "\n";
|
||||||
|
return retc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &key : flags->Args()) {
|
||||||
|
auto val = scsl::SimpleConfig::GetGlobal(key, defaultValue);
|
||||||
|
std::cout << key << ": " << val << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listKeys) {
|
||||||
|
std::cout << "[+] cached keys\n";
|
||||||
|
for (auto &key : scsl::SimpleConfig::KeyListGlobal()) {
|
||||||
|
std::cout << "\t- " << key << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete flags;
|
||||||
|
return retc;
|
||||||
|
}
|
|
@ -42,30 +42,30 @@ TestTrimming(std::string line, std::string lExpected, std::string rExpected, std
|
||||||
std::string result;
|
std::string result;
|
||||||
std::string message;
|
std::string message;
|
||||||
|
|
||||||
result = string::TrimLeadingWhitespaceDup(line);
|
result = scstring::TrimLeadingWhitespaceDup(line);
|
||||||
message = "TrimLeadingDup(\"" + line + "\"): '" + result + "'";
|
message = "TrimLeadingDup(\"" + line + "\"): '" + result + "'";
|
||||||
sctest::Assert(result == lExpected, message);
|
sctest::Assert(result == lExpected, message);
|
||||||
|
|
||||||
result = string::TrimTrailingWhitespaceDup(line);
|
result = scstring::TrimTrailingWhitespaceDup(line);
|
||||||
message = "TrimTrailingDup(\"" + line + "\"): '" + result + "'";
|
message = "TrimTrailingDup(\"" + line + "\"): '" + result + "'";
|
||||||
sctest::Assert(result == rExpected, message);
|
sctest::Assert(result == rExpected, message);
|
||||||
|
|
||||||
result = string::TrimWhitespaceDup(line);
|
result = scstring::TrimWhitespaceDup(line);
|
||||||
message = "TrimDup(\"" + line + "\"): '" + result + "'";
|
message = "TrimDup(\"" + line + "\"): '" + result + "'";
|
||||||
sctest::Assert(result == expected, message);
|
sctest::Assert(result == expected, message);
|
||||||
|
|
||||||
result = line;
|
result = line;
|
||||||
string::TrimLeadingWhitespace(result);
|
scstring::TrimLeadingWhitespace(result);
|
||||||
message = "TrimLeadingDup(\"" + line + "\"): '" + result + "'";
|
message = "TrimLeadingDup(\"" + line + "\"): '" + result + "'";
|
||||||
sctest::Assert(result == lExpected, message);
|
sctest::Assert(result == lExpected, message);
|
||||||
|
|
||||||
result = line;
|
result = line;
|
||||||
string::TrimTrailingWhitespace(result);
|
scstring::TrimTrailingWhitespace(result);
|
||||||
message = "TrimTrailingDup(\"" + line + "\"): '" + result + "'";
|
message = "TrimTrailingDup(\"" + line + "\"): '" + result + "'";
|
||||||
sctest::Assert(result == rExpected, message);
|
sctest::Assert(result == rExpected, message);
|
||||||
|
|
||||||
result = line;
|
result = line;
|
||||||
string::TrimWhitespace(result);
|
scstring::TrimWhitespace(result);
|
||||||
message = "TrimDup(\"" + line + "\"): '" + result + "'";
|
message = "TrimDup(\"" + line + "\"): '" + result + "'";
|
||||||
sctest::Assert(result == expected, message);
|
sctest::Assert(result == expected, message);
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ std::function<bool()>
|
||||||
TestSplit(std::string line, std::string delim, size_t maxCount, std::vector<std::string> expected)
|
TestSplit(std::string line, std::string delim, size_t maxCount, std::vector<std::string> expected)
|
||||||
{
|
{
|
||||||
return [line, delim, maxCount, expected]() {
|
return [line, delim, maxCount, expected]() {
|
||||||
return string::SplitN(line, delim, maxCount) == expected;
|
return scstring::SplitN(line, delim, maxCount) == expected;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ TestSplitChar()
|
||||||
{
|
{
|
||||||
auto expected = std::vector<std::string>{"hello", "world"};
|
auto expected = std::vector<std::string>{"hello", "world"};
|
||||||
const auto *inputLine = "hello=world\n";
|
const auto *inputLine = "hello=world\n";
|
||||||
auto actual = string::SplitKeyValuePair(inputLine, '=');
|
auto actual = scstring::SplitKeyValuePair(inputLine, '=');
|
||||||
|
|
||||||
return actual == expected;
|
return actual == expected;
|
||||||
}
|
}
|
||||||
|
@ -109,11 +109,11 @@ TestWrapping()
|
||||||
"hope so.",
|
"hope so.",
|
||||||
};
|
};
|
||||||
|
|
||||||
auto wrapped = string::WrapText(testLine, 16);
|
auto wrapped = scstring::WrapText(testLine, 16);
|
||||||
if (wrapped.size() != expected.size()) {
|
if (wrapped.size() != expected.size()) {
|
||||||
std::cerr << string::VectorToString(wrapped)
|
std::cerr << scstring::VectorToString(wrapped)
|
||||||
<< " != "
|
<< " != "
|
||||||
<< string::VectorToString(expected)
|
<< scstring::VectorToString(expected)
|
||||||
<< "\n";
|
<< "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ TestWrapping()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// string::WriteTabIndented(std::cout, wrapped, 4, true);
|
// scstring::WriteTabIndented(std::cout, wrapped, 4, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,6 @@ main(int argc, char *argv[])
|
||||||
if (parsed != scsl::Flags::ParseStatus::OK) {
|
if (parsed != scsl::Flags::ParseStatus::OK) {
|
||||||
std::cerr << "Failed to parse flags: "
|
std::cerr << "Failed to parse flags: "
|
||||||
<< scsl::Flags::ParseStatusToString(parsed) << "\n";
|
<< scsl::Flags::ParseStatusToString(parsed) << "\n";
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sctest::SimpleSuite suite;
|
sctest::SimpleSuite suite;
|
||||||
|
|
Loading…
Reference in New Issue