#include "adminive/adminive.hpp" #include "adminive_test.hpp" #include #include #include #include #include #include #include #include #include #include namespace adapter_test { struct Mini_Json; using Mini_Object = std::map; using Mini_Array = std::vector; struct Mini_Json { using Storage = std::variant; Storage value{}; }; struct External_Atomic { int value{}; }; enum class Mode { primary = 10, secondary = 20 }; struct Runtime_Base { External_Atomic worker_count{4}; }; struct Mode_Base { Mode mode{Mode::primary}; }; struct Config : Runtime_Base, Mode_Base { std::string name{"default"}; }; class Runtime_Control final { public: Runtime_Control() = default; Runtime_Control(const Runtime_Control&) = delete; Runtime_Control& operator=(const Runtime_Control&) = delete; [[nodiscard]] int setting() const noexcept { return setting_; } void apply_setting(int value) noexcept { setting_ = value; } private: int setting_{3}; }; } namespace adminive { template <> struct Json_Adapter { using Json = adapter_test::Mini_Json; static Json object() { return Json{adapter_test::Mini_Object{}}; } static Json array() { return Json{adapter_test::Mini_Array{}}; } static Json null() { return Json{nullptr}; } static Json parse(std::string_view) { throw std::runtime_error("parse is not implemented by the test adapter"); } static std::string dump(const Json&, int) { return {}; } static bool is_null(const Json& value) noexcept { return std::holds_alternative(value.value); } static bool is_object(const Json& value) noexcept { return std::holds_alternative(value.value); } static bool is_array(const Json& value) noexcept { return std::holds_alternative(value.value); } static bool is_string(const Json& value) noexcept { return std::holds_alternative(value.value); } static bool is_signed_integer(const Json& value) noexcept { return std::holds_alternative(value.value); } static bool is_unsigned_integer(const Json& value) noexcept { return std::holds_alternative(value.value); } static bool is_floating_point(const Json& value) noexcept { return std::holds_alternative(value.value); } static bool is_number(const Json& value) noexcept { return is_signed_integer(value) || is_unsigned_integer(value) || is_floating_point(value); } static bool is_boolean(const Json& value) noexcept { return std::holds_alternative(value.value); } static std::int64_t signed_integer(const Json& value) { return std::get(value.value); } static std::uint64_t unsigned_integer(const Json& value) { return std::get(value.value); } static long double number(const Json& value) { if(is_signed_integer(value)) { return static_cast(signed_integer(value)); } if(is_unsigned_integer(value)) { return static_cast(unsigned_integer(value)); } return std::get(value.value); } static bool contains(const Json& value, std::string_view name) { const auto& object_value = std::get(value.value); return object_value.contains(std::string(name)); } static std::size_t size(const Json& value) { if(is_object(value)) { return std::get(value.value).size(); } if(is_array(value)) { return std::get(value.value).size(); } return 0; } static std::vector keys(const Json& value) { std::vector result; for(const auto& [name, item] : std::get(value.value)) { static_cast(item); result.push_back(name); } return result; } static Json& at(Json& value, std::string_view name) { return std::get(value.value).at(std::string(name)); } static const Json& at(const Json& value, std::string_view name) { return std::get(value.value).at(std::string(name)); } static Json& at(Json& value, std::size_t index) { return std::get(value.value).at(index); } static const Json& at(const Json& value, std::size_t index) { return std::get(value.value).at(index); } static void set(Json& value, std::string_view name, Json item) { std::get(value.value).insert_or_assign(std::string(name), std::move(item)); } static void append(Json& value, Json item) { std::get(value.value).push_back(std::move(item)); } static void erase(Json& value, std::string_view name) { std::get(value.value).erase(std::string(name)); } template static Json make(T&& value) { using Value = std::remove_cvref_t; if constexpr(std::same_as) { return Json{value}; } else if constexpr(std::signed_integral) { return Json{static_cast(value)}; } else if constexpr(std::unsigned_integral) { return Json{static_cast(value)}; } else if constexpr(std::floating_point) { return Json{static_cast(value)}; } else if constexpr(std::same_as) { return Json{std::forward(value)}; } else if constexpr(std::same_as) { return Json{std::string(value)}; } else if constexpr(std::is_convertible_v) { return Json{std::string(value)}; } else { static_assert(!sizeof(T), "unsupported Mini_Json scalar"); } } template static T get(const Json& value) { if constexpr(std::same_as) { return std::get(value.value); } else if constexpr(std::signed_integral) { return static_cast(std::get(value.value)); } else if constexpr(std::unsigned_integral) { return static_cast(std::get(value.value)); } else if constexpr(std::floating_point) { return static_cast(number(value)); } else if constexpr(std::same_as) { return std::get(value.value); } else { static_assert(!sizeof(T), "unsupported Mini_Json conversion"); } } }; template struct Value_Adapter { using value_type = int; static int read(const adapter_test::External_Atomic& value) noexcept { return value.value; } static void write(adapter_test::External_Atomic& target, int value) noexcept { target.value = value; } }; template <> struct Enum_Adapter { static std::vector values() { return {adapter_test::Mode::primary, adapter_test::Mode::secondary}; } static std::string_view name(adapter_test::Mode value) { return value == adapter_test::Mode::primary ? "primary" : "secondary"; } static std::optional cast(std::string_view value) { if(value == "primary") { return adapter_test::Mode::primary; } if(value == "secondary") { return adapter_test::Mode::secondary; } return std::nullopt; } static std::optional cast(std::underlying_type_t value) { if(value == 10) { return adapter_test::Mode::primary; } if(value == 20) { return adapter_test::Mode::secondary; } return std::nullopt; } }; template <> struct Reflection_Adapter { static constexpr std::size_t field_count = 3; template static decltype(auto) get(adapter_test::Config& value) noexcept { if constexpr(Index == 0) { return (value.worker_count); } else if constexpr(Index == 1) { return (value.mode); } else { return (value.name); } } template static decltype(auto) get(const adapter_test::Config& value) noexcept { if constexpr(Index == 0) { return (value.worker_count); } else if constexpr(Index == 1) { return (value.mode); } else { return (value.name); } } template static constexpr std::string_view name() noexcept { if constexpr(Index == 0) { return "worker_count"; } else if constexpr(Index == 1) { return "mode"; } else { return "name"; } } }; template <> struct Type_Descriptor { static auto get() { using T = adapter_test::Config; return reflected_object_with("config", "配置", [](auto field) { if constexpr(Index == 0) { return field.label("线程数").editable().required(); } else if constexpr(Index == 1) { return field.label("模式").editable().template enum_label("主用").template enum_label("备用"); } else { return field.label("名称").editable(); } }); } }; template <> struct Type_Descriptor { static auto get() { auto accessor = structive::trusted_callable_accessor( [](const adapter_test::Runtime_Control& value) { return value.setting(); }, [](adapter_test::Runtime_Control& target, int value) { target.apply_setting(value); }); return adminive::object( "runtime_control", "Runtime Control", adminive::field("setting", "Setting", std::move(accessor)) .editable() .unsynchronized()); } }; template <> struct Type_View_Descriptor { static Table_View table() { using T = adapter_test::Config; return table_view(column_index("线程数").sort(), column_index("模式").sort().filter(), column_index("名称").search()); } }; } int main() { using Json = adapter_test::Mini_Json; using Config = adapter_test::Config; const Json descriptor = adminive::to_descriptor_json_with_defaults(); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(descriptor, "protocol")) == adminive::resource_protocol)); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(descriptor, "protocol_version")) == adminive::resource_protocol_version)); const auto& fields = adminive::Json_Adapter::at(descriptor, "fields"); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(adminive::Json_Adapter::at(fields, 0), "name")) == "worker_count")); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(adminive::Json_Adapter::at(fields, 0), "default")) == 4)); const auto& presentation = adminive::Json_Adapter::at(adminive::Json_Adapter::at(fields, 1), "presentation"); const auto& options = adminive::Json_Adapter::at(presentation, "options"); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(adminive::Json_Adapter::at(options, 0), "label")) == "主用")); Config config; Json patch = adminive::json_object(); adminive::json_set(patch, "worker_count", 12); adminive::json_set(patch, "mode", "secondary"); adminive::json_set(patch, "name", "updated"); const auto result = adminive::apply_frontend_patch(config, patch); ADMINIVE_CHECK(result.success); ADMINIVE_CHECK(config.worker_count.value == 12); ADMINIVE_CHECK(config.mode == adapter_test::Mode::secondary); ADMINIVE_CHECK(config.name == "updated"); const Json encoded = adminive::to_json(config); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(encoded, "worker_count")) == 12)); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(encoded, "mode")) == "secondary")); adapter_test::Runtime_Control runtime_control; const Json runtime_encoded = adminive::to_json(runtime_control); ADMINIVE_CHECK((adminive::json_get( adminive::Json_Adapter::at(runtime_encoded, "setting")) == 3)); Json runtime_patch = adminive::json_object(); adminive::json_set(runtime_patch, "setting", 17); const auto runtime_result = adminive::apply_frontend_patch(runtime_control, runtime_patch); ADMINIVE_CHECK(runtime_result.success); ADMINIVE_CHECK(runtime_control.setting() == 17); adminive::Collection_Service collection("/configs", {Config{}}); const auto list = collection.list_response(); ADMINIVE_CHECK(list.status == 200); const auto& data = adminive::Json_Adapter::at(list.body, "data"); ADMINIVE_CHECK((adminive::json_get(adminive::Json_Adapter::at(data, "total")) == 1)); return 0; }