extract shared TOML helpers into TomlHelpers.h/.cpp

This commit is contained in:
2026-08-04 18:02:23 +02:00
parent 906000b0e9
commit d664ab54cc
4 changed files with 231 additions and 164 deletions

View 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());
}
}