From d664ab54cc15984cfcfd70d1c7ad1d1fb1cbf8a0 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Tue, 4 Aug 2026 18:02:23 +0200 Subject: [PATCH] extract shared TOML helpers into TomlHelpers.h/.cpp --- src/lib/config/CMakeLists.txt | 2 + src/lib/config/ConfigLoader.cpp | 165 +------------------------------ src/lib/config/TomlHelpers.cpp | 167 ++++++++++++++++++++++++++++++++ src/lib/config/TomlHelpers.h | 61 ++++++++++++ 4 files changed, 231 insertions(+), 164 deletions(-) create mode 100644 src/lib/config/TomlHelpers.cpp create mode 100644 src/lib/config/TomlHelpers.h diff --git a/src/lib/config/CMakeLists.txt b/src/lib/config/CMakeLists.txt index d6d29f6..4d00a6d 100644 --- a/src/lib/config/CMakeLists.txt +++ b/src/lib/config/CMakeLists.txt @@ -13,6 +13,7 @@ SET(HDRS ${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.h ${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.h ${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.h + ${CMAKE_CURRENT_SOURCE_DIR}/TomlHelpers.h PARENT_SCOPE ) @@ -23,6 +24,7 @@ SET(SRCS ${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp ${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TomlHelpers.cpp PARENT_SCOPE ) diff --git a/src/lib/config/ConfigLoader.cpp b/src/lib/config/ConfigLoader.cpp index 65da064..5fe5f82 100644 --- a/src/lib/config/ConfigLoader.cpp +++ b/src/lib/config/ConfigLoader.cpp @@ -1,7 +1,6 @@ #include "ConfigLoader.h" #include -#include #include #include #include @@ -14,158 +13,11 @@ #include "Rotation.h" #include "ShipLayout.h" +#include "TomlHelpers.h" namespace { -// --- Error helpers -------------------------------------------------------- - -std::runtime_error makeError(const std::string& file, - const std::string& path, - const std::string& why) -{ - return std::runtime_error("Config: " + file + ": '" + path + "' " + why); -} - -// --- Typed accessors (throw on missing or wrong type) --------------------- - -int64_t requireInt(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - const std::optional value = node.value(); - if (!value) - { - throw makeError(file, path, "missing or not an integer"); - } - return *value; -} - -double requireDouble(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - if (const std::optional v = node.value()) - { - return *v; - } - if (const std::optional v = node.value()) - { - return static_cast(*v); - } - throw makeError(file, path, "missing or not a number"); -} - -std::string requireString(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - const std::optional value = node.value(); - if (!value) - { - throw makeError(file, path, "missing or not a string"); - } - return *value; -} - -bool requireBool(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - const std::optional value = node.value(); - if (!value) - { - throw makeError(file, path, "missing or not a boolean"); - } - return *value; -} - -const toml::array& requireArray(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - const toml::array* arr = node.as_array(); - if (arr == nullptr) - { - throw makeError(file, path, "missing or not an array"); - } - return *arr; -} - -const toml::table& requireTable(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - const toml::table* tbl = node.as_table(); - if (tbl == nullptr) - { - throw makeError(file, path, "missing or not a table"); - } - return *tbl; -} - -Formula requireFormula(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - const std::string source = requireString(node, file, path); - try - { - return Formula::compile(source); - } - catch (const std::exception& e) - { - throw makeError(file, path, std::string("formula error: ") + e.what()); - } -} - -std::vector requireStringArray(const toml::node_view& node, - const std::string& file, - const std::string& path) -{ - const toml::array& arr = requireArray(node, file, path); - std::vector result; - result.reserve(arr.size()); - for (std::size_t i = 0; i < arr.size(); ++i) - { - const std::string elemPath = path + "[" + std::to_string(i) + "]"; - const std::optional s = arr[i].value(); - if (!s) - { - throw makeError(file, elemPath, "not a string"); - } - result.push_back(*s); - } - return result; -} - -std::vector parseIngredients(const toml::array& arr, - const std::string& file, - const std::string& path) -{ - std::vector result; - result.reserve(arr.size()); - for (std::size_t i = 0; i < arr.size(); ++i) - { - const std::string elemPath = path + "[" + std::to_string(i) + "]"; - const toml::table* t = arr[i].as_table(); - if (t == nullptr) - { - throw makeError(file, elemPath, "not a table"); - } - - // We need a mutable node_view to reuse our helpers, which is fine - // because the helpers never mutate. - toml::table& mt = const_cast(*t); - - RecipeIngredient ing; - ing.item = requireString(mt["item"], file, elemPath + ".item"); - ing.amount = static_cast(requireInt(mt["amount"], file, elemPath + ".amount")); - result.push_back(std::move(ing)); - } - return result; -} - std::vector parseRecipeOutputs(const toml::array& arr, const std::string& file, const std::string& path) @@ -198,21 +50,6 @@ std::vector parseRecipeOutputs(const toml::array& arr, return result; } -toml::table parseFile(const std::string& path, const std::string& file) -{ - try - { - return toml::parse_file(path); - } - catch (const toml::parse_error& e) - { - std::ostringstream oss; - oss << "Config: " << file << ": TOML parse error: " << e.description() - << " at " << e.source().begin; - throw std::runtime_error(oss.str()); - } -} - Rotation parseRotationString(const std::string& s) { if (s == "east") { return Rotation::East; } diff --git a/src/lib/config/TomlHelpers.cpp b/src/lib/config/TomlHelpers.cpp new file mode 100644 index 0000000..0a3f3d5 --- /dev/null +++ b/src/lib/config/TomlHelpers.cpp @@ -0,0 +1,167 @@ +#include "TomlHelpers.h" + +#include +#include + +// --- Error helpers -------------------------------------------------------- + +std::runtime_error makeError(const std::string& file, + const std::string& path, + const std::string& why) +{ + return std::runtime_error("Config: " + file + ": '" + path + "' " + why); +} + +// --- Typed accessors (throw on missing or wrong type) --------------------- + +int64_t requireInt(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + const std::optional value = node.value(); + if (!value) + { + throw makeError(file, path, "missing or not an integer"); + } + return *value; +} + +double requireDouble(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + if (const std::optional v = node.value()) + { + return *v; + } + if (const std::optional v = node.value()) + { + return static_cast(*v); + } + throw makeError(file, path, "missing or not a number"); +} + +std::string requireString(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + const std::optional value = node.value(); + if (!value) + { + throw makeError(file, path, "missing or not a string"); + } + return *value; +} + +bool requireBool(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + const std::optional value = node.value(); + if (!value) + { + throw makeError(file, path, "missing or not a boolean"); + } + return *value; +} + +const toml::array& requireArray(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + const toml::array* arr = node.as_array(); + if (arr == nullptr) + { + throw makeError(file, path, "missing or not an array"); + } + return *arr; +} + +const toml::table& requireTable(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + const toml::table* tbl = node.as_table(); + if (tbl == nullptr) + { + throw makeError(file, path, "missing or not a table"); + } + return *tbl; +} + +Formula requireFormula(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + const std::string source = requireString(node, file, path); + try + { + return Formula::compile(source); + } + catch (const std::exception& e) + { + throw makeError(file, path, std::string("formula error: ") + e.what()); + } +} + +std::vector requireStringArray(const toml::node_view& node, + const std::string& file, + const std::string& path) +{ + const toml::array& arr = requireArray(node, file, path); + std::vector result; + result.reserve(arr.size()); + for (std::size_t i = 0; i < arr.size(); ++i) + { + const std::string elemPath = path + "[" + std::to_string(i) + "]"; + const std::optional s = arr[i].value(); + if (!s) + { + throw makeError(file, elemPath, "not a string"); + } + result.push_back(*s); + } + return result; +} + +std::vector parseIngredients(const toml::array& arr, + const std::string& file, + const std::string& path) +{ + std::vector result; + result.reserve(arr.size()); + for (std::size_t i = 0; i < arr.size(); ++i) + { + const std::string elemPath = path + "[" + std::to_string(i) + "]"; + const toml::table* t = arr[i].as_table(); + if (t == nullptr) + { + throw makeError(file, elemPath, "not a table"); + } + + // We need a mutable node_view to reuse our helpers, which is fine + // because the helpers never mutate. + toml::table& mt = const_cast(*t); + + RecipeIngredient ing; + ing.item = requireString(mt["item"], file, elemPath + ".item"); + ing.amount = static_cast(requireInt(mt["amount"], file, elemPath + ".amount")); + result.push_back(std::move(ing)); + } + return result; +} + +toml::table parseFile(const std::string& path, const std::string& file) +{ + try + { + return toml::parse_file(path); + } + catch (const toml::parse_error& e) + { + std::ostringstream oss; + oss << "Config: " << file << ": TOML parse error: " << e.description() + << " at " << e.source().begin; + throw std::runtime_error(oss.str()); + } +} diff --git a/src/lib/config/TomlHelpers.h b/src/lib/config/TomlHelpers.h new file mode 100644 index 0000000..39d3492 --- /dev/null +++ b/src/lib/config/TomlHelpers.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include +#include + +#include "toml.hpp" + +#include "Formula.h" +#include "RecipesConfig.h" // for RecipeIngredient + +// Shared TOML-parsing helpers used by two or more ConfigLoader per-domain +// loaders. Helpers used by exactly one domain stay local to that domain's +// .cpp file instead. + +// --- Error helpers ---------------------------------------------------------- + +std::runtime_error makeError(const std::string& file, + const std::string& path, + const std::string& why); + +// --- Typed accessors (throw on missing or wrong type) ----------------------- + +int64_t requireInt(const toml::node_view& node, + const std::string& file, + const std::string& path); + +double requireDouble(const toml::node_view& node, + const std::string& file, + const std::string& path); + +std::string requireString(const toml::node_view& node, + const std::string& file, + const std::string& path); + +bool requireBool(const toml::node_view& node, + const std::string& file, + const std::string& path); + +const toml::array& requireArray(const toml::node_view& node, + const std::string& file, + const std::string& path); + +const toml::table& requireTable(const toml::node_view& node, + const std::string& file, + const std::string& path); + +Formula requireFormula(const toml::node_view& node, + const std::string& file, + const std::string& path); + +std::vector requireStringArray(const toml::node_view& node, + const std::string& file, + const std::string& path); + +std::vector parseIngredients(const toml::array& arr, + const std::string& file, + const std::string& path); + +toml::table parseFile(const std::string& path, const std::string& file);