#pragma once #include "thirdparty/toml.hpp" #include "log.hpp" #include #include #include #include #include #define OPTIONS (*Looper::Options::options) namespace Looper::Options { extern toml::table *options; void save_options(); void load_options(); std::string get_options_path(); inline bool option_set(std::string name) { DEBUG.writefln("Checking if option '%s' is set...", name.c_str()); toml::path path(name); auto *tmp = options; std::vector components; for (auto component : path) { components.push_back((std::string)component); } std::string last_component = components[components.size() - 1]; components.pop_back(); for (auto component : components) { auto &cur_tmp = *tmp; DEBUG.writef(".%s", component.c_str()); if (cur_tmp.contains(component)) { if (!cur_tmp[component].is_table()) { DEBUG.writefln("[invalid]"); return false; } else { tmp = cur_tmp[component].as_table(); } } else { DEBUG.writefln("[Not found, parent]"); return false; } } DEBUG.writef(".%s", last_component.c_str()); if (tmp->contains(last_component)) { DEBUG.writefln("[OK]"); return true; } else { DEBUG.writefln("[Not found, last component]"); return false; } } inline void delete_option(std::string name) { DEBUG.writefln("Deleting option '%s'...", name.c_str()); toml::path path(name); auto *tmp = &OPTIONS; std::vector components; for (auto component : path) { std::string component_str = (std::string)component; components.push_back(component_str); } while (!path.empty()) { if (option_set(path.str())) { toml::path parent_path = path.parent(); auto &parent = OPTIONS.at(parent_path.str()); auto last_component = path[path.size() - 1]; if (parent.is_table()) { auto &table = *parent.as_table(); table.erase((std::string)last_component); if (!table.empty()) { return; } } else if (parent.is_array()) { auto &array = *parent.as_array(); array.erase(array.begin() + last_component.index()); if (!array.empty()) { return; } } path = parent_path; } } } template void set_option(std::string name, T value) { DEBUG.writefln("Setting option '%s'...", name.c_str()); toml::path path(name); auto *tmp = &OPTIONS; std::vector components; for (auto component : path) { std::string component_str = (std::string)component; components.push_back(component_str); } auto last_component = components[components.size() - 1]; components.pop_back(); for (auto component : components) { auto &cur_tmp = *tmp; if (cur_tmp.contains(component)) { if (cur_tmp[component].is_table()) { tmp = cur_tmp[component].as_table(); DEBUG.writef(".%s", component.c_str()); } else { DEBUG.writefln("[invalid]"); return; } } else { tmp->insert_or_assign(component, toml::table()); tmp = cur_tmp[component].as_table(); DEBUG.writef(".%s[new]", component.c_str()); } } DEBUG.writefln(".%s%s", last_component.c_str(), tmp->contains(last_component) ? "[set]" : "[new]"); tmp->insert_or_assign(last_component, value); } template inline void init_option(std::string name, T value) { if (!option_set(name)) { DEBUG.writefln("Initializing option '%s'...", name.c_str()); set_option(name, value); } } template T get_option(std::string name, std::optional initial_value = {}) { if (initial_value.has_value()) { init_option(name, initial_value.value()); } return (**(OPTIONS.at_path(name).as())); } } #define UI_BACKEND() (Looper::Options::get_option("ui.frontend", "imgui"))