Program Listing for File TOMLUtils.hpp

↰ Return to documentation for file (src/utils/TOMLUtils.hpp)

#ifndef TOMLUTILS_HPP
#define TOMLUTILS_HPP

#include "../pdfs/PDF.hpp"
#include "../pdfs/PDFFileParser.hpp"
#include "../pdfs/PDFSegmentDelta.hpp"
#include "MiscUtils.hpp"
#include <exception>
#include <filesystem>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <toml.hpp>
#include <tuple>
#include <utility>
#include <vector>

namespace utils
{
    inline auto stringArrayContents(const toml::array* arr) -> std::vector<std::string>
    {
        std::vector<std::string> result;
        if (arr != nullptr)
        {
            arr->for_each([&result](auto&& el) -> void {
                if constexpr (toml::is_string<decltype(el)>)
                {
                    result.push_back(std::string(el));
                }
            });
        }
        return result;
    }

    inline auto getStringArrayField(const toml::table& registry, const std::string& key)
    -> std::vector<std::string>
    {
        return stringArrayContents(registry.at_path(key).as_array());
    }

    inline auto initPDFFromKey(const toml::table& inputDeck,
        const std::string& key,
        const std::string& prefix = "") -> pdfs::PDF
    {
        // First check for numerical value
        const std::optional<double> num =
            inputDeck.at_path(key).value<double>();
        if (num.has_value())
        {
            auto delta = std::make_unique<pdfs::PDFSegmentDelta>(num.value());
            auto pdf = pdfs::PDF(std::move(delta));
            return std::move(pdf);
        }

        // Next check for string value
        const std::optional<std::string> pdfFile =
            inputDeck.at_path(key).value<std::string>();
        if (pdfFile.has_value())
        {
            auto pdfFilePath = getFilePath(pdfFile.value(), prefix).string();
            if (pdfFilePath.empty())
            {
                throw std::runtime_error(
                    "initPDFFromKey: pdf file " + pdfFile.value() + " not found");
            }
            auto pdf = pdfs::parsePDFDescriptor(pdfFilePath);
            return std::move(pdf);
        }

        // If we get here, key does not exist or has invalid type
        throw std::runtime_error(
            "initPDFFromKey: invalid entry for " + key);
    }

    inline auto parseTOMLFile(const std::string& fileName, const std::string& context)
    -> std::pair<toml::table, std::filesystem::path>
    {
        auto path = getFilePath(fileName);
        if (path.empty())
        {
            throw std::runtime_error(
                context + ": registry " + fileName + " not found");
        }
        toml::table table;
        try
        {
            table = toml::parse_file(path.string());
        }
        catch (const std::exception&)
        {
            throw std::runtime_error(
                context + ": registry " + path.string() + " is not a valid toml file");
        }
        return { std::move(table), std::move(path) };
    }

    inline auto parseSetRegistry(
        const std::string& registryName,
        const std::string& setsKey,
        const std::string& itemNoun,
        const std::string& context,
        const bool requireFeH = true)
    -> std::tuple<toml::table, std::filesystem::path, std::vector<std::string>>
    {
        auto [registry, registryPath] = parseTOMLFile(registryName, context);

        auto sets = getStringArrayField(registry, setsKey);
        if (sets.empty())
        {
            throw std::runtime_error(
                context + ": registry " + registryPath.string() +
                " does not contain a valid [" + setsKey + "] field");
        }

        for (const auto& s : sets)
        {
            if (!registry.contains(s))
            {
                std::string msg = context + ": registry " + registryPath.string();
                msg += " is missing entry for ";
                msg += itemNoun;
                msg += " ";
                msg += s;
                throw std::runtime_error(msg);
            }
            const auto entry = registry.at_path(s);
            if (!entry.at_path("file") || (requireFeH && !entry.at_path("Fe_H")))
            {
                std::string msg = context + ": registry " + registryPath.string();
                msg += ", entry for ";
                msg += itemNoun;
                msg += " ";
                msg += s;
                msg += requireFeH ?
                    " is missing required 'file' or 'Fe_H' fields" :
                    " is missing required 'file' field";
                throw std::runtime_error(msg);
            }
        }

        return { std::move(registry), std::move(registryPath), std::move(sets) };
    }

} // namespace utils

#endif // TOMLUTILS_HPP