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