diff --git a/src/debug/config/CMakeLists.txt b/src/debug/config/CMakeLists.txt deleted file mode 100644 index 08994778..00000000 --- a/src/debug/config/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -# Minimum required CMake version -cmake_minimum_required(VERSION 3.20) - -# Project name and version, using C and C++ languages -project(lithium-config VERSION 1.0.0 LANGUAGES C CXX) - -# Project description and information -# This project is the official configuration module for the Lithium server. -# Author: Max Qian -# License: GPL3 -# Project Name: Lithium-Config -# Description: The official config module for lithium server -# Author: Max Qian -# License: GPL3 - -# Project sources -set(PROJECT_SOURCES - configor.cpp -) - -# Project headers -set(PROJECT_HEADERS - configor.hpp -) - -# Required libraries for the project -set(PROJECT_LIBS - loguru - ${CMAKE_THREAD_LIBS_INIT} -) - -if (WIN32) - list(APPEND PROJECT_LIBS ws2_32) -endif() - -# Create object library -add_library(${PROJECT_NAME}_OBJECT OBJECT ${PROJECT_SOURCES} ${PROJECT_HEADERS}) - -# Set object library property to be position independent code -set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE ON) - -# Create static library -add_library(${PROJECT_NAME} STATIC $) - -# Set static library properties -set_target_properties(${PROJECT_NAME} PROPERTIES - VERSION ${PROJECT_VERSION} # Version number - SOVERSION 1 # Compatibility version - OUTPUT_NAME ${PROJECT_NAME} # Output name -) - -# Include directories so that project headers can be included -target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -# Link libraries required by the project -target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_LIBS}) - -# Install target to install the static library to a specified location -install(TARGETS ${PROJECT_NAME} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) diff --git a/src/debug/config/configor.cpp b/src/debug/config/configor.cpp deleted file mode 100644 index 8d052e72..00000000 --- a/src/debug/config/configor.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* - * configor.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-4-30 - -Description: Configor - -**************************************************/ - -#include "configor.hpp" - -#include -#include -#include -#include - -#include - -#include "addon/manager.hpp" - -#include "atom/function/global_ptr.hpp" -#include "atom/io/io.hpp" -#include "atom/log/loguru.hpp" -#include "atom/system/env.hpp" -#include "atom/type/json.hpp" - -#include "utils/constant.hpp" - -using json = nlohmann::json; - -namespace lithium { - -namespace internal { -auto removeComments(const std::string& json5) -> std::string { - std::string result; - bool inSingleLineComment = false; - bool inMultiLineComment = false; - size_t index = 0; - - while (index < json5.size()) { - // Check for single-line comments - if (!inMultiLineComment && !inSingleLineComment && - index + 1 < json5.size() && json5[index] == '/' && - json5[index + 1] == '/') { - inSingleLineComment = true; - index += 2; // Skip "//" - } - // Check for multi-line comments - else if (!inSingleLineComment && !inMultiLineComment && - index + 1 < json5.size() && json5[index] == '/' && - json5[index + 1] == '*') { - inMultiLineComment = true; - index += 2; // Skip "/*" - } - // Handle end of single-line comments - else if (inSingleLineComment && json5[index] == '\n') { - inSingleLineComment = false; // End single-line comment at newline - result += '\n'; // Keep the newline - index++; // Move to the next character - } - // Handle end of multi-line comments - else if (inMultiLineComment && index + 1 < json5.size() && - json5[index] == '*' && json5[index + 1] == '/') { - inMultiLineComment = false; // End multi-line comment at "*/" - index += 2; // Skip "*/" - } - // Handle multi-line strings - else if (!inSingleLineComment && !inMultiLineComment && - json5[index] == '"') { - result += json5[index]; // Add starting quote - index++; // Move to the string content - while (index < json5.size() && - (json5[index] != '"' || json5[index - 1] == '\\')) { - // Check if the end of the string is reached - if (json5[index] == '\\' && index + 1 < json5.size() && - json5[index + 1] == '\n') { - // Handle multi-line strings - index += 2; // Skip backslash and newline - } else { - result += json5[index]; - index++; - } - } - if (index < json5.size()) { - result += json5[index]; // Add ending quote - } - index++; // Move to the next character - } - // If not in a comment, add character to result - else if (!inSingleLineComment && !inMultiLineComment) { - result += json5[index]; - index++; - } else { - index++; // If in a comment, continue moving - } - } - - return result; -} - -auto trimQuotes(const std::string& str) -> std::string { - if (str.front() == '"' && str.back() == '"') { - return str.substr( - 1, str.size() - 2); // Remove leading and trailing quotes - } - return str; -} - -auto convertJSON5toJSON(const std::string& json5) -> std::string { - std::string json = removeComments(json5); - - // Handle keys without quotes - std::string result; - bool inString = false; - size_t index = 0; - - while (index < json.size()) { - // Check for the start of a string - if (json[index] == '"') { - inString = true; - result += json[index]; - } else if ((std::isspace(static_cast(json[index])) != - 0) && - !inString) { - result += json[index]; // Keep whitespace - } else if ((std::isspace(static_cast(json[index])) == - 0) && - !inString && - ((std::isalnum(static_cast(json[index])) != - 0) || - json[index] == '_')) { - // Add keys without quotes - size_t start = index; - while ( - index < json.size() && - ((std::isalnum(static_cast(json[index])) != 0) || - json[index] == '_' || json[index] == '-')) { - index++; - } - result += "\"" + json.substr(start, index - start) + - "\""; // Convert to quoted key - continue; // Skip index++, as it has already moved to the end of - // the loop - } else { - result += json[index]; // Add other characters directly - } - index++; - } - - return result; -} -} // namespace internal - -class ConfigManagerImpl { -public: - mutable std::shared_mutex rwMutex; - json config; - asio::io_context ioContext; - std::thread ioThread; -}; - -ConfigManager::ConfigManager() - : m_impl_(std::make_unique()) { - asio::executor_work_guard workGuard( - m_impl_->ioContext.get_executor()); - m_impl_->ioThread = std::thread([this] { m_impl_->ioContext.run(); }); - if (loadFromFile("config.json")) { - DLOG_F(INFO, "Config loaded successfully."); - } -} - -ConfigManager::~ConfigManager() { - m_impl_->ioContext.stop(); - if (m_impl_->ioThread.joinable()) { - m_impl_->ioThread.join(); - } - if (saveToFile("config.json")) { - DLOG_F(INFO, "Config saved successfully."); - } -} - -auto ConfigManager::createShared() -> std::shared_ptr { - static std::shared_ptr instance = - std::make_shared(); - return instance; -} - -auto ConfigManager::createUnique() -> std::unique_ptr { - return std::make_unique(); -} - -auto ConfigManager::loadFromFile(const fs::path& path) -> bool { - std::shared_lock lock(m_impl_->rwMutex); - try { - std::ifstream ifs(path); - if (!ifs || ifs.peek() == std::ifstream::traits_type::eof()) { - LOG_F(ERROR, "Failed to open file: {}", path.string()); - return false; - } - json j = json::parse(ifs); - if (j.empty()) { - LOG_F(WARNING, "Config file is empty: {}", path.string()); - return false; - } - mergeConfig(j); - LOG_F(INFO, "Config loaded from file: {}", path.string()); - return true; - } catch (const json::exception& e) { - LOG_F(ERROR, "Failed to parse file: {}, error message: {}", - path.string(), e.what()); - } catch (const std::exception& e) { - LOG_F(ERROR, "Failed to load config file: {}, error message: {}", - path.string(), e.what()); - } - return false; -} - -auto ConfigManager::loadFromDir(const fs::path& dir_path, - bool recursive) -> bool { - std::shared_lock lock(m_impl_->rwMutex); - std::weak_ptr componentManagerPtr; - GET_OR_CREATE_WEAK_PTR(componentManagerPtr, ComponentManager, - Constants::COMPONENT_MANAGER); - auto componentManager = componentManagerPtr.lock(); - if (!componentManager) { - LOG_F(ERROR, "ComponentManager not found"); - return false; - } - std::shared_ptr yamlToJsonComponent; - try { - for (const auto& entry : fs::directory_iterator(dir_path)) { - if (entry.is_regular_file()) { - if (entry.path().extension() == ".json" || - entry.path().extension() == ".lithium") { - if (!loadFromFile(entry.path())) { - LOG_F(WARNING, "Failed to load config file: {}", - entry.path().string()); - } - } else if (entry.path().extension() == ".json5" || - entry.path().extension() == ".lithium5") { - std::ifstream ifs(entry.path()); - if (!ifs || - ifs.peek() == std::ifstream::traits_type::eof()) { - LOG_F(ERROR, "Failed to open file: {}", - entry.path().string()); - return false; - } - std::string json5((std::istreambuf_iterator(ifs)), - std::istreambuf_iterator()); - json j = json::parse(internal::convertJSON5toJSON(json5)); - if (j.empty()) { - LOG_F(WARNING, "Config file is empty: {}", - entry.path().string()); - return false; - } - mergeConfig(j); - } - else if (entry.path().extension() == ".yaml") { - // There we will use yaml->json component to convert yaml to - // json - if (!yamlToJsonComponent) { - yamlToJsonComponent = - componentManager->getComponent("yamlToJson") - .value() - .lock(); - if (!yamlToJsonComponent) { - LOG_F(ERROR, "yamlToJson component not found"); - return false; - } - - } - yamlToJsonComponent->dispatch("yaml_to_json", - entry.path().string()); - if (!loadFromFile(entry.path())) { - LOG_F(WARNING, "Failed to load config file: {}", - entry.path().string()); - } - } - } else if (recursive && entry.is_directory()) { - loadFromDir(entry.path(), true); - } - } - LOG_F(INFO, "Config loaded from directory: {}", dir_path.string()); - return true; - } catch (const std::exception& e) { - LOG_F(ERROR, "Failed to load config file from: {}, error message: {}", - dir_path.string(), e.what()); - return false; - } -} - -auto ConfigManager::getValue(const std::string& key_path) const - -> std::optional { - std::shared_lock lock(m_impl_->rwMutex); - const json* p = &m_impl_->config; - for (const auto& key : key_path | std::views::split('/')) { - std::string keyStr = std::string(key.begin(), key.end()); - if (p->is_object() && p->contains(keyStr)) { - p = &(*p)[keyStr]; - } else { - LOG_F(WARNING, "Key not found: {}", key_path); - return std::nullopt; - } - } - return *p; -} - -auto ConfigManager::setValue(const std::string& key_path, - const json& value) -> bool { - std::unique_lock lock(m_impl_->rwMutex); - - // Check if the key_path is "/" and set the root value directly - if (key_path == "/") { - m_impl_->config = value; - LOG_F(INFO, "Set root config: {}", m_impl_->config.dump()); - return true; - } - - json* p = &m_impl_->config; - auto keys = key_path | std::views::split('/'); - - for (auto it = keys.begin(); it != keys.end(); ++it) { - std::string keyStr = std::string((*it).begin(), (*it).end()); - LOG_F(INFO, "Set config: {}", keyStr); - - if (std::next(it) == keys.end()) { // If this is the last key - (*p)[keyStr] = value; - LOG_F(INFO, "Final config: {}", m_impl_->config.dump()); - return true; - } - - if (!p->contains(keyStr) || !(*p)[keyStr].is_object()) { - (*p)[keyStr] = json::object(); - } - p = &(*p)[keyStr]; - LOG_F(INFO, "Current config: {}", p->dump()); - } - return false; -} - -auto ConfigManager::setValue(const std::string& key_path, - json&& value) -> bool { - std::unique_lock lock(m_impl_->rwMutex); - - // Check if the key_path is "/" and set the root value directly - if (key_path == "/") { - m_impl_->config = std::move(value); - LOG_F(INFO, "Set root config: {}", m_impl_->config.dump()); - return true; - } - - json* p = &m_impl_->config; - auto keys = key_path | std::views::split('/'); - - for (auto it = keys.begin(); it != keys.end(); ++it) { - std::string keyStr = std::string((*it).begin(), (*it).end()); - LOG_F(INFO, "Set config: {}", keyStr); - - if (std::next(it) == keys.end()) { // If this is the last key - (*p)[keyStr] = std::move(value); - LOG_F(INFO, "Final config: {}", m_impl_->config.dump()); - return true; - } - - if (!p->contains(keyStr) || !(*p)[keyStr].is_object()) { - (*p)[keyStr] = json::object(); - } - p = &(*p)[keyStr]; - LOG_F(INFO, "Current config: {}", p->dump()); - } - return false; -} - -auto ConfigManager::appendValue(const std::string& key_path, - const json& value) -> bool { - std::unique_lock lock(m_impl_->rwMutex); - - json* p = &m_impl_->config; - auto keys = key_path | std::views::split('/'); - - for (auto it = keys.begin(); it != keys.end(); ++it) { - std::string keyStr = std::string((*it).begin(), (*it).end()); - - if (std::next(it) == keys.end()) { // If this is the last key - if (!p->contains(keyStr)) { - (*p)[keyStr] = json::array(); - } - - if (!(*p)[keyStr].is_array()) { - LOG_F(ERROR, "Target key is not an array"); - return false; - } - - (*p)[keyStr].push_back(value); - LOG_F(INFO, "Appended value to config: {}", m_impl_->config.dump()); - return true; - } - - if (!p->contains(keyStr) || !(*p)[keyStr].is_object()) { - (*p)[keyStr] = json::object(); - } - p = &(*p)[keyStr]; - } - return false; -} - -auto ConfigManager::deleteValue(const std::string& key_path) -> bool { - std::unique_lock lock(m_impl_->rwMutex); - json* p = &m_impl_->config; - std::vector keys; - for (const auto& key : key_path | std::views::split('/')) { - keys.emplace_back(key.begin(), key.end()); - } - for (auto it = keys.begin(); it != keys.end(); ++it) { - if (std::next(it) == keys.end()) { - if (p->is_object() && p->contains(*it)) { - p->erase(*it); - LOG_F(INFO, "Deleted key: {}", key_path); - return true; - } - LOG_F(WARNING, "Key not found for deletion: {}", key_path); - return false; - } - if (!p->is_object() || !p->contains(*it)) { - LOG_F(WARNING, "Key not found for deletion: {}", key_path); - return false; - } - p = &(*p)[*it]; - } - return false; -} - -auto ConfigManager::hasValue(const std::string& key_path) const -> bool { - return getValue(key_path).has_value(); -} - -auto ConfigManager::saveToFile(const fs::path& file_path) const -> bool { - std::unique_lock lock(m_impl_->rwMutex); - std::ofstream ofs(file_path); - if (!ofs) { - LOG_F(ERROR, "Failed to open file: {}", file_path.string()); - return false; - } - try { - ofs << m_impl_->config.dump(4); - ofs.close(); - LOG_F(INFO, "Config saved to file: {}", file_path.string()); - return true; - } catch (const std::exception& e) { - LOG_F(ERROR, "Failed to save config to file: {}, error message: {}", - file_path.string(), e.what()); - return false; - } -} - -void ConfigManager::tidyConfig() { - std::unique_lock lock(m_impl_->rwMutex); - json updatedConfig; - for (const auto& [key, value] : m_impl_->config.items()) { - json* p = &updatedConfig; - for (const auto& subKey : key | std::views::split('/')) { - std::string subKeyStr = std::string(subKey.begin(), subKey.end()); - if (!p->contains(subKeyStr)) { - (*p)[subKeyStr] = json::object(); - } - p = &(*p)[subKeyStr]; - } - *p = value; - } - m_impl_->config = std::move(updatedConfig); - LOG_F(INFO, "Config tidied."); -} - -void ConfigManager::mergeConfig(const json& src, json& target) { - for (auto it = src.begin(); it != src.end(); ++it) { - LOG_F(INFO, "Merge config: {}", it.key()); - if (it->is_object() && target.contains(it.key()) && - target[it.key()].is_object()) { - mergeConfig(*it, target[it.key()]); - } else { - target[it.key()] = *it; - } - } -} - -void ConfigManager::mergeConfig(const json& src) { - LOG_F(INFO, "Current config: {}", m_impl_->config.dump()); - std::function merge = [&](json& target, - const json& source) { - for (auto it = source.begin(); it != source.end(); ++it) { - if (it.value().is_object() && target.contains(it.key()) && - target[it.key()].is_object()) { - LOG_F(INFO, "Merge config: {}", it.key()); - merge(target[it.key()], it.value()); - } else { - LOG_F(INFO, "Merge config: {}", it.key()); - target[it.key()] = it.value(); - } - } - }; - - merge(m_impl_->config, src); - LOG_F(INFO, "Config merged."); -} - -void ConfigManager::clearConfig() { - std::unique_lock lock(m_impl_->rwMutex); - m_impl_->config.clear(); - LOG_F(INFO, "Config cleared."); -} - -void ConfigManager::asyncLoadFromFile(const fs::path& path, - std::function callback) { - asio::post(m_impl_->ioContext, [this, path, callback]() { - bool success = loadFromFile(path); - // Post back to caller's thread or IO context - asio::post(m_impl_->ioContext, - [callback, success]() { callback(success); }); - }); -} - -void ConfigManager::asyncSaveToFile(const fs::path& file_path, - std::function callback) const { - asio::post(m_impl_->ioContext, [this, file_path, callback]() { - bool success = saveToFile(file_path); - // Post back to caller's thread or IO context - asio::post(m_impl_->ioContext, - [callback, success]() { callback(success); }); - }); -} - -auto ConfigManager::getKeys() const -> std::vector { - std::shared_lock lock(m_impl_->rwMutex); - std::vector paths; - std::function listPaths = - [&](const json& j, std::string path) { - for (auto it = j.begin(); it != j.end(); ++it) { - if (it.value().is_object()) { - listPaths(it.value(), path + "/" + it.key()); - } else { - paths.emplace_back(path + "/" + it.key()); - } - } - }; - listPaths(m_impl_->config, ""); - return paths; -} - -auto ConfigManager::listPaths() const -> std::vector { - std::shared_lock lock(m_impl_->rwMutex); - std::vector paths; - std::weak_ptr envPtr; - GET_OR_CREATE_WEAK_PTR(envPtr, atom::utils::Env, Constants::ENVIRONMENT); - auto env = envPtr.lock(); - if (!env) { - LOG_F(ERROR, "Failed to get environment instance"); - return paths; - } - - // Get the config directory from the command line arguments - auto configDir = env->get("config"); - if (configDir.empty() || !atom::io::isFolderExists(configDir)) { - // Get the config directory from the environment if not set or invalid - configDir = env->getEnv("LITHIUM_CONFIG_DIR", "./config"); - } - - // Check for JSON files in the config directory - return atom::io::checkFileTypeInFolder(configDir, {".json"}, - atom::io::FileOption::PATH); -} -} // namespace lithium diff --git a/src/debug/config/configor.hpp b/src/debug/config/configor.hpp deleted file mode 100644 index b385ed35..00000000 --- a/src/debug/config/configor.hpp +++ /dev/null @@ -1,275 +0,0 @@ -/* - * configor.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-4-4 - -Description: Configor - -**************************************************/ - -#ifndef LITHIUM_CONFIG_CONFIGOR_HPP -#define LITHIUM_CONFIG_CONFIGOR_HPP - -#include -#include -#include -#include - -#include "atom/error/exception.hpp" -#include "atom/type/json_fwd.hpp" - -#include "utils/constant.hpp" - -namespace fs = std::filesystem; -using json = nlohmann::json; - -#define GetIntConfig(path) \ - GetPtr(Constatns::CONFIG_MANAGER) \ - .value() \ - ->getValue(path) \ - .value() \ - .get() - -#define GetFloatConfig(path) \ - GetPtr(Constatns::CONFIG_MANAGER) \ - .value() \ - ->getValue(path) \ - .value() \ - .get() - -#define GetBoolConfig(path) \ - GetPtr(Constatns::CONFIG_MANAGER) \ - .value() \ - ->getValue(path) \ - .value() \ - .get() - -#define GetDoubleConfig(path) \ - GetPtr(Constatns::CONFIG_MANAGER) \ - .value() \ - ->getValue(path) \ - .value() \ - .get() - -#define GetStringConfig(path) \ - GetPtr(Constatns::CONFIG_MANAGER) \ - .value() \ - ->getValue(path) \ - .value() \ - .get() - -#define GET_CONFIG_VALUE(configManager, path, type, outputVar) \ - type outputVar; \ - do { \ - auto opt = (configManager)->getValue(path); \ - if (opt.has_value()) { \ - try { \ - (outputVar) = opt.value().get(); \ - } catch (const std::bad_optional_access& e) { \ - LOG_F(ERROR, "Bad access to config value for {}: {}", path, \ - e.what()); \ - THROW_BAD_CONFIG_EXCEPTION(e.what()); \ - } catch (const json::exception& e) { \ - LOG_F(ERROR, "Invalid config value for {}: {}", path, \ - e.what()); \ - THROW_INVALID_CONFIG_EXCEPTION(e.what()); \ - } catch (const std::exception& e) { \ - THROW_UNKOWN(e.what()); \ - } \ - } else { \ - LOG_F(WARNING, "Config value for {} not found", path); \ - THROW_OBJ_NOT_EXIST("Config value for", path); \ - } \ - } while (0) - -class BadConfigException : public atom::error::Exception { - using atom::error::Exception::Exception; -}; - -#define THROW_BAD_CONFIG_EXCEPTION(...) \ - throw BadConfigException(ATOM_FILE_NAME, ATOM_FILE_LINE, ATOM_FUNC_NAME, \ - __VA_ARGS__) - -#define THROW_NESTED_BAD_CONFIG_EXCEPTION(...) \ - BadConfigException::rethrowNested(ATOM_FILE_NAME, ATOM_FILE_LINE, \ - ATOM_FUNC_NAME, __VA_ARGS__) - -class InvalidConfigException : public BadConfigException { - using BadConfigException::BadConfigException; -}; - -#define THROW_INVALID_CONFIG_EXCEPTION(...) \ - throw InvalidConfigException(ATOM_FILE_NAME, ATOM_FILE_LINE, \ - ATOM_FUNC_NAME, __VA_ARGS__) - -#define THROW_NESTED_INVALID_CONFIG_EXCEPTION(...) \ - InvalidConfigException::rethrowNested(ATOM_FILE_NAME, ATOM_FILE_LINE, \ - ATOM_FUNC_NAME, __VA_ARGS__) - -namespace lithium { -class ConfigManagerImpl; -/** - * @brief The ConfigManager class manages configuration data using JSON format. - * - * This class provides methods to manipulate configuration values, load from - * files or directories, save to a file, and perform various operations like - * merging configurations. - */ -class ConfigManager { -public: - /** - * @brief Default constructor. - */ - ConfigManager(); - - /** - * @brief Destructor. - */ - ~ConfigManager(); - - /** - * @brief Creates a shared pointer instance of ConfigManager. - * @return std::shared_ptr Shared pointer to ConfigManager - * instance. - */ - static auto createShared() -> std::shared_ptr; - - /** - * @brief Creates a unique pointer instance of ConfigManager. - * @return std::unique_ptr Unique pointer to ConfigManager - * instance. - */ - static auto createUnique() -> std::unique_ptr; - - /** - * @brief Retrieves the value associated with the given key path. - * @param key_path The path to the configuration value. - * @return std::optional The optional JSON value if found. - */ - [[nodiscard]] auto getValue(const std::string& key_path) const - -> std::optional; - - /** - * @brief Sets the value for the specified key path. - * @param key_path The path to set the configuration value. - * @param value The JSON value to set. - * @return bool True if the value was successfully set, false otherwise. - */ - auto setValue(const std::string& key_path, const json& value) -> bool; - - /** - * @brief Sets the value for the specified key path. - * @param key_path The path to set the configuration value. - * @param value The JSON value to set. - * @return bool True if the value was successfully set, false otherwise. - */ - auto setValue(const std::string& key_path, json&& value) -> bool; - /** - * @brief Appends a value to an array at the specified key path. - * @param key_path The path to the array. - * @param value The JSON value to append. - * @return bool True if the value was successfully appended, false - * otherwise. - */ - auto appendValue(const std::string& key_path, const json& value) -> bool; - - /** - * @brief Deletes the value associated with the given key path. - * @param key_path The path to the configuration value to delete. - * @return bool True if the value was successfully deleted, false otherwise. - */ - auto deleteValue(const std::string& key_path) -> bool; - - /** - * @brief Checks if a value exists for the given key path. - * @param key_path The path to check. - * @return bool True if a value exists for the key path, false otherwise. - */ - [[nodiscard]] auto hasValue(const std::string& key_path) const -> bool; - - /** - * @brief Retrieves all keys in the configuration. - * @return std::vector A vector of keys in the configuration. - */ - [[nodiscard]] auto getKeys() const -> std::vector; - - /** - * @brief Lists all configuration files in specified directory. - * @return std::vector A vector of paths to configuration - * files. - */ - [[nodiscard]] auto listPaths() const -> std::vector; - - /** - * @brief Loads configuration data from a file. - * @param path The path to the file containing configuration data. - * @return bool True if the file was successfully loaded, false otherwise. - */ - auto loadFromFile(const fs::path& path) -> bool; - - /** - * @brief Loads configuration data from a directory. - * @param dir_path The path to the directory containing configuration files. - * @param recursive Flag indicating whether to recursively load from - * subdirectories. - * @return bool True if the directory was successfully loaded, false - * otherwise. - */ - auto loadFromDir(const fs::path& dir_path, bool recursive = false) -> bool; - - /** - * @brief Saves the current configuration to a file. - * @param file_path The path to save the configuration file. - * @return bool True if the configuration was successfully saved, false - * otherwise. - */ - [[nodiscard]] auto saveToFile(const fs::path& file_path) const -> bool; - - /** - * @brief Cleans up the configuration by removing unused entries or - * optimizing data. - */ - void tidyConfig(); - - /** - * @brief Clears all configuration data. - */ - void clearConfig(); - - /** - * @brief Merges the current configuration with the provided JSON data. - * @param src The JSON object to merge into the current configuration. - */ - void mergeConfig(const json& src); - - /** - * @brief Asynchronously loads configuration data from a file. - * @param path The path to the file containing configuration data. - * @param callback The callback function to invoke upon completion. - */ - void asyncLoadFromFile(const fs::path& path, - std::function callback); - - /** - * @brief Asynchronously saves the current configuration to a file. - * @param file_path The path to save the configuration file. - * @param callback The callback function to invoke upon completion. - */ - void asyncSaveToFile(const fs::path& file_path, - std::function callback) const; - -private: - std::unique_ptr - m_impl_; ///< Implementation-specific pointer. - - void mergeConfig(const json& src, json& target); -}; - -} // namespace lithium - -#endif diff --git a/src/debug/config/xmake.lua b/src/debug/config/xmake.lua deleted file mode 100644 index dce5e9a8..00000000 --- a/src/debug/config/xmake.lua +++ /dev/null @@ -1,48 +0,0 @@ -set_project("lithium-config") -set_version("1.0.0") - --- Set the C++ standard -set_languages("cxx20") - --- Add required packages -add_requires("loguru", "pthread") - --- Define libraries -local project_libs = { - "loguru", - "pthread" -} - --- Source files -local source_files = { - "configor.cpp" -} - --- Header files -local header_files = { - "configor.hpp" -} - --- Object Library -target("lithium-config_object") - set_kind("object") - add_files(table.unpack(source_files)) - add_headerfiles(table.unpack(header_files)) - add_packages(table.unpack(project_libs)) -target_end() - --- Static Library -target("lithium-config") - set_kind("static") - add_deps("lithium-config_object") - add_files(table.unpack(source_files)) - add_headerfiles(table.unpack(header_files)) - add_packages(table.unpack(project_libs)) - add_includedirs(".") - set_targetdir("$(buildir)/lib") - set_installdir("$(installdir)/lib") - set_version("1.0.0", {build = "%Y%m%d%H%M"}) - on_install(function (target) - os.cp(target:targetfile(), path.join(target:installdir(), "lib")) - end) -target_end() diff --git a/src/debug/console.cpp b/src/debug/console.cpp index 544c15d6..4e61425e 100644 --- a/src/debug/console.cpp +++ b/src/debug/console.cpp @@ -13,12 +13,14 @@ #endif #include "atom/error/exception.hpp" +#include "atom/log/loguru.hpp" namespace lithium::debug { #ifdef _WIN32 void clearScreen() { + LOG_F(INFO, "Clearing screen"); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; DWORD cellCount; @@ -26,10 +28,12 @@ void clearScreen() { COORD homeCoords = {0, 0}; if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); THROW_RUNTIME_ERROR("Invalid handle"); } if (GetConsoleScreenBufferInfo(hConsole, &csbi) == 0) { + LOG_F(ERROR, "Console buffer info error"); THROW_RUNTIME_ERROR("Console buffer info error"); } @@ -37,22 +41,27 @@ void clearScreen() { if (!FillConsoleOutputCharacter(hConsole, (TCHAR)' ', cellCount, homeCoords, &count)) { + LOG_F(ERROR, "Fill console output error"); THROW_RUNTIME_ERROR("Fill console output error"); -} + } if (FillConsoleOutputAttribute(hConsole, csbi.wAttributes, cellCount, - homeCoords, &count) == 0) { + homeCoords, &count) == 0) { + LOG_F(ERROR, "Fill console attribute error"); THROW_RUNTIME_ERROR("Fill console attribute error"); -} + } SetConsoleCursorPosition(hConsole, homeCoords); + LOG_F(INFO, "Screen cleared"); } void setTextColor(Color color) { + LOG_F(INFO, "Setting text color"); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); THROW_RUNTIME_ERROR("Invalid handle"); -} + } WORD attributes = 0; @@ -88,13 +97,16 @@ void setTextColor(Color color) { } SetConsoleTextAttribute(hConsole, attributes); + LOG_F(INFO, "Text color set"); } void setBackgroundColor(Color color) { + LOG_F(INFO, "Setting background color"); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); THROW_RUNTIME_ERROR("Invalid handle"); -} + } WORD attributes = 0; @@ -132,68 +144,118 @@ void setBackgroundColor(Color color) { CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hConsole, &csbi); SetConsoleTextAttribute(hConsole, (csbi.wAttributes & 0x0F) | attributes); + LOG_F(INFO, "Background color set"); } void resetTextFormat() { + LOG_F(INFO, "Resetting text format"); setTextColor(Color::Default); setBackgroundColor(Color::Default); + LOG_F(INFO, "Text format reset"); } void moveCursor(int row, int col) { + LOG_F(INFO, "Moving cursor to row: {}, col: {}", row, col); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); THROW_RUNTIME_ERROR("Invalid handle"); -} + } COORD position = {static_cast(col), static_cast(row)}; SetConsoleCursorPosition(hConsole, position); + LOG_F(INFO, "Cursor moved"); } void hideCursor() { + LOG_F(INFO, "Hiding cursor"); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); THROW_RUNTIME_ERROR("Invalid handle"); -} + } CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hConsole, &cursorInfo); cursorInfo.bVisible = FALSE; SetConsoleCursorInfo(hConsole, &cursorInfo); + LOG_F(INFO, "Cursor hidden"); } void showCursor() { + LOG_F(INFO, "Showing cursor"); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); THROW_RUNTIME_ERROR("Invalid handle"); -} + } CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hConsole, &cursorInfo); cursorInfo.bVisible = TRUE; SetConsoleCursorInfo(hConsole, &cursorInfo); + LOG_F(INFO, "Cursor shown"); } auto getTerminalSize() -> std::pair { + LOG_F(INFO, "Getting terminal size"); HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); THROW_RUNTIME_ERROR("Invalid handle"); -} + } CONSOLE_SCREEN_BUFFER_INFO csbi; if (GetConsoleScreenBufferInfo(hConsole, &csbi) == 0) { + LOG_F(ERROR, "Console buffer info error"); THROW_RUNTIME_ERROR("Console buffer info error"); -} + } int rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; int cols = csbi.srWindow.Right - csbi.srWindow.Left + 1; + LOG_F(INFO, "Terminal size: rows={}, cols={}", rows, cols); return {rows, cols}; } +void setConsoleSize(int width, int height) { + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole == INVALID_HANDLE_VALUE) { + LOG_F(ERROR, "Invalid handle"); + return; + } + + COORD bufferSize; + bufferSize.X = width; + bufferSize.Y = height; + if (!SetConsoleScreenBufferSize(hConsole, bufferSize)) { + LOG_F(ERROR, "Unable to set console buffer size"); + return; + } + + SMALL_RECT windowSize; + windowSize.Left = 0; + windowSize.Top = 0; + windowSize.Right = width - 1; + windowSize.Bottom = height - 1; + + if (!SetConsoleWindowInfo(hConsole, TRUE, &windowSize)) { + LOG_F(ERROR, "Unable to set console window size"); + return; + } + + LOG_F(INFO, "Console size set to {}x{}.", width, height); +} + #else -void clearScreen() { std::cout << "\033[2J\033[1;1H"; } +void clearScreen() { + LOG_F(INFO, "Clearing screen"); + std::cout << "\033[2J\033[1;1H"; + LOG_F(INFO, "Screen cleared"); +} void setTextColor(Color color) { + LOG_F(INFO, "Setting text color"); switch (color) { case Color::Black: std::cout << "\033[30m"; @@ -224,9 +286,11 @@ void setTextColor(Color color) { std::cout << "\033[39m"; break; } + LOG_F(INFO, "Text color set"); } void setBackgroundColor(Color color) { + LOG_F(INFO, "Setting background color"); switch (color) { case Color::Black: std::cout << "\033[40m"; @@ -257,55 +321,99 @@ void setBackgroundColor(Color color) { std::cout << "\033[49m"; break; } + LOG_F(INFO, "Background color set"); } -void resetTextFormat() { std::cout << "\033[0m"; } +void resetTextFormat() { + LOG_F(INFO, "Resetting text format"); + std::cout << "\033[0m"; + LOG_F(INFO, "Text format reset"); +} void moveCursor(int row, int col) { + LOG_F(INFO, "Moving cursor to row: {}, col: {}", row, col); std::cout << "\033[" << row << ";" << col << "H"; + LOG_F(INFO, "Cursor moved"); } -void hideCursor() { std::cout << "\033[?25l"; } +void hideCursor() { + LOG_F(INFO, "Hiding cursor"); + std::cout << "\033[?25l"; + LOG_F(INFO, "Cursor hidden"); +} -void showCursor() { std::cout << "\033[?25h"; } +void showCursor() { + LOG_F(INFO, "Showing cursor"); + std::cout << "\033[?25h"; + LOG_F(INFO, "Cursor shown"); +} auto getTerminalSize() -> std::pair { + LOG_F(INFO, "Getting terminal size"); struct winsize w {}; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + LOG_F(INFO, "Terminal size: rows={}, cols={}", w.ws_row, w.ws_col); return {w.ws_row, w.ws_col}; } +void setTerminalSize(int width, int height) { + LOG_F(INFO, "Setting terminal size to width: {}, height: {}", width, + height); + struct winsize ws; + + ws.ws_col = width; + ws.ws_row = height; + ws.ws_xpixel = 0; + ws.ws_ypixel = 0; + + if (ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) == -1) { + LOG_F(ERROR, "Error: Unable to set terminal size."); + std::cerr << "Error: Unable to set terminal size." << std::endl; + return; + } + + LOG_F(INFO, "Terminal size set to {} x {}.", width, height); +} + #endif auto supportsColor() -> bool { + LOG_F(INFO, "Checking if terminal supports color"); #ifdef _WIN32 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) { + LOG_F(WARNING, "Invalid handle"); return false; } DWORD dwMode = 0; if (GetConsoleMode(hOut, &dwMode) == 0) { + LOG_F(WARNING, "GetConsoleMode failed"); return false; } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - return SetConsoleMode(hOut, dwMode) != 0; + bool result = SetConsoleMode(hOut, dwMode) != 0; + LOG_F(INFO, "Terminal supports color: {}", result ? "true" : "false"); + return result; #else const char* term = std::getenv("TERM"); if (term == nullptr) { + LOG_F(WARNING, "TERM environment variable not set"); return false; } std::string termStr(term); - return (termStr == "xterm" || termStr == "xterm-color" || - termStr == "xterm-256color" || termStr == "screen" || - termStr == "screen-256color" || termStr == "tmux" || - termStr == "tmux-256color" || termStr == "rxvt-unicode" || - termStr == "rxvt-unicode-256color" || termStr == "linux" || - termStr == "cygwin") && - (isatty(fileno(stdout)) != 0); + bool result = (termStr == "xterm" || termStr == "xterm-color" || + termStr == "xterm-256color" || termStr == "screen" || + termStr == "screen-256color" || termStr == "tmux" || + termStr == "tmux-256color" || termStr == "rxvt-unicode" || + termStr == "rxvt-unicode-256color" || termStr == "linux" || + termStr == "cygwin") && + (isatty(fileno(stdout)) != 0); + LOG_F(INFO, "Terminal supports color: {}", result ? "true" : "false"); + return result; #endif } -} // namespace lithium::debug +} // namespace lithium::debug \ No newline at end of file diff --git a/src/debug/console.hpp b/src/debug/console.hpp index acf4cf37..7e8958f8 100644 --- a/src/debug/console.hpp +++ b/src/debug/console.hpp @@ -69,6 +69,14 @@ void showCursor(); */ auto getTerminalSize() -> std::pair; +/** + * @brief Sets the size of the terminal. + * + * @param width The width to set the terminal to. + * @param height The height to set the terminal to. + */ +void setTerminalSize(int width, int height); + /** * @brief Checks if the terminal supports color. *