#pragma once #ifndef Convention_Runtime_GlobalConfig_hpp #define Convention_Runtime_GlobalConfig_hpp #include "Config.hpp" #include "File.hpp" #include namespace Convention { class GlobalConfig { public: constexpr static auto ConstConfigFile = "config.json"; static void InitExtensionEnv() { } static void GenerateEmptyConfigJson(ToolFile& file) { nlohmann::json config; config["properties"] = nlohmann::json::object(); file.SaveAsText(config.dump(4)); file.Close(); } private: size_t configLogging_tspace = 19; // "Property not found".length() ToolFile DataDir; std::map data_pair; public: GlobalConfig(const std::string& dataDir, bool isTryCreateDataDir = false, bool isLoad = true) : GlobalConfig(ToolFile(dataDir), isTryCreateDataDir, isLoad) {} GlobalConfig(ToolFile dataDir, bool isTryCreateDataDir = false, bool isLoad = true) : DataDir(std::move(dataDir)) { // Build up data folder if (!DataDir.IsDir()) { DataDir = DataDir.BackToParentDir(); } if (!DataDir.Exists()) { if (isTryCreateDataDir) { DataDir.MustExistsPath(); } else { throw std::runtime_error("Data dir not found: " + DataDir.GetFullPath()); } } // Build up init data file auto configFile = GetConfigFile(); if (!configFile.Exists()) { GenerateEmptyConfigJson(configFile); } else if (isLoad) { LoadProperties(); } } ~GlobalConfig() = default; ToolFile GetConfigFile() const { return DataDir | ConstConfigFile; } ToolFile ConfigFile() const { return GetConfigFile(); } ToolFile GetFile(const std::string& path, bool isMustExist = false) { auto file = DataDir | path; if (isMustExist) { file.MustExistsPath(); } return file; } bool EraseFile(const std::string& path) { auto file = DataDir | path; if (file.Exists()) { file.MustExistsPath(); if (file.IsFile()) { file.Remove(); file.Create(); } return true; } return false; } bool RemoveFile(const std::string& path) { auto file = DataDir | path; if (file.Exists()) { try { file.Delete(); return true; } catch (...) {} } return false; } bool CreateFile(const std::string& path) { auto file = DataDir | path; if (file.Exists()) return false; if (!file.GetParentDir().Exists()) return false; file.Create(); return true; } // Data access with operator[] nlohmann::json& operator[](const std::string& key) { return data_pair[key]; } const nlohmann::json& operator[](const std::string& key) const { auto it = data_pair.find(key); if (it == data_pair.end()) { throw std::out_of_range("Key not found: " + key); } return it->second; } bool Contains(const std::string& key) const { return data_pair.find(key) != data_pair.end(); } bool Remove(const std::string& key) { auto it = data_pair.find(key); if (it != data_pair.end()) { data_pair.erase(it); return true; } return false; } // Iterator support for foreach/for loops auto begin() { return data_pair.begin(); } auto end() { return data_pair.end(); } auto begin() const { return data_pair.cbegin(); } auto end() const { return data_pair.cend(); } size_t DataSize() const { return data_pair.size(); } GlobalConfig& SaveProperties() { auto configFile = GetConfigFile(); nlohmann::json config; config["properties"] = nlohmann::json::object(); for (const auto& [key, value] : data_pair) { config["properties"][key] = value; } configFile.SaveAsText(config.dump(4)); return *this; } GlobalConfig& LoadProperties() { auto configFile = GetConfigFile(); if (!configFile.Exists()) { data_pair.clear(); } else { try { auto content = configFile.LoadAsText(); auto config = nlohmann::json::parse(content); if (config.contains("properties") && config["properties"].is_object()) { data_pair.clear(); for (auto& [key, value] : config["properties"].items()) { data_pair[key] = value; } } else { throw std::runtime_error("Can't find properties in config file"); } } catch (const nlohmann::json::exception& e) { throw std::runtime_error("JSON parsing error: " + std::string(e.what())); } } return *this; } ToolFile GetLogFile() { auto configFile = GetConfigFile(); auto logName = configFile.GetName(true) + "_log.txt"; return GetFile(logName, true); } ToolFile LogFile() { return GetLogFile(); } private: std::function MyDefaultLogger; public: std::function DefaultLogger() const { return MyDefaultLogger ? MyDefaultLogger : [](const std::string& msg) { std::cout << msg << std::endl; }; } void SetDefaultLogger(std::function logger) { MyDefaultLogger = std::move(logger); } virtual void Log(const std::string& messageType, const std::string& message, std::function logger) { configLogging_tspace = std::max(configLogging_tspace, messageType.length()); auto currentTime = std::chrono::system_clock::now(); auto time_t = std::chrono::system_clock::to_time_t(currentTime); auto tm = *std::localtime(&time_t); std::ostringstream timeStr; timeStr << std::put_time(&tm, "%Y-%m-%d_%H-%M-%S"); std::string padding(configLogging_tspace / 2, ' '); std::string paddingEnd(configLogging_tspace - configLogging_tspace / 2, ' '); std::ostringstream logMessage; logMessage << "[" << timeStr.str() << "]" << padding << messageType << paddingEnd << ": " << message; if (logger) { logger(logMessage.str()); } else { DefaultLogger()(logMessage.str()); } } void Log(const std::string& messageType, const std::string& message) { Log(messageType, message, nullptr); } void LogPropertyNotFound(const std::string& message, std::function logger, const std::string& defaultValue) { std::string fullMessage = message; if (!defaultValue.empty()) { fullMessage += " (default: " + defaultValue + ")"; } Log("Property not found", fullMessage, logger); } void LogPropertyNotFound(const std::string& message, const std::string& defaultValue) { LogPropertyNotFound(message, nullptr, defaultValue); } void LogMessageOfPleaseCompleteConfiguration() { Log("Configuration", "Please complete the configuration"); } template T FindItem(const std::string& key, const T& defaultValue = T{}) { auto it = data_pair.find(key); if (it != data_pair.end()) { try { return it->second.get(); } catch (const nlohmann::json::exception&) { LogPropertyNotFound(std::string("Cannot convert value for key: ") + key, std::to_string(defaultValue)); } } else { LogPropertyNotFound(std::string("Key not found: ") + key, std::to_string(defaultValue)); } return defaultValue; } }; class ProjectConfig : public GlobalConfig { private: constexpr static auto ProjectConfigFileFocus = "Assets/"; public: ProjectConfig(bool isLoad = true) : GlobalConfig(ToolFile(ProjectConfigFileFocus), true, isLoad) {} static std::string GetProjectConfigFileFocus() { return ProjectConfigFileFocus; } }; } #endif // Convention_Runtime_GlobalConfig_hpp