Program Listing for File settings.cpp

Return to documentation for file (portal/application/settings.cpp)

//
// Copyright © 2025 Jonatan Nevo.
// Distributed under the MIT license (see LICENSE file).
//


#include "portal/application/settings.h"

#include <fmt/ranges.h>
#include <fmt/std.h>

#include "portal/core/files/file_system.h"
#include "portal/serialization/archive/json_archive.h"

namespace portal
{
constexpr auto LOG_LEVEL_ENTRY = "log-level";


ProjectSettings ProjectSettings::create_settings(
    const SettingsArchiveType type,
    const std::filesystem::path& working_directory,
    const std::filesystem::path& settings_file_name
)
{
    if (!FileSystem::exists(working_directory / settings_file_name))
    {
        throw std::runtime_error("Local settings file does not exist");
    }

    const auto local_settings_path = working_directory / settings_file_name;
    const auto mutable_settings_path = FileSystem::get_data_home() / settings_file_name;

    auto output = ProjectSettings(type, local_settings_path);
    if (FileSystem::exists(mutable_settings_path))
    {
        auto user_settings = ProjectSettings(type, mutable_settings_path);
        output.update(user_settings);
        output.settings_path = mutable_settings_path;
    }

    auto log_level_string = output.get_setting<std::string>(LOG_LEVEL_ENTRY);
    if (log_level_string)
    {
        const auto log_level = portal::from_string<Log::LogLevel>(*log_level_string);
        Log::set_default_log_level(log_level);
    }

    output.debug_print();
    return output;
}

ProjectSettings::ProjectSettings(const SettingsArchiveType type, const std::filesystem::path& path) : type(type), settings_path(std::filesystem::absolute(path))
{
    switch (type)
    {
    case SettingsArchiveType::Json:
        root = std::make_unique<JsonArchive>();
        break;
    }

    load();
}

ProjectSettings::~ProjectSettings()
{
    if (root)
        dump();
}

ProjectSettings::ProjectSettings(ProjectSettings&& other) noexcept
    : ArchiveObject(std::move(other)),
      type(other.type),
      settings_path(std::move(other.settings_path)),
      root(std::move(other.root)) {}

ProjectSettings& ProjectSettings::operator=(ProjectSettings&& other) noexcept
{
    std::lock_guard lock_guard(lock);
    std::lock_guard other_lock_guard(other.lock);

    ArchiveObject::operator=(std::move(other));
    type = other.type;
    settings_path = std::move(other.settings_path);
    root = std::move(other.root);

    return *this;
}

void ProjectSettings::load()
{
    switch (type)
    {
    case SettingsArchiveType::Json:
        {
            auto& archive = dynamic_cast<JsonArchive&>(*root);
            archive.read(settings_path);
            update(archive);
            break;
        }
    }
}

void ProjectSettings::dump() const
{
    switch (type)
    {
    case SettingsArchiveType::Json:
        {
            auto& archive = dynamic_cast<JsonArchive&>(*root);
            archive.update(*this);
            archive.dump(settings_path);
            break;
        }
    }
}

void ProjectSettings::debug_print() const
{
    debug_print("", *this);
}

void ProjectSettings::debug_print_scalar(const std::string& name, const reflection::Property& prop) const
{
    switch (prop.type)
    {
    case reflection::PropertyType::binary:
        {
            std::byte data = *prop.value.as<std::byte*>();
            LOG_DEBUG("{}: {}", name, static_cast<uint8_t>(data));
            break;
        }
    case reflection::PropertyType::integer8:
        {
            uint8_t data = *prop.value.as<uint8_t*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::integer16:
        {
            uint16_t data = *prop.value.as<uint16_t*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::integer32:
        {
            uint32_t data = *prop.value.as<uint32_t*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::integer64:
        {
            uint64_t data = *prop.value.as<uint64_t*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::integer128:
        {
            uint128_t data = *prop.value.as<uint128_t*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::floating32:
        {
            float data = *prop.value.as<float*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::floating64:
        {
            double data = *prop.value.as<double*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::character:
        {
            double data = *prop.value.as<double*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::boolean:
        {
            bool data = *prop.value.as<bool*>();
            LOG_DEBUG("{}: {}", name, data);
            break;;
        }
    case reflection::PropertyType::invalid:
        LOG_DEBUG("{}: null", name);
        break;
    case reflection::PropertyType::object:
    case reflection::PropertyType::null_term_string:
    case reflection::PropertyType::string:
        LOG_WARN("{}: not a scalarn", name);
        break;
    }
}


void ProjectSettings::debug_print_array(const std::string& name, const reflection::Property& prop) const
{
    switch (prop.type)
    {
    case reflection::PropertyType::binary:
    case reflection::PropertyType::character:
        {
            llvm::SmallVector<char> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<char>, char>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::integer8:
        {
            llvm::SmallVector<uint8_t> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<uint8_t>, uint8_t>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::integer16:
        {
            llvm::SmallVector<uint16_t> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<uint16_t>, uint16_t>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::integer32:
        {
            llvm::SmallVector<uint32_t> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<uint32_t>, uint32_t>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::integer64:
        {
            llvm::SmallVector<uint64_t> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<uint64_t>, uint64_t>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::integer128:
        {
            llvm::SmallVector<uint128_t> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<uint128_t>, uint128_t>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::floating32:
        {
            llvm::SmallVector<float> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<float>, float>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::floating64:
        {
            llvm::SmallVector<double> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<double>, double>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::boolean:
        {
            llvm::SmallVector<bool> buffer;
            buffer.resize(prop.elements_number);
            format_array<llvm::SmallVector<bool>, bool>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }

    case reflection::PropertyType::null_term_string:
    case reflection::PropertyType::string:
        {
            std::vector<std::string> buffer;
            buffer.resize(prop.elements_number);
            format_array<std::vector<std::string>, std::string>(name, prop, buffer);
            LOG_DEBUG("{}: [{}]", name, fmt::join(buffer, ", "));
            break;
        }
    case reflection::PropertyType::object:
        {
            auto* objects = prop.value.as<ArchiveObject*>();
            for (size_t i = 0; i < prop.elements_number; ++i)
            {
                std::string element_name = fmt::format("{}[{}]", name, i);
                debug_print(element_name, objects[i]);
            }
            break;
        }
    case reflection::PropertyType::invalid:
        break;
    }
}

void ProjectSettings::debug_print(std::string base_name, const ArchiveObject& object) const
{
    for (auto& [key, prop] : object)
    {
        std::string name;
        if (base_name.empty())
            name = key;
        else
            name = fmt::format("{}.{}", base_name, std::string_view(key));

        switch (prop.container_type)
        {
        case reflection::PropertyContainerType::invalid:
            LOG_DEBUG("{}: null", name);
            break;
        case reflection::PropertyContainerType::scalar:
            debug_print_scalar(name, prop);
            break;
        case reflection::PropertyContainerType::null_term_string:
            {
                const size_t string_length = prop.elements_number - 1;;
                const auto* data = prop.value.as<const char*>();
                LOG_DEBUG("{}: {}", name, std::string(data, string_length));
                break;
            }
        case reflection::PropertyContainerType::string:
            {
                const size_t string_length = prop.elements_number;
                const auto* data = prop.value.as<const char*>();
                LOG_DEBUG("{}: {}", name, std::string(data, string_length));
                break;
            }
        case reflection::PropertyContainerType::object:
            {
                debug_print(name, *prop.value.as<ArchiveObject*>());
                break;
            }
        case reflection::PropertyContainerType::array:
            {
                debug_print_array(name, prop);
                break;
            }
        case reflection::PropertyContainerType::vector:
            break;
        case reflection::PropertyContainerType::matrix:
            break;
        }
    }
}

reflection::Property& ProjectSettings::get_property_from_map(const PropertyName name)
{
    std::lock_guard lock_guard(lock);
    return ArchiveObject::get_property_from_map(name);
}

reflection::Property& ProjectSettings::add_property_to_map(const PropertyName name, reflection::Property&& property)
{
    std::lock_guard lock_guard(lock);
    return ArchiveObject::add_property_to_map(name, std::move(property));
}
} // portal