This commit is contained in:
+261
@@ -0,0 +1,261 @@
|
||||
#include <luma/config.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <expected>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/json/object.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
#include <modulus.hpp>
|
||||
|
||||
#include <luma/detail/errors.hpp>
|
||||
#include <luma/theme.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto json_options = modulus::json_config::options{
|
||||
.category = "luma",
|
||||
.invalid_argument_code = luma::detail::error_invalid_argument,
|
||||
.not_found_code = luma::detail::error_not_found
|
||||
};
|
||||
|
||||
auto invalid_display(std::string message) -> std::expected<luma::display_config, modulus::error> {
|
||||
assert(!message.empty());
|
||||
return luma::detail::fail<luma::display_config>(luma::detail::error_invalid_argument, std::move(message));
|
||||
}
|
||||
|
||||
auto invalid_touch(std::string message) -> std::expected<luma::touch_config, modulus::error> {
|
||||
assert(!message.empty());
|
||||
return luma::detail::fail<luma::touch_config>(luma::detail::error_invalid_argument, std::move(message));
|
||||
}
|
||||
|
||||
auto invalid_application(std::string message) -> std::expected<luma::application_config, modulus::error> {
|
||||
assert(!message.empty());
|
||||
return luma::detail::fail<luma::application_config>(luma::detail::error_invalid_argument, std::move(message));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
auto luma::validate(luma::display_config const& config) -> std::expected<void, modulus::error> {
|
||||
if (config.width <= 0) {
|
||||
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "display.width must be positive");
|
||||
}
|
||||
if (config.height <= 0) {
|
||||
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "display.height must be positive");
|
||||
}
|
||||
if (config.target_fps <= 0) {
|
||||
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "display.target_fps must be positive");
|
||||
}
|
||||
if (config.idle_fps <= 0) {
|
||||
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "display.idle_fps must be positive");
|
||||
}
|
||||
if (config.idle_fps > config.target_fps) {
|
||||
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "display.idle_fps must be less than or equal to display.target_fps");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto luma::validate(luma::touch_config const& config) -> std::expected<void, modulus::error> {
|
||||
if (config.max_points <= 0) {
|
||||
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "touch.max_points must be positive");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto luma::validate(luma::application_config const& config) -> std::expected<void, modulus::error> {
|
||||
if (config.title.empty()) {
|
||||
return luma::detail::fail<void>(luma::detail::error_invalid_argument, "title must not be empty");
|
||||
}
|
||||
if (auto status = luma::validate(config.display); !status) {
|
||||
return modulus::fail<void>(status.error());
|
||||
}
|
||||
if (auto status = luma::validate(config.touch); !status) {
|
||||
return modulus::fail<void>(status.error());
|
||||
}
|
||||
if (auto status = luma::validate(config.theme); !status) {
|
||||
return modulus::fail<void>(status.error());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto luma::parse_display_config(boost::json::value const& value) -> std::expected<luma::display_config, modulus::error> {
|
||||
auto object = modulus::json_config::require_object(value, "display", json_options);
|
||||
if (!object) {
|
||||
return invalid_display(std::string{object.error().message()});
|
||||
}
|
||||
auto unknown = modulus::json_config::reject_unknown_fields(**object, {
|
||||
"width",
|
||||
"height",
|
||||
"target_fps",
|
||||
"idle_fps",
|
||||
"fullscreen",
|
||||
}, "display", json_options);
|
||||
if (!unknown) {
|
||||
return invalid_display(std::string{unknown.error().message()});
|
||||
}
|
||||
auto config = luma::display_config{};
|
||||
auto width = modulus::json_config::optional_int32_field(**object, "width", config.width, std::numeric_limits<std::int32_t>::min(), std::numeric_limits<std::int32_t>::max(), json_options);
|
||||
auto height = modulus::json_config::optional_int32_field(**object, "height", config.height, std::numeric_limits<std::int32_t>::min(), std::numeric_limits<std::int32_t>::max(), json_options);
|
||||
auto target_fps = modulus::json_config::optional_int32_field(**object, "target_fps", config.target_fps, std::numeric_limits<std::int32_t>::min(), std::numeric_limits<std::int32_t>::max(), json_options);
|
||||
auto idle_fps = modulus::json_config::optional_int32_field(**object, "idle_fps", config.idle_fps, std::numeric_limits<std::int32_t>::min(), std::numeric_limits<std::int32_t>::max(), json_options);
|
||||
auto fullscreen = modulus::json_config::optional_bool_field(**object, "fullscreen", config.fullscreen, json_options);
|
||||
if (!width) {
|
||||
return invalid_display(std::string{width.error().message()});
|
||||
}
|
||||
if (!height) {
|
||||
return invalid_display(std::string{height.error().message()});
|
||||
}
|
||||
if (!target_fps) {
|
||||
return invalid_display(std::string{target_fps.error().message()});
|
||||
}
|
||||
if (!idle_fps) {
|
||||
return invalid_display(std::string{idle_fps.error().message()});
|
||||
}
|
||||
if (!fullscreen) {
|
||||
return invalid_display(std::string{fullscreen.error().message()});
|
||||
}
|
||||
config.width = *width;
|
||||
config.height = *height;
|
||||
config.target_fps = *target_fps;
|
||||
config.idle_fps = *idle_fps;
|
||||
config.fullscreen = *fullscreen;
|
||||
if (auto status = luma::validate(config); !status) {
|
||||
return invalid_display(std::string{status.error().message()});
|
||||
}
|
||||
return modulus::success(config);
|
||||
}
|
||||
|
||||
auto luma::parse_touch_config(boost::json::value const& value) -> std::expected<luma::touch_config, modulus::error> {
|
||||
auto object = modulus::json_config::require_object(value, "touch", json_options);
|
||||
if (!object) {
|
||||
return invalid_touch(std::string{object.error().message()});
|
||||
}
|
||||
auto unknown = modulus::json_config::reject_unknown_fields(**object, {"max_points"}, "touch", json_options);
|
||||
if (!unknown) {
|
||||
return invalid_touch(std::string{unknown.error().message()});
|
||||
}
|
||||
auto config = luma::touch_config{};
|
||||
auto max_points = modulus::json_config::optional_int32_field(**object, "max_points", config.max_points, std::numeric_limits<std::int32_t>::min(), std::numeric_limits<std::int32_t>::max(), json_options);
|
||||
if (!max_points) {
|
||||
return invalid_touch(std::string{max_points.error().message()});
|
||||
}
|
||||
config.max_points = *max_points;
|
||||
if (auto status = luma::validate(config); !status) {
|
||||
return invalid_touch(std::string{status.error().message()});
|
||||
}
|
||||
return modulus::success(config);
|
||||
}
|
||||
|
||||
auto luma::parse_application_config(boost::json::value const& value) -> std::expected<luma::application_config, modulus::error> {
|
||||
auto object = modulus::json_config::require_object(value, "application config", json_options);
|
||||
if (!object) {
|
||||
return invalid_application(std::string{object.error().message()});
|
||||
}
|
||||
auto unknown = modulus::json_config::reject_unknown_fields(**object, {
|
||||
"title",
|
||||
"display",
|
||||
"touch",
|
||||
"theme",
|
||||
}, "application config", json_options);
|
||||
if (!unknown) {
|
||||
return invalid_application(std::string{unknown.error().message()});
|
||||
}
|
||||
auto config = luma::application_config{};
|
||||
auto title = modulus::json_config::optional_string_field(**object, "title", config.title, json_options);
|
||||
if (!title) {
|
||||
return invalid_application(std::string{title.error().message()});
|
||||
}
|
||||
config.title = *title;
|
||||
if (auto const* display = (*object)->if_contains("display")) {
|
||||
auto parsed = luma::parse_display_config(*display);
|
||||
if (!parsed) {
|
||||
return invalid_application(std::string{parsed.error().message()});
|
||||
}
|
||||
config.display = *parsed;
|
||||
}
|
||||
if (auto const* touch = (*object)->if_contains("touch")) {
|
||||
auto parsed = luma::parse_touch_config(*touch);
|
||||
if (!parsed) {
|
||||
return invalid_application(std::string{parsed.error().message()});
|
||||
}
|
||||
config.touch = *parsed;
|
||||
}
|
||||
if (auto const* theme = (*object)->if_contains("theme")) {
|
||||
auto parsed = luma::parse_theme(*theme);
|
||||
if (!parsed) {
|
||||
return invalid_application(std::string{parsed.error().message()});
|
||||
}
|
||||
config.theme = *parsed;
|
||||
}
|
||||
if (auto status = luma::validate(config); !status) {
|
||||
return invalid_application(std::string{status.error().message()});
|
||||
}
|
||||
return modulus::success(config);
|
||||
}
|
||||
|
||||
auto luma::load_application_config(std::string const& path) -> std::expected<luma::application_config, modulus::error> {
|
||||
auto value = modulus::json_config::load_value(path, json_options);
|
||||
if (!value) {
|
||||
return modulus::fail<luma::application_config>(value.error());
|
||||
}
|
||||
return luma::parse_application_config(*value);
|
||||
}
|
||||
|
||||
auto luma::to_json(luma::display_config const& config) -> boost::json::value {
|
||||
return boost::json::object{
|
||||
{
|
||||
"width",
|
||||
config.width,
|
||||
},
|
||||
{
|
||||
"height",
|
||||
config.height,
|
||||
},
|
||||
{
|
||||
"target_fps",
|
||||
config.target_fps,
|
||||
},
|
||||
{
|
||||
"idle_fps",
|
||||
config.idle_fps,
|
||||
},
|
||||
{
|
||||
"fullscreen",
|
||||
config.fullscreen,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
auto luma::to_json(luma::touch_config const& config) -> boost::json::value {
|
||||
return boost::json::object{
|
||||
{
|
||||
"max_points",
|
||||
config.max_points,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
auto luma::to_json(luma::application_config const& config) -> boost::json::value {
|
||||
return boost::json::object{
|
||||
{
|
||||
"title",
|
||||
config.title,
|
||||
},
|
||||
{
|
||||
"display",
|
||||
luma::to_json(config.display),
|
||||
},
|
||||
{
|
||||
"touch",
|
||||
luma::to_json(config.touch),
|
||||
},
|
||||
{
|
||||
"theme",
|
||||
luma::to_json(config.theme),
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user