Revamp of the TOML serialization code.
This is a combination of 4 commits. --- This is the 1st commit message: Final attempt to revamp this cursed API This is the commit message #2: --- Simplify util/macros.hpp and rewrite util/toml from scratch This is the commit message #3: --- Very nearly done refactoring TOML serializer; still failing 2-3 unit tests This is the commit message #4: --- Finalize the toml serialization API refactor. Additionally: Standardize the data structures used in Toml unit tests
This commit is contained in:
parent
ec369bdb89
commit
f4f6ea7c81
@ -17,8 +17,6 @@ namespace IOCore {
|
|||||||
|
|
||||||
struct TomlTable : public toml::table {
|
struct TomlTable : public toml::table {
|
||||||
private:
|
private:
|
||||||
using TomlSerializer = IOCore::TOML::Serializer;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TomlTable() = default;
|
TomlTable() = default;
|
||||||
TomlTable(const toml::table& tbl) : toml::table(tbl) {}
|
TomlTable(const toml::table& tbl) : toml::table(tbl) {}
|
||||||
@ -26,8 +24,7 @@ struct TomlTable : public toml::table {
|
|||||||
TomlTable(TomlTable&& tbl) noexcept : toml::table(std::move(tbl)) {}
|
TomlTable(TomlTable&& tbl) noexcept : toml::table(std::move(tbl)) {}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
TomlTable(const T& obj)
|
TomlTable(const T& obj) : toml::table(create_toml<std::decay_t<T>>(obj))
|
||||||
: toml::table(TomlSerializer::to_table<std::decay_t<T>>(obj))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +33,7 @@ struct TomlTable : public toml::table {
|
|||||||
auto operator=(const T& obj) -> TomlTable&
|
auto operator=(const T& obj) -> TomlTable&
|
||||||
{
|
{
|
||||||
using value_t = std::decay_t<T>;
|
using value_t = std::decay_t<T>;
|
||||||
auto value = TomlSerializer::to_table<value_t>(obj);
|
auto value = create_toml(obj);
|
||||||
toml::table::operator=(value);
|
toml::table::operator=(value);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -46,16 +43,16 @@ struct TomlTable : public toml::table {
|
|||||||
auto operator=(T&& obj) -> TomlTable&
|
auto operator=(T&& obj) -> TomlTable&
|
||||||
{
|
{
|
||||||
using value_t = std::decay_t<T>;
|
using value_t = std::decay_t<T>;
|
||||||
auto value = TomlSerializer::to_table<value_t>(obj);
|
to_toml(*this, obj);
|
||||||
toml::table::operator=(std::move(value));
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
auto get() const -> T
|
auto get() const -> T
|
||||||
{
|
{
|
||||||
T retval;
|
using value_t = std::decay_t<T>;
|
||||||
TomlSerializer::from_table<T>(*this, retval);
|
value_t retval;
|
||||||
|
from_toml(*this, retval);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,58 +10,34 @@
|
|||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define TOML_EXPAND(x) x
|
#define MACRO_EXPAND(x) x
|
||||||
#define TOML_ENUM_FIELD(field) { field, #field }
|
#define NAMED_PAIR(field) { #field, field }
|
||||||
|
|
||||||
#define FOREACH_PARAM_1(func, param) func(param)
|
#define FOREACH_PARAM_1(func, param) func(param)
|
||||||
#define FOREACH_PARAM_2(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_1(func, __VA_ARGS__))
|
#define FOREACH_PARAM_2(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_1(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_3(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_2(func, __VA_ARGS__))
|
#define FOREACH_PARAM_3(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_2(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_4(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_3(func, __VA_ARGS__))
|
#define FOREACH_PARAM_4(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_3(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_5(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_4(func, __VA_ARGS__))
|
#define FOREACH_PARAM_5(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_4(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_6(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_5(func, __VA_ARGS__))
|
#define FOREACH_PARAM_6(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_5(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_7(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_6(func, __VA_ARGS__))
|
#define FOREACH_PARAM_7(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_6(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_8(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_7(func, __VA_ARGS__))
|
#define FOREACH_PARAM_8(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_7(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_9(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_8(func, __VA_ARGS__))
|
#define FOREACH_PARAM_9(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_8(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_10(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_9(func, __VA_ARGS__))
|
#define FOREACH_PARAM_10(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_9(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_11(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_10(func, __VA_ARGS__))
|
#define FOREACH_PARAM_11(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_10(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_12(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_11(func, __VA_ARGS__))
|
#define FOREACH_PARAM_12(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_11(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_13(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_12(func, __VA_ARGS__))
|
#define FOREACH_PARAM_13(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_12(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_14(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_13(func, __VA_ARGS__))
|
#define FOREACH_PARAM_14(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_13(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_15(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_14(func, __VA_ARGS__))
|
#define FOREACH_PARAM_15(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_14(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_16(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_15(func, __VA_ARGS__))
|
#define FOREACH_PARAM_16(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_15(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_17(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_16(func, __VA_ARGS__))
|
#define FOREACH_PARAM_17(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_16(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_18(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_17(func, __VA_ARGS__))
|
#define FOREACH_PARAM_18(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_17(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_19(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_18(func, __VA_ARGS__))
|
#define FOREACH_PARAM_19(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_18(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_20(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_19(func, __VA_ARGS__))
|
#define FOREACH_PARAM_20(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_19(func, __VA_ARGS__))
|
||||||
#define FOREACH_PARAM_21(func, param, ...) func(param); TOML_EXPAND(FOREACH_PARAM_20(func, __VA_ARGS__))
|
#define FOREACH_PARAM_21(func, param, ...) func(param) MACRO_EXPAND(FOREACH_PARAM_20(func, __VA_ARGS__))
|
||||||
|
|
||||||
|
|
||||||
#define FOREACH_ENUM_PARAM_1(func, param) func(param)
|
|
||||||
#define FOREACH_ENUM_PARAM_2(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_1(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_3(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_2(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_4(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_3(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_5(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_4(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_6(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_5(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_7(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_6(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_8(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_7(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_9(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_8(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_10(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_9(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_11(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_10(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_12(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_11(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_13(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_12(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_14(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_13(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_15(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_14(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_16(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_15(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_17(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_16(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_18(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_17(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_19(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_18(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_20(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_19(func, __VA_ARGS__))
|
|
||||||
#define FOREACH_ENUM_PARAM_21(func, param, ...) func(param), TOML_EXPAND(FOREACH_ENUM_PARAM_20(func, __VA_ARGS__))
|
|
||||||
|
|
||||||
|
|
||||||
#define GET_FOREACH_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,NAME,...) NAME
|
#define GET_FOREACH_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,NAME,...) NAME
|
||||||
#define FOREACH_PARAM(func, ...) TOML_EXPAND(GET_FOREACH_MACRO(__VA_ARGS__, FOREACH_PARAM_21, FOREACH_PARAM_20, FOREACH_PARAM_19, FOREACH_PARAM_18, FOREACH_PARAM_17, FOREACH_PARAM_16, FOREACH_PARAM_15, FOREACH_PARAM_14, FOREACH_PARAM_13, FOREACH_PARAM_12, FOREACH_PARAM_11, FOREACH_PARAM_10, FOREACH_PARAM_9, FOREACH_PARAM_8, FOREACH_PARAM_7, FOREACH_PARAM_6, FOREACH_PARAM_5, FOREACH_PARAM_4, FOREACH_PARAM_3, FOREACH_PARAM_2, FOREACH_PARAM_1)(func, __VA_ARGS__))
|
#define FOREACH_PARAM(func, ...) MACRO_EXPAND(GET_FOREACH_MACRO(__VA_ARGS__, FOREACH_PARAM_21, FOREACH_PARAM_20, FOREACH_PARAM_19, FOREACH_PARAM_18, FOREACH_PARAM_17, FOREACH_PARAM_16, FOREACH_PARAM_15, FOREACH_PARAM_14, FOREACH_PARAM_13, FOREACH_PARAM_12, FOREACH_PARAM_11, FOREACH_PARAM_10, FOREACH_PARAM_9, FOREACH_PARAM_8, FOREACH_PARAM_7, FOREACH_PARAM_6, FOREACH_PARAM_5, FOREACH_PARAM_4, FOREACH_PARAM_3, FOREACH_PARAM_2, FOREACH_PARAM_1)(func, __VA_ARGS__))
|
||||||
#define FOREACH_ENUM_PARAM(func, ...) TOML_EXPAND(GET_FOREACH_MACRO(__VA_ARGS__, FOREACH_ENUM_PARAM_21, FOREACH_ENUM_PARAM_20, FOREACH_ENUM_PARAM_19, FOREACH_ENUM_PARAM_18, FOREACH_ENUM_PARAM_17, FOREACH_ENUM_PARAM_16, FOREACH_ENUM_PARAM_15, FOREACH_ENUM_PARAM_14, FOREACH_ENUM_PARAM_13, FOREACH_ENUM_PARAM_12, FOREACH_ENUM_PARAM_11, FOREACH_ENUM_PARAM_10, FOREACH_ENUM_PARAM_9, FOREACH_ENUM_PARAM_8, FOREACH_ENUM_PARAM_7, FOREACH_ENUM_PARAM_6, FOREACH_ENUM_PARAM_5, FOREACH_ENUM_PARAM_4, FOREACH_ENUM_PARAM_3, FOREACH_ENUM_PARAM_2, FOREACH_ENUM_PARAM_1)(func, __VA_ARGS__))
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* toml.hpp
|
/* util/toml.hpp
|
||||||
* Copyright © 2024 Saul D. Beniquez
|
* Copyright © 2024 Saul D. Beniquez
|
||||||
* License: Mozilla Public License v. 2.0
|
* License: Mozilla Public License v2.0 (MPL2)
|
||||||
*
|
*
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
* v.2.0. If a copy of the MPL was not distributed with this file, You can
|
* v.2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
@ -9,152 +9,151 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <toml++/toml.hpp>
|
#include <toml++/toml.hpp>
|
||||||
|
|
||||||
#include "../Exception.hpp"
|
#include "../Exception.hpp"
|
||||||
#include "./macros.hpp"
|
#include "macros.hpp"
|
||||||
|
|
||||||
template<typename T>
|
inline namespace TOML {
|
||||||
constexpr bool is_toml_type_t = toml::impl::can_partially_represent_native<T>;
|
|
||||||
|
|
||||||
namespace IOCore {
|
struct TomlException : public IOCore::Exception {
|
||||||
struct TomlException : public Exception {
|
|
||||||
|
|
||||||
TomlException()
|
TomlException()
|
||||||
: Exception("TOML Serialization/Deseralization Exception")
|
: IOCore::Exception("TOML Serialization/Deseralization Exception")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
explicit TomlException(const std::string& message) : Exception(message)
|
explicit TomlException(const std::string& message)
|
||||||
|
: IOCore::Exception(message)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
~TomlException() override = default;
|
~TomlException() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool is_toml_type_t = toml::impl::can_partially_represent_native<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void to_toml(toml::table&, const T&);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void from_toml(const toml::table&, T&);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
auto create_toml(const T& obj) -> toml::table
|
||||||
|
{
|
||||||
|
toml::table tbl;
|
||||||
|
to_toml(tbl, obj);
|
||||||
|
return tbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace IOCore::TOML {
|
template<typename TField>
|
||||||
|
inline void
|
||||||
struct Serializer {
|
add_toml_field(const TField& obj, const char* fieldName, toml::table& tbl)
|
||||||
template<typename TClass>
|
{
|
||||||
static auto to_table(const TClass& obj) -> toml::table // NOLINT
|
using value_type = std::decay_t<TField>;
|
||||||
{
|
|
||||||
using class_t = std::decay_t<TClass>;
|
|
||||||
auto result = class_t::to_toml(obj);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
template<typename TClass>
|
|
||||||
static void from_table(const toml::table& tbl, TClass& result) // NOLINT
|
|
||||||
{
|
|
||||||
using class_t = std::decay_t<TClass>;
|
|
||||||
class_t::from_toml(tbl, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename TField>
|
|
||||||
static void insert_field( // NOLINT
|
|
||||||
toml::table& tbl, const char* fieldName, const TField& obj
|
|
||||||
)
|
|
||||||
{
|
|
||||||
using value_type = std::decay_t<TField>;
|
|
||||||
|
|
||||||
|
if constexpr (std::is_enum_v<value_type>) {
|
||||||
|
add_toml_enum_field(obj, fieldName, tbl);
|
||||||
|
} else if constexpr (std::is_same_v<char, value_type>) {
|
||||||
|
tbl.insert_or_assign(fieldName, static_cast<int>(obj));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
if constexpr (is_toml_type_t<value_type>) {
|
if constexpr (is_toml_type_t<value_type>) {
|
||||||
tbl.insert_or_assign(fieldName, obj);
|
tbl.insert_or_assign(fieldName, obj);
|
||||||
} else {
|
} else {
|
||||||
auto subtable = to_table(obj);
|
toml::table subtable;
|
||||||
|
to_toml(subtable, obj);
|
||||||
tbl.insert_or_assign(fieldName, subtable);
|
tbl.insert_or_assign(fieldName, subtable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<typename TField>
|
}
|
||||||
static void extract_field( // NOLINT
|
template<typename TField>
|
||||||
const toml::table& tbl, const char* fieldName, TField& output
|
inline void
|
||||||
)
|
extract_toml_field(const toml::table& tbl, const char* fieldName, TField& output)
|
||||||
{
|
{
|
||||||
using value_type = std::decay_t<TField>;
|
using value_type = std::decay_t<TField>;
|
||||||
|
|
||||||
if (!tbl.contains(fieldName)) {
|
if (!tbl.contains(fieldName)) {
|
||||||
throw IOCore::TomlException(
|
throw TomlException("Missing field " + std::string(fieldName));
|
||||||
"Missing field " + std::string(fieldName)
|
}
|
||||||
);
|
|
||||||
}
|
if constexpr (std::is_enum_v<value_type>) {
|
||||||
if constexpr (is_toml_type_t<value_type>) {
|
extract_toml_enum_field(tbl, fieldName, output);
|
||||||
output = tbl[fieldName].value<value_type>().value();
|
} else if constexpr (std::is_same_v<char, value_type>) {
|
||||||
} else {
|
output = tbl[fieldName].value<int>().value();
|
||||||
auto subtable = *(tbl[fieldName].as_table());
|
} else if constexpr (is_toml_type_t<value_type>) {
|
||||||
from_table(subtable, output);
|
output = tbl[fieldName].value<value_type>().value();
|
||||||
}
|
} else {
|
||||||
|
auto subtable = *(tbl[fieldName].as_table());
|
||||||
|
from_toml(subtable, output);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define IOCORE_TOML_FIELD(field) \
|
} // namespace TOML
|
||||||
IOCore::TOML::Serializer::insert_field<decltype(obj.field)>( \
|
|
||||||
tbl, #field, obj.field \
|
|
||||||
);
|
|
||||||
#define IOCORE_TOML_EXTRACT_FIELD(field) \
|
|
||||||
IOCore::TOML::Serializer::extract_field<decltype(result.field)>( \
|
|
||||||
tbl, #field, result.field \
|
|
||||||
);
|
|
||||||
|
|
||||||
#define IOCORE_TOML_SERIALIZABLE(CLASS, ...) \
|
#define INSERT_FIELD(FIELD) add_toml_field(obj.FIELD, #FIELD, table);
|
||||||
static constexpr auto _class_name()->const char* \
|
#define EXTRACT_FIELD(FIELD) extract_toml_field(table, #FIELD, obj.FIELD);
|
||||||
|
|
||||||
|
#define ENUM_FIELD_ENTRY(field) NAMED_PAIR(field),
|
||||||
|
|
||||||
|
#define TOML_STRUCT(STRUCT_TYPE, ...) \
|
||||||
|
inline void to_toml(toml::table& table, const STRUCT_TYPE& obj) \
|
||||||
{ \
|
{ \
|
||||||
return #CLASS; \
|
FOREACH_PARAM(INSERT_FIELD, __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static auto to_toml(const CLASS& obj)->toml::table \
|
inline void from_toml(const toml::table& table, STRUCT_TYPE& obj) \
|
||||||
{ \
|
{ \
|
||||||
toml::table tbl; \
|
FOREACH_PARAM(EXTRACT_FIELD, __VA_ARGS__); \
|
||||||
FOREACH_PARAM(IOCORE_TOML_FIELD, __VA_ARGS__) \
|
}
|
||||||
return tbl; \
|
|
||||||
|
#define TOML_CLASS(CLASS_TYPE, ...) \
|
||||||
|
inline friend void to_toml(toml::table& table, const CLASS_TYPE& obj) \
|
||||||
|
{ \
|
||||||
|
FOREACH_PARAM(INSERT_FIELD, __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static void from_toml(const toml::table& tbl, CLASS& result) \
|
inline friend void from_toml(const toml::table& table, CLASS_TYPE& obj) \
|
||||||
{ \
|
{ \
|
||||||
FOREACH_PARAM(IOCORE_TOML_EXTRACT_FIELD, __VA_ARGS__) \
|
FOREACH_PARAM(EXTRACT_FIELD, __VA_ARGS__); \
|
||||||
} \
|
}
|
||||||
friend class IOCore::TOML::Serializer;
|
|
||||||
|
|
||||||
#define IOCORE_TOML_ENUM(ENUM_TYPE, ...) \
|
#define TOML_ENUM(ENUM_TYPE, ...) \
|
||||||
template<> \
|
inline void add_toml_enum_field( \
|
||||||
inline void IOCore::TOML::Serializer::insert_field<ENUM_TYPE>( \
|
const ENUM_TYPE& obj, const char* fieldName, toml::table& tbl \
|
||||||
toml::table & tbl, const char* fieldName, const ENUM_TYPE& obj \
|
|
||||||
) \
|
) \
|
||||||
{ \
|
{ \
|
||||||
constexpr bool enum_check = std::is_enum<ENUM_TYPE>::value; \
|
static_assert( \
|
||||||
static_assert(enum_check, #ENUM_TYPE "must be an enum!"); \
|
std::is_enum<ENUM_TYPE>::value, \
|
||||||
\
|
#ENUM_TYPE "must be an enum!" \
|
||||||
using pair_t = std::pair<ENUM_TYPE, const char*>; \
|
); \
|
||||||
|
using pair_t = std::pair<const char*, ENUM_TYPE>; \
|
||||||
static const pair_t _enum_to_string[] = { \
|
static const pair_t _enum_to_string[] = { \
|
||||||
FOREACH_ENUM_PARAM(TOML_ENUM_FIELD, __VA_ARGS__) \
|
FOREACH_PARAM(ENUM_FIELD_ENTRY, __VA_ARGS__) \
|
||||||
}; \
|
}; \
|
||||||
auto it = std::find_if( \
|
auto it = std::find_if( \
|
||||||
std::begin(_enum_to_string), \
|
std::begin(_enum_to_string), \
|
||||||
std::end(_enum_to_string), \
|
std::end(_enum_to_string), \
|
||||||
[obj](const auto& pair) -> bool { \
|
[obj](const auto& pair) -> bool { \
|
||||||
return pair.first == obj; \
|
return pair.second == obj; \
|
||||||
} \
|
} \
|
||||||
); \
|
); \
|
||||||
tbl.insert_or_assign(fieldName, it->second); \
|
tbl.insert_or_assign(fieldName, it->first); \
|
||||||
} \
|
} \
|
||||||
\
|
inline void extract_toml_enum_field( \
|
||||||
template<> \
|
|
||||||
inline void IOCore::TOML::Serializer::extract_field<ENUM_TYPE>( \
|
|
||||||
const toml::table& tbl, const char* fieldName, ENUM_TYPE& obj \
|
const toml::table& tbl, const char* fieldName, ENUM_TYPE& obj \
|
||||||
) \
|
) \
|
||||||
{ \
|
{ \
|
||||||
constexpr bool enum_check = std::is_enum<ENUM_TYPE>::value; \
|
static_assert( \
|
||||||
static_assert(enum_check, #ENUM_TYPE "must be an enum!"); \
|
std::is_enum<ENUM_TYPE>::value, \
|
||||||
\
|
#ENUM_TYPE " must be an enum!" \
|
||||||
using pair_t = std::pair<ENUM_TYPE, const char*>; \
|
); \
|
||||||
|
using pair_t = std::pair<const char*, ENUM_TYPE>; \
|
||||||
static const pair_t _enum_to_string[] = { \
|
static const pair_t _enum_to_string[] = { \
|
||||||
FOREACH_ENUM_PARAM(TOML_ENUM_FIELD, __VA_ARGS__) \
|
FOREACH_PARAM(ENUM_FIELD_ENTRY, __VA_ARGS__) \
|
||||||
}; \
|
}; \
|
||||||
auto val = tbl[fieldName].value<std::string>().value(); \
|
auto val = tbl[fieldName].value<std::string>().value(); \
|
||||||
for (const auto& [enum_val, str] : _enum_to_string) { \
|
for (const auto& [str, enum_val] : _enum_to_string) { \
|
||||||
if (str == val) { \
|
if (str == val) { \
|
||||||
obj = enum_val; \
|
obj = enum_val; \
|
||||||
break; \
|
break; \
|
||||||
@ -162,16 +161,12 @@ struct Serializer {
|
|||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
#define TOML_SERIALIZE_IMPL(CLASS) \
|
||||||
#define IOCORE_TOML_SERIALIZE_IMPL(CLASS) \
|
template<> \
|
||||||
template<> \
|
void to_toml<CLASS>(toml::table & result, const CLASS& obj)
|
||||||
auto Serializer::to_table<CLASS>(const CLASS& obj) \
|
#define TOML_DESERIALIZE_IMPL(CLASS) \
|
||||||
->toml::table
|
template<> \
|
||||||
|
void from_toml<CLASS>(const toml::table& tbl, CLASS& result)
|
||||||
#define IOCORE_TOML_DESERIALIZE_IMPL(CLASS) \
|
|
||||||
template<> \
|
|
||||||
auto Serializer::from_table<CLASS>(const toml::table& tbl, CLASS& result)\
|
|
||||||
-> void
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// vim: set foldmethod=marker foldmarker=@{,@} foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
||||||
|
155
include/util/toml.old.hpp
Normal file
155
include/util/toml.old.hpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/* toml.hpp
|
||||||
|
* Copyright © 2024 Saul D. Beniquez
|
||||||
|
* License: Mozilla Public License v. 2.0
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
* v.2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
* obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <any>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <typeindex>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <toml++/toml.hpp>
|
||||||
|
|
||||||
|
#include "../Exception.hpp"
|
||||||
|
#include "./macros.hpp"
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool is_toml_type_t = toml::impl::can_partially_represent_native<T>;
|
||||||
|
|
||||||
|
namespace IOCore {
|
||||||
|
struct TomlException : public Exception {
|
||||||
|
|
||||||
|
TomlException()
|
||||||
|
: Exception("TOML Serialization/Deseralization Exception")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit TomlException(const std::string& message) : Exception(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~TomlException() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace TOML {
|
||||||
|
template<typename TClass>
|
||||||
|
struct AdlSerializer {
|
||||||
|
static auto to_toml(const TClass& obj)
|
||||||
|
{
|
||||||
|
using class_t = std::decay_t<TClass>;
|
||||||
|
return class_t::to_toml(obj);
|
||||||
|
}
|
||||||
|
static void from_toml(const toml::table& tbl, TClass& result)
|
||||||
|
{
|
||||||
|
using class_t = std::decay_t<TClass>;
|
||||||
|
class_t::from_toml(tbl, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TField>
|
||||||
|
static void
|
||||||
|
insert_field(toml::table& tbl, const char* fieldName, const TField& obj)
|
||||||
|
{
|
||||||
|
using value_type = std::decay_t<TField>;
|
||||||
|
|
||||||
|
if constexpr (is_toml_type_t<value_type>) {
|
||||||
|
tbl.insert_or_assign(fieldName, obj);
|
||||||
|
} else {
|
||||||
|
if constexpr (std::is_enum_v<value_type>) {
|
||||||
|
AdlSerializer::insert_enum_field<value_type>()
|
||||||
|
} else {
|
||||||
|
auto subtable = AdlSerializer<value_type>::to_toml(obj);
|
||||||
|
tbl.insert_or_assign(fieldName, subtable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<typename TField>
|
||||||
|
static void insert_enum_field(
|
||||||
|
toml::table& tbl, const char* fieldName, TField obj
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
template<typename TField>
|
||||||
|
static void extract_field(
|
||||||
|
const toml::table& tbl, const char* fieldName, TField& output
|
||||||
|
)
|
||||||
|
{
|
||||||
|
using value_type = std::decay_t<TField>;
|
||||||
|
|
||||||
|
if (!tbl.contains(fieldName)) {
|
||||||
|
throw IOCore::TomlException(
|
||||||
|
"Missing field " + std::string(fieldName)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if constexpr (is_toml_type_t<value_type>) {
|
||||||
|
output = tbl[fieldName].value<value_type>().value();
|
||||||
|
} else {
|
||||||
|
auto subtable = *(tbl[fieldName].as_table());
|
||||||
|
AdlSerializer<value_type>::from_toml(subtable, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IOCORE_TOML_FIELD(field) \
|
||||||
|
IOCore::TOML::AdlSerializer<decltype(obj \
|
||||||
|
)>::insert_field<decltype(obj.field)>(tbl, #field, obj.field);
|
||||||
|
#define IOCORE_TOML_EXTRACT_FIELD(field) \
|
||||||
|
IOCore::TOML::AdlSerializer<decltype(result \
|
||||||
|
)>::extract_field<decltype(result.field)>(tbl, #field, result.field);
|
||||||
|
|
||||||
|
#define IOCORE_TOML_SERIALIZABLE(CLASS, ...) \
|
||||||
|
private: \
|
||||||
|
static constexpr auto _class_name()->const char* \
|
||||||
|
{ \
|
||||||
|
return #CLASS; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static auto to_toml(const CLASS& obj)->toml::table \
|
||||||
|
{ \
|
||||||
|
toml::table tbl; \
|
||||||
|
FOREACH_PARAM(IOCORE_TOML_FIELD, __VA_ARGS__) \
|
||||||
|
return tbl; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static void from_toml(const toml::table& tbl, CLASS& result) \
|
||||||
|
{ \
|
||||||
|
FOREACH_PARAM(IOCORE_TOML_EXTRACT_FIELD, __VA_ARGS__) \
|
||||||
|
} \
|
||||||
|
friend IOCore::TOML::AdlSerializer<CLASS>; \
|
||||||
|
;
|
||||||
|
|
||||||
|
#define IOCORE_TOML_ENUM(ENUM_TYPE, ...) \
|
||||||
|
template<typename T> \
|
||||||
|
void IOCore::TOML::AdlSerializer<T>::insert_field( \
|
||||||
|
toml::table& tbl, const char* fieldName, ENUM_TYPE obj \
|
||||||
|
) \
|
||||||
|
{ \
|
||||||
|
; \
|
||||||
|
} \
|
||||||
|
template<typename T> \
|
||||||
|
void IOCore::TOML::AdlSerializer<T>::extract_field( \
|
||||||
|
const toml::table& tbl, const char* fieldName, ENUM_TYPE& output \
|
||||||
|
) \
|
||||||
|
{ \
|
||||||
|
; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IOCORE_TOML_SERIALIZE_IMPL(CLASS) \
|
||||||
|
template<> \
|
||||||
|
auto AdlSerializer<CLASS>::to_table(const CLASS& obj)->toml::table
|
||||||
|
|
||||||
|
#define IOCORE_TOML_DESERIALIZE_IMPL(CLASS) \
|
||||||
|
template<> \
|
||||||
|
void AdlSerializer<CLASS>::from_table( \
|
||||||
|
const toml::table& tbl, CLASS& result \
|
||||||
|
)
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// vim: set foldmethod=marker foldmarker=@{,@} foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
@ -4,7 +4,7 @@ add_library(IOCore OBJECT
|
|||||||
Exception.cpp
|
Exception.cpp
|
||||||
FileResource.cpp
|
FileResource.cpp
|
||||||
debuginfo.cpp
|
debuginfo.cpp
|
||||||
JsonConfigFile.cpp
|
#JsonConfigFile.cpp
|
||||||
TomlConfigFile.cpp
|
TomlConfigFile.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "TomlConfigFile.hpp"
|
#include "TomlConfigFile.hpp"
|
||||||
|
#include "util/toml.hpp"
|
||||||
|
|
||||||
#include "Exception.hpp"
|
#include "Exception.hpp"
|
||||||
#include "sys/debuginfo.hpp"
|
#include "sys/debuginfo.hpp"
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
# Define the executable 'test-runner'
|
# Define the executable 'test-runner'
|
||||||
add_executable(test-runner
|
add_executable(test-runner
|
||||||
runtime-tests.cpp
|
runtime-tests.cpp
|
||||||
|
Exception.test.cpp
|
||||||
Util.macros.test.cpp
|
Util.macros.test.cpp
|
||||||
Util.toml.test.cpp
|
Util.toml.test.cpp
|
||||||
TomlTable.test.cpp
|
TomlTable.test.cpp
|
||||||
Application.test.cpp
|
#Application.test.cpp
|
||||||
FileResource.test.cpp
|
#FileResource.test.cpp
|
||||||
Exception.test.cpp
|
#JsonConfigFile.test.cpp
|
||||||
JsonConfigFile.test.cpp
|
|
||||||
TomlConfigFile.test.cpp
|
TomlConfigFile.test.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "IOCore/util/debug_print.hpp"
|
#include "IOCore/util/debug_print.hpp"
|
||||||
|
|
||||||
#include "test-utils/common.hpp"
|
#include "test-utils/common.hpp"
|
||||||
|
#include "test-utils/serialization.hpp"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -33,45 +34,22 @@ c::string_constant kNonExistantPath = "/hades/tmp/dne/file.toml";
|
|||||||
using TomlTable = IOCore::TomlTable;
|
using TomlTable = IOCore::TomlTable;
|
||||||
using TomlConfigFile = IOCore::TomlConfigFile;
|
using TomlConfigFile = IOCore::TomlConfigFile;
|
||||||
using StringDictionary = IOCore::Dictionary<std::string>;
|
using StringDictionary = IOCore::Dictionary<std::string>;
|
||||||
using Serializer = IOCore::TOML::Serializer;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZE_IMPL(StringDictionary)
|
TOML_SERIALIZE_IMPL(StringDictionary)
|
||||||
{
|
{
|
||||||
toml::table result;
|
|
||||||
for (const auto& [key, value] : obj) {
|
for (const auto& [key, value] : obj) {
|
||||||
result.insert_or_assign(key, value);
|
result.insert_or_assign(key, value);
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
IOCORE_TOML_DESERIALIZE_IMPL(StringDictionary)
|
TOML_DESERIALIZE_IMPL(StringDictionary)
|
||||||
{
|
{
|
||||||
result.clear();
|
result.clear();
|
||||||
for (auto [key, value] : tbl) {
|
for (auto [key, value] : tbl) {
|
||||||
result.emplace(key, value.ref<std::string>());
|
result.emplace(key, value.ref<std::string>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct SimpleStruct {
|
|
||||||
int field1;
|
|
||||||
int field2;
|
|
||||||
|
|
||||||
auto operator==(const SimpleStruct& other) const -> bool
|
|
||||||
{
|
|
||||||
return field1 == other.field1 && field2 == other.field2;
|
|
||||||
}
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(SimpleStruct, field1, field2);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ComplexStruct {
|
|
||||||
SimpleStruct field1;
|
|
||||||
int field2;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(ComplexStruct, field1, field2);
|
|
||||||
};
|
|
||||||
|
|
||||||
BEGIN_TEST_SUITE("elemental::TomlConfigFile")
|
BEGIN_TEST_SUITE("elemental::TomlConfigFile")
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace { // Test fixtures
|
namespace { // Test fixtures
|
||||||
struct SampleFileGenerator {
|
struct SampleFileGenerator {
|
||||||
SampleFileGenerator()
|
SampleFileGenerator()
|
||||||
@ -255,12 +233,24 @@ BEGIN_TEST_SUITE("elemental::TomlConfigFile")
|
|||||||
FIXTURE_TEST("TomlConfigFile works with ComplexStruct ")
|
FIXTURE_TEST("TomlConfigFile works with ComplexStruct ")
|
||||||
{
|
{
|
||||||
auto config_file = TomlConfigFile(kInputFilePath);
|
auto config_file = TomlConfigFile(kInputFilePath);
|
||||||
config_file.set(ComplexStruct{ { 11, 22 }, 30 });
|
config_file.set(ComplexStruct{
|
||||||
|
{ 11, 'c' }, { 30, 0 }, { 1, 2, Red }, Blue, ns::Windowed });
|
||||||
auto toml_data = config_file.getTomlTable();
|
auto toml_data = config_file.getTomlTable();
|
||||||
|
|
||||||
REQUIRE(toml_data["field1"]["field1"].value<int>() == 11);
|
REQUIRE(toml_data["part1"]["field1"].value<int>() == 11);
|
||||||
REQUIRE(toml_data["field1"]["field2"].value<int>() == 22);
|
REQUIRE(toml_data["part1"]["field2"].value<int>() == 'c');
|
||||||
REQUIRE(toml_data["field2"].value<int>() == 30);
|
REQUIRE(toml_data["part2"]["field1"].value<int>() == 30);
|
||||||
|
REQUIRE(toml_data["part2"]["field2"].value<int>() == 0);
|
||||||
|
|
||||||
|
REQUIRE(toml_data["part3"]["field1"].value<int>() == 1);
|
||||||
|
REQUIRE(toml_data["part3"]["field2"].value<int>() == 2);
|
||||||
|
REQUIRE(
|
||||||
|
toml_data["part3"]["foreground"].value<std::string>() ==
|
||||||
|
"Red"
|
||||||
|
);
|
||||||
|
|
||||||
|
REQUIRE(toml_data["background"].value<std::string>() == "Blue");
|
||||||
|
REQUIRE(toml_data["mode"].value<std::string>() == "Windowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -11,81 +11,54 @@
|
|||||||
|
|
||||||
#include "test-utils/common.hpp"
|
#include "test-utils/common.hpp"
|
||||||
|
|
||||||
#include "../include/TomlTable.hpp"
|
#include "IOCore/TomlTable.hpp"
|
||||||
#include "../include/util/toml.hpp"
|
#include "IOCore/util/toml.hpp"
|
||||||
|
|
||||||
enum Colors { Red, Green, Blue };
|
#include "test-utils/serialization.hpp"
|
||||||
IOCORE_TOML_ENUM(Colors, Red, Green, Blue);
|
|
||||||
|
|
||||||
BEGIN_TEST_SUITE("IOCore::TomlTable")
|
BEGIN_TEST_SUITE("IOCore::TomlTable")
|
||||||
{
|
{
|
||||||
using IOCore::TomlTable;
|
using IOCore::TomlTable;
|
||||||
struct SimpleStruct {
|
|
||||||
int field1;
|
|
||||||
int field2;
|
|
||||||
|
|
||||||
auto operator==(const SimpleStruct& other) const -> bool
|
|
||||||
{
|
|
||||||
return field1 == other.field1 && field2 == other.field2;
|
|
||||||
}
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(SimpleStruct, field1, field2);
|
|
||||||
};
|
|
||||||
struct SimpleStruct2 {
|
|
||||||
int field1;
|
|
||||||
int field2;
|
|
||||||
std::string label;
|
|
||||||
|
|
||||||
auto operator==(const SimpleStruct2& other) const -> bool
|
|
||||||
{
|
|
||||||
return label == other.label && field1 == other.field1 &&
|
|
||||||
field2 == other.field2;
|
|
||||||
}
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(SimpleStruct2, field1, field2, label);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ComplexStruct {
|
|
||||||
SimpleStruct field1;
|
|
||||||
int field2;
|
|
||||||
Colors foreground;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(
|
|
||||||
ComplexStruct, field1, field2, foreground
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CASE("IOCore::TomlTable class construction")
|
TEST_CASE("IOCore::TomlTable class construction")
|
||||||
{
|
{
|
||||||
TomlTable table;
|
TomlTable table;
|
||||||
}
|
}
|
||||||
TEST_CASE("IOCore::TomlTable core operators, simple struct")
|
TEST_CASE("IOCore::TomlTable core operators, simple struct")
|
||||||
{
|
{
|
||||||
auto data = SimpleStruct2{ 11, 22, "Hello" };
|
auto data = SimpleClass{ 11, 22 };
|
||||||
TomlTable table = data;
|
TomlTable table = data;
|
||||||
|
|
||||||
CHECK(table.size() == 3);
|
CHECK(table.size() == 2);
|
||||||
|
|
||||||
CHECK(table["field1"].value<int>() == 11);
|
CHECK(table["field1"].value<int>() == 11);
|
||||||
CHECK(table["field2"].value<int>() == 22);
|
CHECK(table["field2"].value<int>() == 22);
|
||||||
REQUIRE(table["label"].value<std::string>() == "Hello");
|
|
||||||
}
|
}
|
||||||
TEST_CASE("IOCore::TomlTable core operators complex struct")
|
TEST_CASE("IOCore::TomlTable core operators complex struct")
|
||||||
{
|
{
|
||||||
auto data = ComplexStruct{ { 11, 22 }, 30, Blue };
|
auto data = ComplexStruct{};
|
||||||
|
data.part1 = { 1, 'a' };
|
||||||
|
data.part2 = { 3, 4 };
|
||||||
|
data.part3 = { 5, 6, Blue };
|
||||||
|
data.background = Red;
|
||||||
|
data.mode = ns::Windowed;
|
||||||
|
|
||||||
TomlTable table = data;
|
TomlTable table = data;
|
||||||
auto newdest = table.as<ComplexStruct>();
|
auto newdest = table.get<ComplexStruct>();
|
||||||
|
|
||||||
CHECK(table.size() == 3);
|
REQUIRE(table.size() == 5);
|
||||||
|
|
||||||
CHECK(table["field1"]["field1"].value<int>() == 11);
|
CHECK(table["part1"]["field1"].value<int>() == 1);
|
||||||
CHECK(table["field1"]["field2"].value<int>() == 22);
|
CHECK(table["part1"]["field2"].value<int>() == 'a');
|
||||||
CHECK(table["field2"].value<int>() == 30);
|
CHECK(table["part2"]["field1"].value<int>() == 3);
|
||||||
CHECK(table["foreground"].value<std::string>() == "Blue");
|
CHECK(table["part2"]["field2"].value<int>() == 4);
|
||||||
|
CHECK(table["part3"]["field1"].value<int>() == 5);
|
||||||
|
CHECK(table["part3"]["field2"].value<int>() == 6);
|
||||||
|
|
||||||
CHECK(newdest.field1 == data.field1);
|
CHECK(
|
||||||
CHECK(newdest.field2 == data.field2);
|
table["part3"]["foreground"].value<std::string>() == "Blue"
|
||||||
CHECK(newdest.foreground == data.foreground);
|
);
|
||||||
|
CHECK(table["background"].value<std::string>() == "Red");
|
||||||
|
CHECK(table["mode"].value<std::string>() == "Windowed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
#include "test-utils/common.hpp"
|
#include "test-utils/common.hpp"
|
||||||
|
|
||||||
#include "../include/util/macros.hpp"
|
#include "../include/util/macros.hpp"
|
||||||
#include "../include/util/toml.hpp"
|
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -20,6 +19,8 @@
|
|||||||
|
|
||||||
enum Colors { Red, Green, Blue };
|
enum Colors { Red, Green, Blue };
|
||||||
|
|
||||||
|
#define NAME_ENTRY(field) NAMED_PAIR(field),
|
||||||
|
|
||||||
BEGIN_TEST_SUITE("Util.Macros")
|
BEGIN_TEST_SUITE("Util.Macros")
|
||||||
{
|
{
|
||||||
TEST_CASE("FOREACH_PARAM Macro works")
|
TEST_CASE("FOREACH_PARAM Macro works")
|
||||||
@ -34,14 +35,14 @@ BEGIN_TEST_SUITE("Util.Macros")
|
|||||||
|
|
||||||
TEST_CASE("FOREACH_ENUM_PARAM Macro works")
|
TEST_CASE("FOREACH_ENUM_PARAM Macro works")
|
||||||
{
|
{
|
||||||
std::pair<Colors, const std::string> color_pairs[] = {
|
std::pair<const std::string, Colors> color_pairs[] = {
|
||||||
FOREACH_ENUM_PARAM(TOML_ENUM_FIELD, Red, Green, Blue)
|
FOREACH_PARAM(NAME_ENTRY, Red, Green, Blue)
|
||||||
};
|
};
|
||||||
|
|
||||||
CHECK(std::size(color_pairs) == 3);
|
CHECK(std::size(color_pairs) == 3);
|
||||||
REQUIRE(color_pairs[0].second == "Red");
|
REQUIRE(color_pairs[0].first == "Red");
|
||||||
REQUIRE(color_pairs[1].second == "Green");
|
REQUIRE(color_pairs[1].first == "Green");
|
||||||
REQUIRE(color_pairs[2].second == "Blue");
|
REQUIRE(color_pairs[2].first == "Blue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,160 +7,109 @@
|
|||||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
* obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <toml++/toml.h>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "test-utils/common.hpp"
|
#include "test-utils/common.hpp"
|
||||||
|
#include "test-utils/serialization.hpp"
|
||||||
|
|
||||||
#include "../include/util/toml.hpp"
|
#include "IOCore/util/toml.hpp"
|
||||||
|
|
||||||
enum Colors { Red, Green, Blue };
|
|
||||||
IOCORE_TOML_ENUM(Colors, Red, Green, Blue);
|
|
||||||
|
|
||||||
BEGIN_TEST_SUITE("Util.Toml")
|
BEGIN_TEST_SUITE("Util.Toml")
|
||||||
{
|
{
|
||||||
struct SimpleStruct {
|
TEST_CASE("SimpleStruct Serializes")
|
||||||
int field1;
|
|
||||||
int field2;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(SimpleStruct, field1, field2);
|
|
||||||
};
|
|
||||||
struct SimpleStruct2 {
|
|
||||||
int fieldA;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(SimpleStruct2, fieldA);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StructWithEnum {
|
|
||||||
int field1;
|
|
||||||
int field2;
|
|
||||||
Colors foreground;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(
|
|
||||||
StructWithEnum, field1, field2, foreground
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompositeStruct {
|
|
||||||
SimpleStruct part1;
|
|
||||||
SimpleStruct2 part2;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(CompositeStruct, part1, part2);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ComplexStruct {
|
|
||||||
SimpleStruct part1;
|
|
||||||
SimpleStruct2 part2;
|
|
||||||
StructWithEnum part3;
|
|
||||||
Colors background;
|
|
||||||
|
|
||||||
IOCORE_TOML_SERIALIZABLE(
|
|
||||||
ComplexStruct, part1, part2, part3, background
|
|
||||||
);
|
|
||||||
};
|
|
||||||
TEST_CASE("IOCORE_TOML_TO Macro works")
|
|
||||||
{
|
{
|
||||||
toml::table tbl;
|
SimpleStruct data{ 1 };
|
||||||
SimpleStruct obj;
|
toml::table result;
|
||||||
|
SimpleStruct deserialized;
|
||||||
|
|
||||||
IOCORE_TOML_FIELD(field1);
|
to_toml(result, data);
|
||||||
IOCORE_TOML_FIELD(field2);
|
from_toml(result, deserialized);
|
||||||
REQUIRE(tbl.size() == 2);
|
|
||||||
}
|
|
||||||
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with SimpleStruct")
|
|
||||||
{
|
|
||||||
|
|
||||||
toml::table table;
|
REQUIRE(result.size() == 2);
|
||||||
SimpleStruct data = { 10, 20 };
|
CHECK(result.at("field1").value<int>() == 1);
|
||||||
SimpleStruct newdest;
|
CHECK(deserialized.field1 == 1);
|
||||||
|
CHECK(deserialized.field2 == 0);
|
||||||
table = SimpleStruct::to_toml(data);
|
|
||||||
SimpleStruct::from_toml(table, newdest);
|
|
||||||
|
|
||||||
CHECK(table.size() == 2);
|
|
||||||
|
|
||||||
CHECK(table["field1"].value<int>() == 10);
|
|
||||||
CHECK(table["field2"].value<int>() == 20);
|
|
||||||
|
|
||||||
CHECK(newdest.field1 == data.field1);
|
|
||||||
CHECK(newdest.field2 == data.field2);
|
|
||||||
}
|
|
||||||
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with StructWithEnum")
|
|
||||||
{
|
|
||||||
|
|
||||||
toml::table table;
|
|
||||||
StructWithEnum data = { 11, 22, Green };
|
|
||||||
StructWithEnum newdest;
|
|
||||||
|
|
||||||
table = StructWithEnum::to_toml(data);
|
|
||||||
StructWithEnum::from_toml(table, newdest);
|
|
||||||
|
|
||||||
CHECK(table.size() == 3);
|
|
||||||
|
|
||||||
CHECK(table["field1"].value<int>() == 11);
|
|
||||||
CHECK(table["field2"].value<int>() == 22);
|
|
||||||
CHECK(table["foreground"].value<std::string>() == "Green");
|
|
||||||
|
|
||||||
CHECK(newdest.field1 == data.field1);
|
|
||||||
CHECK(newdest.field2 == data.field2);
|
|
||||||
CHECK(newdest.foreground == data.foreground);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with CompositeStruct")
|
TEST_CASE("SimpleClass Serializes")
|
||||||
{
|
{
|
||||||
|
SimpleClass data{ 1, 22 };
|
||||||
|
toml::table result;
|
||||||
|
SimpleClass deserialized;
|
||||||
|
|
||||||
toml::table table;
|
to_toml(result, data);
|
||||||
CompositeStruct data = { { 10, 20 }, { 30 } };
|
from_toml(result, deserialized);
|
||||||
CompositeStruct newdest;
|
|
||||||
|
|
||||||
table = CompositeStruct::to_toml(data);
|
REQUIRE(result.size() == 2);
|
||||||
CompositeStruct::from_toml(table, newdest);
|
CHECK(result.at("field1").value<int>() == 1);
|
||||||
|
CHECK(result.at("field2").value<int>() == 22);
|
||||||
|
|
||||||
CHECK(table.size() == 2);
|
CHECK(deserialized.field1 == 1);
|
||||||
|
CHECK(deserialized.field2 == 22);
|
||||||
CHECK(table["part1"].as_table()->size() == 2);
|
|
||||||
CHECK(table["part2"].as_table()->size() == 1);
|
|
||||||
|
|
||||||
CHECK(table["part1"]["field1"].value<int>() == 10);
|
|
||||||
CHECK(table["part1"]["field2"].value<int>() == 20);
|
|
||||||
CHECK(table["part2"]["fieldA"].value<int>() == 30);
|
|
||||||
|
|
||||||
CHECK(newdest.part1.field1 == data.part1.field1);
|
|
||||||
CHECK(newdest.part1.field2 == data.part1.field2);
|
|
||||||
CHECK(newdest.part2.fieldA == data.part2.fieldA);
|
|
||||||
}
|
}
|
||||||
TEST_CASE("IOCORE_TOML_SERIALIZABLE Macro works with ComplexStruct")
|
TEST_CASE("StructWithEnum Serializes")
|
||||||
{
|
{
|
||||||
toml::table table;
|
StructWithEnum data{ 1, 2, Colors::Green };
|
||||||
ComplexStruct data = {
|
toml::table result;
|
||||||
{ 10, 20 }, { 30 }, { 40, 50, Blue }, Red
|
StructWithEnum deserialized;
|
||||||
|
|
||||||
|
to_toml(result, data);
|
||||||
|
from_toml(result, deserialized);
|
||||||
|
|
||||||
|
REQUIRE(result.size() == 3);
|
||||||
|
CHECK(result.at("field1").value<int>() == 1);
|
||||||
|
CHECK(result.at("field2").value<int>() == 2);
|
||||||
|
CHECK(result.at("foreground").value<std::string>() == "Green");
|
||||||
|
|
||||||
|
CHECK(deserialized.field1 == data.field1);
|
||||||
|
CHECK(deserialized.field2 == data.field2);
|
||||||
|
CHECK(deserialized.foreground == data.foreground);
|
||||||
|
}
|
||||||
|
TEST_CASE("CompositeStruct Serializes")
|
||||||
|
{
|
||||||
|
CompositeStruct data{ { 1, 'c' }, { 3, 4 } };
|
||||||
|
toml::table result;
|
||||||
|
CompositeStruct deserialized;
|
||||||
|
|
||||||
|
to_toml(result, data);
|
||||||
|
from_toml(result, deserialized);
|
||||||
|
|
||||||
|
REQUIRE(result.size() == 2);
|
||||||
|
REQUIRE(result.at("part1").as_table()->size() == 2);
|
||||||
|
REQUIRE(result.at("part2").as_table()->size() == 2);
|
||||||
|
|
||||||
|
CHECK(deserialized.part1.field1 == data.part1.field1);
|
||||||
|
CHECK(deserialized.part1.field2 == data.part1.field2);
|
||||||
|
CHECK(deserialized.part2.field1 == data.part2.field1);
|
||||||
|
CHECK(deserialized.part2.field2 == data.part2.field2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComplexStruct Serializes")
|
||||||
|
{
|
||||||
|
ComplexStruct data{
|
||||||
|
{ 1, 'c' }, { 3, 4 }, { 5, 6, Colors::Red }, Colors::Blue
|
||||||
};
|
};
|
||||||
ComplexStruct newdest;
|
toml::table result;
|
||||||
|
ComplexStruct deserialized;
|
||||||
|
|
||||||
table = ComplexStruct::to_toml(data);
|
to_toml(result, data);
|
||||||
ComplexStruct::from_toml(table, newdest);
|
from_toml(result, deserialized);
|
||||||
|
|
||||||
CHECK(table.size() == 4);
|
REQUIRE(result.size() == 5);
|
||||||
|
REQUIRE(result.at("part1").as_table()->size() == 2);
|
||||||
|
REQUIRE(result.at("part2").as_table()->size() == 2);
|
||||||
|
REQUIRE(result.at("part3").as_table()->size() == 3);
|
||||||
|
|
||||||
CHECK(table["part1"].as_table()->size() == 2);
|
CHECK(deserialized.part1.field1 == data.part1.field1);
|
||||||
CHECK(table["part2"].as_table()->size() == 1);
|
CHECK(deserialized.part1.field2 == data.part1.field2);
|
||||||
CHECK(table["part3"].as_table()->size() == 3);
|
CHECK(deserialized.part2.field1 == data.part2.field1);
|
||||||
|
CHECK(deserialized.part2.field2 == data.part2.field2);
|
||||||
CHECK(table["part1"]["field1"].value<int>() == 10);
|
CHECK(deserialized.part3.field1 == data.part3.field1);
|
||||||
CHECK(table["part1"]["field2"].value<int>() == 20);
|
CHECK(deserialized.part3.field2 == data.part3.field2);
|
||||||
CHECK(table["part2"]["fieldA"].value<int>() == 30);
|
CHECK(deserialized.part3.foreground == data.part3.foreground);
|
||||||
CHECK(table["part3"]["field1"].value<int>() == 40);
|
CHECK(deserialized.background == data.background);
|
||||||
CHECK(table["part3"]["field2"].value<int>() == 50);
|
CHECK(deserialized.mode == data.mode);
|
||||||
CHECK(
|
|
||||||
table["part3"]["foreground"].value<std::string>() == "Blue"
|
|
||||||
);
|
|
||||||
|
|
||||||
CHECK(newdest.part1.field1 == data.part1.field1);
|
|
||||||
CHECK(newdest.part1.field2 == data.part1.field2);
|
|
||||||
CHECK(newdest.part2.fieldA == data.part2.fieldA);
|
|
||||||
CHECK(newdest.part3.field1 == data.part3.field1);
|
|
||||||
CHECK(newdest.part3.field2 == data.part3.field2);
|
|
||||||
CHECK(newdest.part3.foreground == data.part3.foreground);
|
|
||||||
CHECK(newdest.background == data.background);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
88
tests/test-utils/serialization.hpp
Normal file
88
tests/test-utils/serialization.hpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/* test-util/serialization.hpp
|
||||||
|
* Copyright © 2024 Saul D. Beniquez
|
||||||
|
* License: Mozilla Public License v. 2.0
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||||
|
* v.2.0. If a copy of the MPL was not distributed with this file, You can
|
||||||
|
* obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IOCore/util/toml.hpp"
|
||||||
|
|
||||||
|
inline namespace util {
|
||||||
|
inline namespace serialization {
|
||||||
|
|
||||||
|
enum Colors { Red, Green, Blue };
|
||||||
|
TOML_ENUM(Colors, Red, Green, Blue);
|
||||||
|
|
||||||
|
namespace ns {
|
||||||
|
enum WindowMode { Windowed, Fullscreen, Borderless };
|
||||||
|
TOML_ENUM(WindowMode, Windowed, Fullscreen, Borderless);
|
||||||
|
} // namespace ns
|
||||||
|
|
||||||
|
struct SimpleStruct {
|
||||||
|
int field1;
|
||||||
|
char field2;
|
||||||
|
|
||||||
|
auto operator==(const SimpleStruct& other) const -> bool
|
||||||
|
{
|
||||||
|
return field1 == other.field1 && field2 == other.field2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TOML_STRUCT(SimpleStruct, field1, field2);
|
||||||
|
|
||||||
|
class SimpleClass {
|
||||||
|
public:
|
||||||
|
int field1;
|
||||||
|
int field2;
|
||||||
|
|
||||||
|
auto operator==(const SimpleClass& other) const -> bool
|
||||||
|
{
|
||||||
|
return field1 == other.field1 && field2 == other.field2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TOML_CLASS(SimpleClass, field1, field2);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StructWithEnum {
|
||||||
|
int field1;
|
||||||
|
int field2;
|
||||||
|
Colors foreground;
|
||||||
|
|
||||||
|
auto operator==(const StructWithEnum& other) const -> bool
|
||||||
|
{
|
||||||
|
return field1 == other.field1 && field2 == other.field2 &&
|
||||||
|
foreground == other.foreground;
|
||||||
|
}
|
||||||
|
TOML_CLASS(StructWithEnum, field1, field2, foreground);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompositeStruct {
|
||||||
|
SimpleStruct part1;
|
||||||
|
SimpleClass part2;
|
||||||
|
|
||||||
|
auto operator==(const CompositeStruct& other) const -> bool
|
||||||
|
{
|
||||||
|
return part1 == other.part1 && part2 == other.part2;
|
||||||
|
}
|
||||||
|
TOML_CLASS(CompositeStruct, part1, part2);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ComplexStruct {
|
||||||
|
SimpleStruct part1;
|
||||||
|
SimpleClass part2;
|
||||||
|
StructWithEnum part3;
|
||||||
|
Colors background;
|
||||||
|
ns::WindowMode mode;
|
||||||
|
|
||||||
|
TOML_CLASS(ComplexStruct, part1, part2, part3, background, mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace serialization
|
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
Loading…
Reference in New Issue
Block a user