extract shared TOML helpers into TomlHelpers.h/.cpp
This commit is contained in:
@@ -13,6 +13,7 @@ SET(HDRS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.h
|
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.h
|
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/TomlHelpers.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ SET(SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/SurfaceMask.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/BlueprintSerializer.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/ShipLayoutBlueprintSerializer.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/TomlHelpers.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "ConfigLoader.h"
|
#include "ConfigLoader.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <sstream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
@@ -14,158 +13,11 @@
|
|||||||
|
|
||||||
#include "Rotation.h"
|
#include "Rotation.h"
|
||||||
#include "ShipLayout.h"
|
#include "ShipLayout.h"
|
||||||
|
#include "TomlHelpers.h"
|
||||||
|
|
||||||
namespace
|
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,
|
std::vector<RecipeOutput> parseRecipeOutputs(const toml::array& arr,
|
||||||
const std::string& file,
|
const std::string& file,
|
||||||
const std::string& path)
|
const std::string& path)
|
||||||
@@ -198,21 +50,6 @@ std::vector<RecipeOutput> parseRecipeOutputs(const toml::array& arr,
|
|||||||
return result;
|
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)
|
Rotation parseRotationString(const std::string& s)
|
||||||
{
|
{
|
||||||
if (s == "east") { return Rotation::East; }
|
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