173 lines
5.1 KiB
C++
173 lines
5.1 KiB
C++
#include "TomlHelpers.h"
|
|
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
namespace 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());
|
|
}
|
|
}
|
|
|
|
} // namespace utility
|