diff --git a/src/config/private/config.cpp b/src/config/private/config.cpp index 5598acb..76e7d43 100644 --- a/src/config/private/config.cpp +++ b/src/config/private/config.cpp @@ -46,7 +46,7 @@ bool Config::loadConfig(const std::string& configFile) { std::cerr << "Error: " << e.what() << std::endl; return false; } - loaded = true; + m_loaded = true; return true; } @@ -61,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 c563560..fa04eea 100644 --- a/src/config/public/config.h +++ b/src/config/public/config.h @@ -23,15 +23,19 @@ #include #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. @@ -96,6 +100,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. @@ -138,7 +157,7 @@ public: */ template T get(const std::string &key, T defaultValue) { - if (!loaded) { + if (!m_loaded) { throw std::runtime_error("Configuration file not loaded! This should be done at the very start of whatever main function you are using (and only done once!)"); } // --- Check if the key has already been checked for existence @@ -176,6 +195,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. @@ -183,6 +215,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{ @@ -195,6 +231,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