Initial public khodo snapshot
publish / publish (push) Successful in 27m4s

This commit is contained in:
khodowany
2026-09-04 19:30:11 -05:00
commit 51339606a2
38 changed files with 8989 additions and 0 deletions
+261
View File
@@ -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),
},
};
}