Files
2026-08-12 01:44:13 +08:00

344 lines
14 KiB
C++

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