From e702c0100541f88a48bb386b743910b66ba0295c Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Mon, 3 Aug 2026 22:22:48 +0200 Subject: [PATCH] extract shared TOML helpers into TomlHelpers.h/.cpp ConfigLoader.cpp had grown to 877 lines by mixing generic TOML-parsing helpers (used by every per-file loader) with per-domain parsing logic. Splitting the file per config domain first requires pulling out the helpers shared by two or more domains, so each domain .cpp can include them without duplication. Pure move: no logic, message, or ordering changes. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG --- 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);