[WIP] Add TOML Serialization support for aarbitrary game data.
- Now partly using the new TOML serialization feature from IOCore - Remove JsonConfigFile class and move it to IOCore
This commit is contained in:
parent
ebfc03b968
commit
091ebd7afe
@ -11,7 +11,6 @@ PRIVATE
|
||||
target_link_libraries(phong PRIVATE
|
||||
elemental
|
||||
IOCore
|
||||
nlohmann_json
|
||||
)
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "types/rendering.hpp"
|
||||
|
||||
#include "IOCore/util/serialization.hpp"
|
||||
#include "IOCore/util/toml.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@ -20,8 +21,7 @@ namespace elemental {
|
||||
struct GameSettings {
|
||||
RendererSettings renderer_settings;
|
||||
|
||||
// DEFINE_TOML_FIELDS(renderer_settings);
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GameSettings, renderer_settings);
|
||||
JSON_SERIALIZABLE(GameSettings, renderer_settings);
|
||||
};
|
||||
|
||||
} // namespace elemental
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "util/debug.hpp"
|
||||
|
||||
#include "IOCore/Exception.hpp"
|
||||
#include "JsonConfigFile.hpp"
|
||||
#include "LoopRegulator.hpp"
|
||||
#include "SdlEventSource.hpp"
|
||||
#include "SdlRenderer.hpp"
|
||||
|
@ -14,7 +14,9 @@
|
||||
#include "IObserver.hpp"
|
||||
|
||||
#include "IOCore/Application.hpp"
|
||||
#include "JsonConfigFile.hpp"
|
||||
#include "IOCore/JsonConfigFile.hpp"
|
||||
|
||||
// #include "JsonConfigFile.hpp"
|
||||
#include "LoopRegulator.hpp"
|
||||
#include "Observable.hpp"
|
||||
#include "Singleton.hpp"
|
||||
@ -65,7 +67,7 @@ class Phong
|
||||
SdlEventSource& event_emitter;
|
||||
|
||||
GameSettings settings;
|
||||
configuration::JsonConfigFile settings_file;
|
||||
IOCore::JsonConfigFile settings_file;
|
||||
};
|
||||
|
||||
} // namespace elemental
|
||||
|
6
Doxyfile
6
Doxyfile
@ -914,9 +914,9 @@ WARN_LOGFILE =
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = src/ \
|
||||
include/ \
|
||||
demo/
|
||||
INPUT = Modules/elemental/ \
|
||||
Apps/ \
|
||||
Tests
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
@ -1,6 +1,5 @@
|
||||
add_library(elemental
|
||||
OBJECT
|
||||
JsonConfigFile.cpp
|
||||
LoopRegulator.cpp
|
||||
Observable.cpp
|
||||
SdlRenderer.cpp
|
||||
|
@ -1,55 +0,0 @@
|
||||
/* JsonConfigFile.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 "types.hpp"
|
||||
|
||||
#include "IOCore/FileResource.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace elemental::configuration {
|
||||
|
||||
using IOCore::CreateDirs;
|
||||
using IOCore::FileResource;
|
||||
|
||||
class JsonConfigFile : public FileResource {
|
||||
public:
|
||||
JsonConfigFile(
|
||||
const std::filesystem::path& file_path,
|
||||
CreateDirs mode = CreateDirs::Disable
|
||||
);
|
||||
~JsonConfigFile() override;
|
||||
|
||||
auto read() -> nlohmann::json&;
|
||||
void write();
|
||||
|
||||
template<typename TData>
|
||||
[[nodiscard]] auto get() const -> TData
|
||||
{
|
||||
return config_json.get<TData>();
|
||||
}
|
||||
|
||||
template<typename T_>
|
||||
void set(const T_& value)
|
||||
{
|
||||
config_json = value;
|
||||
}
|
||||
|
||||
auto jsonDataRef() -> nlohmann::json& { return this->config_json; };
|
||||
|
||||
protected:
|
||||
nlohmann::json config_json;
|
||||
};
|
||||
} // namespace elemental::configuration
|
||||
|
||||
// clang-format off
|
||||
// vim: set foldmethod=syntax foldlevel=1 foldminlines=12 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
@ -7,18 +7,17 @@
|
||||
* obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_image.h>
|
||||
|
||||
#include "IOCore/Exception.hpp"
|
||||
#include "IRenderer.hpp"
|
||||
#include "SdlRenderer.hpp"
|
||||
|
||||
#include "util/debug.hpp"
|
||||
|
||||
#include "types/input.hpp"
|
||||
#include "types/rendering.hpp"
|
||||
#include "util/debug.hpp"
|
||||
|
||||
#include <IOCore/Exception.hpp>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_image.h>
|
||||
#include <fmt/core.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <memory>
|
||||
@ -53,10 +52,11 @@ void SdlRenderer::init(RendererSettings& settings)
|
||||
IMG_Init(
|
||||
IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF | IMG_INIT_WEBP
|
||||
)) {
|
||||
error_buffer.str("");
|
||||
error_buffer << "Could not initialize SDL_Image: IMG_INIT() == 0"
|
||||
<< std::flush;
|
||||
throw IOCore::Exception(error_buffer.str());
|
||||
HANDLE_SDL_ERROR(fmt::format(
|
||||
"Could not initialize SDL_Image: {}",
|
||||
IMG_GetError()
|
||||
)
|
||||
.c_str());
|
||||
}
|
||||
int window_xpos, window_ypos, window_width, window_height, res_width,
|
||||
res_height;
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/serialization.hpp"
|
||||
#include "IOCore/util/serialization.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@ -18,13 +18,15 @@ namespace elemental {
|
||||
|
||||
struct Point {
|
||||
uint32_t x, y;
|
||||
SERIALIZABLE(Point, x, y);
|
||||
JSON_SERIALIZABLE(Point, x, y);
|
||||
TOML_SERIALIZABLE(Point, x, y);
|
||||
};
|
||||
using Position2D = Point;
|
||||
|
||||
struct Area {
|
||||
uint32_t width, height;
|
||||
SERIALIZABLE(Area, width, height);
|
||||
JSON_SERIALIZABLE(Area, width, height);
|
||||
TOML_SERIALIZABLE(Area, width, height);
|
||||
};
|
||||
using Resolution = Area;
|
||||
|
||||
@ -38,7 +40,8 @@ struct Rectangle {
|
||||
uint32_t& width = size.width;
|
||||
uint32_t& height = size.height;
|
||||
|
||||
SERIALIZABLE(Rectangle, position, size);
|
||||
JSON_SERIALIZABLE(Rectangle, position, size);
|
||||
// TOML_SERIALIZABLE(Rectangle, position);
|
||||
};
|
||||
|
||||
enum class WindowMode {
|
||||
@ -46,23 +49,12 @@ enum class WindowMode {
|
||||
Borderless = 0x01,
|
||||
Fullscreen = 0x11,
|
||||
};
|
||||
// NOLINTNEXTLINE(readability-identifier-length)
|
||||
SERIALIZABLE_ENUM(
|
||||
WindowMode, { { WindowMode::Windowed, "windowed" },
|
||||
{ WindowMode::Borderless, "borderless" },
|
||||
{ WindowMode::Fullscreen, "fullscreen" } }
|
||||
);
|
||||
enum class WindowPlacement : int
|
||||
|
||||
{
|
||||
Manual = 0x00,
|
||||
Centered = 0x01
|
||||
};
|
||||
// NOLINTNEXTLINE(readability-identifier-length)
|
||||
SERIALIZABLE_ENUM(
|
||||
WindowPlacement, { { WindowPlacement::Manual, "manual" },
|
||||
{ WindowPlacement::Centered, "centered" } }
|
||||
JSON_SERIALIZABLE_ENUM( // NOLINT(readability-identifier-length)
|
||||
WindowMode, { { WindowMode::Windowed, "Windowed" },
|
||||
{ WindowMode::Borderless, "Borderless" },
|
||||
{ WindowMode::Fullscreen, "Fullscreen" } }
|
||||
);
|
||||
enum class WindowPlacement : int { Manual = 0x00, Centered = 0x01 };
|
||||
|
||||
struct WindowParameters {
|
||||
std::string title;
|
||||
@ -71,14 +63,17 @@ struct WindowParameters {
|
||||
Position2D position;
|
||||
Area size;
|
||||
|
||||
SERIALIZABLE(WindowParameters, title, mode, placement, position, size);
|
||||
JSON_SERIALIZABLE(
|
||||
WindowParameters, title, mode, placement, position, size
|
||||
);
|
||||
};
|
||||
|
||||
struct RendererSettings {
|
||||
WindowParameters window;
|
||||
Resolution resolution;
|
||||
int field1;
|
||||
|
||||
SERIALIZABLE(RendererSettings, window, resolution);
|
||||
JSON_SERIALIZABLE(RendererSettings, window, resolution, field1);
|
||||
};
|
||||
|
||||
} // namespace elemental
|
||||
|
@ -1,21 +0,0 @@
|
||||
/* 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 <nlohmann/json.hpp>
|
||||
|
||||
/* NOLINTBEGIN(readability-identifier-length) */
|
||||
|
||||
#define SERIALIZABLE(...) NLOHMANN_DEFINE_TYPE_INTRUSIVE(__VA_ARGS__)
|
||||
#define SERIALIZABLE_ENUM(...) NLOHMANN_JSON_SERIALIZE_ENUM(__VA_ARGS__)
|
||||
|
||||
/* NOLINTEND */
|
||||
|
||||
// clang-format off
|
||||
// vim: set foldmethod=syntax foldminlines=10 textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
@ -8,9 +8,7 @@ add_executable(test-runner
|
||||
Observable.test.cpp
|
||||
sdl/SdlEventSource.test.cpp
|
||||
SDL_Memory.test.cpp
|
||||
JsonConfigFile.test.cpp
|
||||
paths.test.cpp
|
||||
|
||||
)
|
||||
|
||||
set_target_properties(test-runner
|
||||
|
@ -1,212 +0,0 @@
|
||||
/* JsonConfigFile.test.cpp
|
||||
* }
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#include "JsonConfigFile.hpp"
|
||||
#include "IOCore/Exception.hpp"
|
||||
|
||||
#include "sys/debuginfo.hpp"
|
||||
#include "util/debug.hpp"
|
||||
|
||||
#include "test-utils/common.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace elemental;
|
||||
using namespace elemental::configuration;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
constexpr c::const_string kINPUT_FILE_PATH = "/tmp/test_config.json";
|
||||
constexpr c::const_string kBADFILE_PATH = "/tmp/test_bad_config.json";
|
||||
constexpr c::const_string kNON_EXISTENT_PATH = "/hades/tmp/dne/file.json";
|
||||
|
||||
BEGIN_TEST_SUITE("elemental::JsonConfigFile")
|
||||
{
|
||||
namespace { // Test fixtures
|
||||
struct SampleFileGenerator {
|
||||
SampleFileGenerator()
|
||||
{
|
||||
if (!fs::exists(kINPUT_FILE_PATH)) {
|
||||
std::ofstream new_file(
|
||||
kINPUT_FILE_PATH,
|
||||
std::ios::out | std::ios::trunc
|
||||
);
|
||||
new_file
|
||||
<< R"({"key1": "value1", "key2": "value2"})"
|
||||
<< std::endl;
|
||||
new_file.close();
|
||||
}
|
||||
}
|
||||
~SampleFileGenerator()
|
||||
{
|
||||
try {
|
||||
if (fs::exists(kINPUT_FILE_PATH)) {
|
||||
fs::remove(kINPUT_FILE_PATH);
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
DBG_PRINT(e.what());
|
||||
}
|
||||
}
|
||||
};
|
||||
using TestFixture = SampleFileGenerator;
|
||||
} // anonymous namespace
|
||||
|
||||
TEST("elemental::nlohmann::json is serializablable like "
|
||||
"std::map<std::string,std::string>")
|
||||
{
|
||||
IOCore::Dictionary<std::string> test_data;
|
||||
nlohmann::json jsonified;
|
||||
|
||||
test_data["one"] = "1";
|
||||
test_data["resolution"] = "1280x720";
|
||||
test_data["Hello"] = "world";
|
||||
|
||||
jsonified = test_data;
|
||||
|
||||
REQUIRE(test_data["one"] == jsonified["one"].get<std::string>());
|
||||
REQUIRE(
|
||||
test_data["resolution"] ==
|
||||
jsonified["resolution"].get<std::string>()
|
||||
);
|
||||
REQUIRE(
|
||||
test_data["Hello"] == jsonified["Hello"].get<std::string>()
|
||||
);
|
||||
}
|
||||
|
||||
FIXTURE_TEST("JsonConfigFile construction")
|
||||
{
|
||||
SECTION("JsonConfigFile w/ valid path")
|
||||
{
|
||||
auto config = JsonConfigFile(kINPUT_FILE_PATH);
|
||||
auto& config_data = config.jsonDataRef();
|
||||
|
||||
REQUIRE(config_data.empty());
|
||||
}
|
||||
SECTION("JsonConfigFile w/ invalid path throws exception")
|
||||
{
|
||||
|
||||
REQUIRE_THROWS_AS(
|
||||
JsonConfigFile(kNON_EXISTENT_PATH),
|
||||
IOCore::UnreachablePathException
|
||||
);
|
||||
}
|
||||
}
|
||||
FIXTURE_TEST("JsonConfigFile::Read")
|
||||
{
|
||||
auto config_file = JsonConfigFile(kINPUT_FILE_PATH);
|
||||
auto& json_data = config_file.jsonDataRef();
|
||||
|
||||
SECTION("JsonConfigFile::Read w/ valid file")
|
||||
{
|
||||
config_file.read();
|
||||
REQUIRE_FALSE(json_data.empty());
|
||||
REQUIRE(json_data.size() == 2);
|
||||
|
||||
REQUIRE(json_data["key1"] == "value1");
|
||||
REQUIRE(json_data["key2"] == "value2");
|
||||
}
|
||||
SECTION("JsonConfigFile::Read w/ bad file throws exception")
|
||||
{
|
||||
if (!fs::exists(kBADFILE_PATH)) {
|
||||
std::ofstream fileout(kBADFILE_PATH);
|
||||
fileout << R"({"Hello World"})" << std::endl;
|
||||
fileout.close();
|
||||
}
|
||||
REQUIRE_THROWS_AS(
|
||||
[&]() {
|
||||
auto bad_config =
|
||||
JsonConfigFile(kBADFILE_PATH);
|
||||
|
||||
bad_config.read();
|
||||
}(),
|
||||
IOCore::Exception
|
||||
);
|
||||
}
|
||||
if (fs::exists(kBADFILE_PATH)) {
|
||||
fs::remove(kBADFILE_PATH);
|
||||
}
|
||||
}
|
||||
TEST("JsonConfigFile::Write()")
|
||||
{
|
||||
SECTION("Create File and Read It back In")
|
||||
{
|
||||
auto config_file = JsonConfigFile(kINPUT_FILE_PATH);
|
||||
auto& test_data = config_file.jsonDataRef();
|
||||
|
||||
test_data["one"] = "1";
|
||||
test_data["resolution"] = "1280x720";
|
||||
test_data["Hello"] = "world";
|
||||
|
||||
config_file.write();
|
||||
|
||||
std::ifstream resulting_file(kINPUT_FILE_PATH);
|
||||
nlohmann::json jobject;
|
||||
|
||||
resulting_file >> jobject;
|
||||
auto written_data =
|
||||
jobject.get<IOCore::Dictionary<std::string>>();
|
||||
|
||||
REQUIRE(
|
||||
written_data["one"] ==
|
||||
test_data["one"].get<std::string>()
|
||||
);
|
||||
REQUIRE(
|
||||
written_data["resolution"] ==
|
||||
test_data["resolution"].get<std::string>()
|
||||
);
|
||||
REQUIRE(
|
||||
written_data["Hello"] ==
|
||||
test_data["Hello"].get<std::string>()
|
||||
);
|
||||
}
|
||||
fs::remove(kINPUT_FILE_PATH);
|
||||
}
|
||||
|
||||
FIXTURE_TEST(
|
||||
"JsonConfigFile::Get<T>() basically wraps nlohmann::json::get<T>()"
|
||||
)
|
||||
{
|
||||
auto config_file = JsonConfigFile(kINPUT_FILE_PATH);
|
||||
auto& json_data = config_file.jsonDataRef();
|
||||
|
||||
config_file.read();
|
||||
auto obtained_data =
|
||||
config_file.get<IOCore::Dictionary<std::string>>();
|
||||
|
||||
REQUIRE(obtained_data["key1"] == "value1");
|
||||
REQUIRE(obtained_data["key2"] == "value2");
|
||||
}
|
||||
FIXTURE_TEST(
|
||||
"JsonConfigFile::Set() basically wraps nlohmann::json::operator=()"
|
||||
)
|
||||
{
|
||||
auto config_file = JsonConfigFile(kINPUT_FILE_PATH);
|
||||
IOCore::Dictionary<std::string> test_data;
|
||||
|
||||
test_data["one"] = "1";
|
||||
test_data["resolution"] = "1280x720";
|
||||
test_data["Hello"] = "world";
|
||||
|
||||
config_file.set(test_data);
|
||||
|
||||
auto& json_data = config_file.jsonDataRef();
|
||||
|
||||
REQUIRE(json_data["one"] == test_data["one"]);
|
||||
REQUIRE(json_data["resolution"] == test_data["resolution"]);
|
||||
REQUIRE(json_data["Hello"] == test_data["Hello"]);
|
||||
}
|
||||
}
|
||||
// clang-format off
|
||||
// vim: set foldmethod=syntax textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :
|
Loading…
Reference in New Issue
Block a user