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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHcUerKAZKWNvSKJxYKbnG
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "ConfigLoader.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
@@ -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<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<int64_t> value = node.value<int64_t>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not an integer");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
double requireDouble(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
if (const std::optional<double> v = node.value<double>())
|
||||
{
|
||||
return *v;
|
||||
}
|
||||
if (const std::optional<int64_t> v = node.value<int64_t>())
|
||||
{
|
||||
return static_cast<double>(*v);
|
||||
}
|
||||
throw makeError(file, path, "missing or not a number");
|
||||
}
|
||||
|
||||
std::string requireString(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<std::string> value = node.value<std::string>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not a string");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
bool requireBool(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<bool> value = node.value<bool>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not a boolean");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
const toml::array& requireArray(const toml::node_view<toml::node>& 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<toml::node>& 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<toml::node>& 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<std::string> requireStringArray(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const toml::array& arr = requireArray(node, file, path);
|
||||
std::vector<std::string> 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<std::string> s = arr[i].value<std::string>();
|
||||
if (!s)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a string");
|
||||
}
|
||||
result.push_back(*s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RecipeIngredient> parseIngredients(const toml::array& arr,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
std::vector<RecipeIngredient> 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<toml::table&>(*t);
|
||||
|
||||
RecipeIngredient ing;
|
||||
ing.item = requireString(mt["item"], file, elemPath + ".item");
|
||||
ing.amount = static_cast<int>(requireInt(mt["amount"], file, elemPath + ".amount"));
|
||||
result.push_back(std::move(ing));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RecipeOutput> parseRecipeOutputs(const toml::array& arr,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
@@ -198,21 +50,6 @@ std::vector<RecipeOutput> 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; }
|
||||
|
||||
167
src/lib/config/TomlHelpers.cpp
Normal file
167
src/lib/config/TomlHelpers.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
#include "TomlHelpers.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
// --- 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<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<int64_t> value = node.value<int64_t>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not an integer");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
double requireDouble(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
if (const std::optional<double> v = node.value<double>())
|
||||
{
|
||||
return *v;
|
||||
}
|
||||
if (const std::optional<int64_t> v = node.value<int64_t>())
|
||||
{
|
||||
return static_cast<double>(*v);
|
||||
}
|
||||
throw makeError(file, path, "missing or not a number");
|
||||
}
|
||||
|
||||
std::string requireString(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<std::string> value = node.value<std::string>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not a string");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
bool requireBool(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const std::optional<bool> value = node.value<bool>();
|
||||
if (!value)
|
||||
{
|
||||
throw makeError(file, path, "missing or not a boolean");
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
const toml::array& requireArray(const toml::node_view<toml::node>& 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<toml::node>& 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<toml::node>& 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<std::string> requireStringArray(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
const toml::array& arr = requireArray(node, file, path);
|
||||
std::vector<std::string> 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<std::string> s = arr[i].value<std::string>();
|
||||
if (!s)
|
||||
{
|
||||
throw makeError(file, elemPath, "not a string");
|
||||
}
|
||||
result.push_back(*s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RecipeIngredient> parseIngredients(const toml::array& arr,
|
||||
const std::string& file,
|
||||
const std::string& path)
|
||||
{
|
||||
std::vector<RecipeIngredient> 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<toml::table&>(*t);
|
||||
|
||||
RecipeIngredient ing;
|
||||
ing.item = requireString(mt["item"], file, elemPath + ".item");
|
||||
ing.amount = static_cast<int>(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());
|
||||
}
|
||||
}
|
||||
61
src/lib/config/TomlHelpers.h
Normal file
61
src/lib/config/TomlHelpers.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
double requireDouble(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
std::string requireString(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
bool requireBool(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
const toml::array& requireArray(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
const toml::table& requireTable(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
Formula requireFormula(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
std::vector<std::string> requireStringArray(const toml::node_view<toml::node>& node,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
std::vector<RecipeIngredient> parseIngredients(const toml::array& arr,
|
||||
const std::string& file,
|
||||
const std::string& path);
|
||||
|
||||
toml::table parseFile(const std::string& path, const std::string& file);
|
||||
Reference in New Issue
Block a user