#include "Config.h"
#include <filesystem>
#include <iostream>
#include <unordered_set>
#include "FatalException.h"
#include "Logging.h"
#include "TomlParseError.h"
namespace objcgen {
toml::Value config;
static void append_to_left(toml::Value& lhs, const toml::Array& rhs)
{
assert(lhs.is<toml::Array>());
for (auto&& item : rhs) {
lhs.push(item);
}
}
static void merge_to_left_array(toml::Value& lhs, const toml::Value& rhs, const std::string& property_name)
{
assert(lhs.is<toml::Table>());
assert(rhs.is<toml::Table>());
auto* lhs_array = lhs.find(property_name);
if (lhs_array && !lhs_array->is<toml::Array>()) {
fatal("TOML property `", property_name, "` should be an array");
}
const auto* rhs_array = rhs.find(property_name);
if (rhs_array && !rhs_array->is<toml::Array>()) {
fatal("TOML property `", property_name, "` should be an array");
}
if (lhs_array) {
if (rhs_array) {
append_to_left(*lhs_array, rhs_array->as<toml::Array>());
}
return;
}
if (rhs_array) {
lhs.set(property_name, *rhs_array);
}
}
static void merge_to_left(toml::Value& lhs, const toml::Table& rhs)
{
merge_to_left_array(lhs, rhs, "packages");
merge_to_left_array(lhs, rhs, "mappings");
}
static toml::Value parse_toml_file(const std::string& path, std::unordered_set<std::string> imported)
{
if (verbosity >= LogLevel::INFO) {
std::cerr << "Reading TOML file `" << path << "`" << std::endl;
}
if (auto [_, new_path] = imported.emplace(path); !new_path) {
fatal("TOML file `", path, "` is recursive");
}
if (!std::filesystem::exists(path)) {
fatal("TOML file `", path, "` doesn't exist");
}
auto parse_result = toml::parseFile(path);
if (!parse_result.valid()) {
throw TomlParseError(parse_result.errorReason);
}
assert(parse_result.value.is<toml::Table>());
if (const auto* imports_any = parse_result.value.find("imports")) {
if (!imports_any->is<toml::Array>()) {
fatal("`imports` in `", path, "` should be a TOML array of strings");
}
std::size_t i = 0;
for (auto&& item_any : imports_any->as<toml::Array>()) {
if (!item_any.is<std::string>()) {
fatal("`imports` in `", path, "` item #", i, " should be a string");
}
const auto& import_path = item_any.as<std::string>();
if (import_path.empty()) {
fatal("`imports` in `", path, "` item #", i, " is empty");
}
auto import_config = parse_toml_file(import_path, imported);
assert(import_config.is<toml::Table>());
if (verbosity >= LogLevel::INFO) {
std::cerr << "Merging TOML file `" << import_path << "` into `" << path << "`" << std::endl;
}
merge_to_left(parse_result.value, import_config.as<toml::Table>());
i++;
}
}
return parse_result.value;
}
void parse_toml_config_file(const std::string& path)
{
config = parse_toml_file(path, {});
}
}