From 18f3f6689d5a1718bd3e65ba760924f5d56d53e2 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Thu, 20 Mar 2025 14:27:57 -0400 Subject: [PATCH] feat(config): added ability to get all keys and check if a key exists in the given config file also added the ability to get a config value without specifying a default (this is only avalible to freind classes) --- src/config/private/config.cpp | 43 +++++++++++++++++++++++++++++++++++ src/config/public/config.h | 43 ++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/config/private/config.cpp b/src/config/private/config.cpp index ffce7a3..76e7d43 100644 --- a/src/config/private/config.cpp +++ b/src/config/private/config.cpp @@ -46,6 +46,7 @@ bool Config::loadConfig(const std::string& configFile) { std::cerr << "Error: " << e.what() << std::endl; return false; } + m_loaded = true; return true; } @@ -60,3 +61,45 @@ void Config::addToCache(const std::string &key, const YAML::Node &node) { void Config::registerUnknownKey(const std::string &key) { unknownKeys.push_back(key); } + +bool Config::has(const std::string &key) { + if (!m_loaded) { + throw std::runtime_error("Error! Config file not loaded"); + } + if (isKeyInCache(key)) { return true; } + + YAML::Node node = YAML::Clone(yamlRoot); + std::istringstream keyStream(key); + std::string subKey; + while (std::getline(keyStream, subKey, ':')) { + if (!node[subKey]) { + registerUnknownKey(key); + return false; + } + node = node[subKey]; // go deeper + } + + // Key exists and is of the requested type + addToCache(key, node); + return true; +} + +void recurse_keys(const YAML::Node& node, std::vector& keyList, const std::string& path = "") { + if (node.IsMap()) { + for (const auto& it : node) { + std::string key = it.first.as(); + std::string new_path = path.empty() ? key : path + ":" + key; + recurse_keys(it.second, keyList, new_path); + } + } else { + keyList.push_back(path); + } + +} + +std::vector Config::keys() const { + std::vector keyList; + YAML::Node node = YAML::Clone(yamlRoot); + recurse_keys(node, keyList); + return keyList; +} \ No newline at end of file diff --git a/src/config/public/config.h b/src/config/public/config.h index e8b96a0..be33163 100644 --- a/src/config/public/config.h +++ b/src/config/public/config.h @@ -23,14 +23,18 @@ #include #include -#include #include #include #include #include +#include +// Required for YAML parsing #include "yaml-cpp/yaml.h" +// -- Forward Def of Resource manager to let it act as a friend of Config -- +class ResourceManager; + /** * @class Config * @brief Singleton class to manage configuration settings loaded from a YAML file. @@ -94,6 +98,21 @@ private: */ void registerUnknownKey(const std::string &key); + bool m_loaded = false; + + // Only friends can access get without a default value + template + T get(const std::string &key) { + if (!m_loaded) { + throw std::runtime_error("Error! Config file not loaded"); + } + if (has(key)) { + return getFromCache(key, T()); + } else { + throw std::runtime_error("Error! Key not found in config file"); + } + } + public: /** * @brief Get the singleton instance of the Config class. @@ -136,6 +155,9 @@ public: */ template T get(const std::string &key, T defaultValue) { + if (!m_loaded) { + throw std::runtime_error("Error! Config file not loaded"); + } // --- Check if the key has already been checked for existence if (std::find(unknownKeys.begin(), unknownKeys.end(), key) != unknownKeys.end()) { return defaultValue; // If the key has already been added to the unknown cache do not traverse the YAML tree or hit the cache @@ -171,6 +193,19 @@ public: } } + /** + * @brief Check if the key exists in the given config file + * @param key Key to check; + * @return boolean true or false + */ + bool has(const std::string &key); + + /** + * @brief Get all keys defined in the configuration file. + * @return Vector of all keys in the configuration file. + */ + std::vector keys() const; + /** * @brief Print the configuration file path and the YAML root node. * @param os Output stream. @@ -178,6 +213,10 @@ public: * @return Output stream. */ friend std::ostream& operator<<(std::ostream& os, const Config& config) { + if (!config.m_loaded) { + os << "Config file not loaded" << std::endl; + return os; + } if (!config.debug) { os << "Config file: " << config.configFilePath << std::endl; } else{ @@ -190,6 +229,8 @@ public: // Setup gTest class as a friend friend class configTestPrivateAccessor; + // -- Resource Manager is a friend of config so it can create a seperate instance + friend class ResourceManager; }; #endif \ No newline at end of file