Files
2026-08-09 22:47:14 +08:00

307 lines
11 KiB
C++

#pragma once
#include "adminive/composition.hpp"
#include "adminive/json.hpp"
#include "adminive/view.hpp"
#include <map>
#include <string_view>
namespace adminive {
inline constexpr std::string_view view_protocol = "adminive.view";
inline constexpr int view_protocol_version = 1;
inline constexpr std::string_view composition_manifest_protocol = "adminive.composition-manifest";
inline constexpr int composition_manifest_protocol_version = 1;
inline std::string_view composition_node_kind_name(Composition_Node_Kind value) noexcept {
switch(value) {
case Composition_Node_Kind::slot:
return "slot";
case Composition_Node_Kind::heading:
return "heading";
case Composition_Node_Kind::frontend:
return "frontend";
case Composition_Node_Kind::vertical:
return "vertical";
case Composition_Node_Kind::horizontal:
return "horizontal";
case Composition_Node_Kind::flow:
return "flow";
case Composition_Node_Kind::grid:
return "grid";
case Composition_Node_Kind::list:
return "list";
case Composition_Node_Kind::group:
return "group";
case Composition_Node_Kind::card:
return "card";
case Composition_Node_Kind::tabs:
return "tabs";
case Composition_Node_Kind::tab:
return "tab";
}
return "vertical";
}
template <Json_Type Json>
Json composition_node_to_json(const Composition_Node& node) {
Json result = json_object<Json>();
json_set(result, "kind", std::string(composition_node_kind_name(node.kind)));
if(!node.slot.empty()) {
json_set(result, "slot", node.slot);
}
if(!node.title.empty()) {
json_set(result, "title", node.title);
}
if(!node.description.empty()) {
json_set(result, "description", node.description);
}
if(!node.visible_on.empty()) {
json_set(result, "visible_on", node.visible_on);
}
if(!node.accent_color.empty()) {
json_set(result, "accent_color", node.accent_color);
}
if(node.columns != 0) {
json_set(result, "columns", node.columns);
}
if(node.collapsible) {
json_set(result, "collapsible", true);
json_set(result, "collapsed", node.collapsed);
}
if(!node.children.empty()) {
Json children = json_array<Json>();
for(const auto& child : node.children) {
json_append(children, composition_node_to_json<Json>(child));
}
json_set(result, "children", std::move(children));
}
return result;
}
template <Json_Type Json>
Json to_view_json(const Composition_View& view) {
validate_composition_view(view);
Json result = json_object<Json>();
json_set(result, "protocol", view_protocol);
json_set(result, "protocol_version", view_protocol_version);
json_set(result, "kind", "composition");
json_set(result, "name", view.name);
if(!view.title.empty()) {
json_set(result, "title", view.title);
}
json_set(result, "body", composition_node_to_json<Json>(view.body));
return result;
}
template <Json_Type Json>
Json to_composition_manifest_json(const Composition_View& view, const std::vector<Composition_Slot_Contract>& contracts, const std::map<std::string, Json>& schemas) {
validate_composition_slot_contracts(view, contracts);
Json slots = json_object<Json>();
for(const auto& contract : contracts) {
const auto schema = schemas.find(contract.name);
if(schema == schemas.end()) {
throw std::invalid_argument("composition slot schema is not provided: " + contract.name);
}
Json item = json_object<Json>();
json_set(item, "component_kind", contract.component_kind);
if(!contract.descriptor_api.empty()) {
json_set(item, "descriptor_api", contract.descriptor_api);
}
if(!contract.view_api.empty()) {
json_set(item, "view_api", contract.view_api);
}
if(!contract.data_api.empty()) {
json_set(item, "data_api", contract.data_api);
}
if(!contract.amis_api.empty()) {
json_set(item, "amis_api", contract.amis_api);
}
json_set(item, "schema", schema->second);
json_set(slots, contract.name, std::move(item));
}
Json result = json_object<Json>();
json_set(result, "protocol", composition_manifest_protocol);
json_set(result, "protocol_version", composition_manifest_protocol_version);
json_set(result, "view", to_view_json<Json>(view));
json_set(result, "slots", std::move(slots));
return result;
}
inline std::string_view view_node_kind_name(View_Node_Kind value) noexcept {
switch(value) {
case View_Node_Kind::all_fields:
return "all_fields";
case View_Node_Kind::field:
return "field";
case View_Node_Kind::frontend:
return "frontend";
case View_Node_Kind::vertical:
return "vertical";
case View_Node_Kind::horizontal:
return "horizontal";
case View_Node_Kind::flow:
return "flow";
case View_Node_Kind::grid:
return "grid";
case View_Node_Kind::list:
return "list";
case View_Node_Kind::group:
return "group";
case View_Node_Kind::card:
return "card";
case View_Node_Kind::tabs:
return "tabs";
case View_Node_Kind::tab:
return "tab";
}
return "vertical";
}
inline std::string_view form_mode_name(Form_Mode value) noexcept {
switch(value) {
case Form_Mode::display:
return "display";
case Form_Mode::edit:
return "edit";
case Form_Mode::create:
return "create";
}
return "display";
}
inline std::string_view table_fixed_name(Table_Fixed value) noexcept {
switch(value) {
case Table_Fixed::none:
return "none";
case Table_Fixed::left:
return "left";
case Table_Fixed::right:
return "right";
}
return "none";
}
template <Json_Type Json>
Json view_node_to_json(const View_Node& node) {
Json result = json_object<Json>();
json_set(result, "kind", std::string(view_node_kind_name(node.kind)));
if(!node.field.empty()) {
json_set(result, "field", node.field);
}
if(!node.title.empty()) {
json_set(result, "title", node.title);
}
if(!node.description.empty()) {
json_set(result, "description", node.description);
}
if(!node.visible_on.empty()) {
json_set(result, "visible_on", node.visible_on);
}
if(node.columns != 0) {
json_set(result, "columns", node.columns);
}
if(node.collapsible) {
json_set(result, "collapsible", true);
json_set(result, "collapsed", node.collapsed);
}
if(!node.children.empty()) {
Json children = json_array<Json>();
for(const auto& child : node.children) {
json_append(children, view_node_to_json<Json>(child));
}
json_set(result, "children", std::move(children));
}
return result;
}
template <Json_Type Json, Described_Type T>
Json to_view_json(const Form_View& view) {
validate_form_view<T>(view);
Json result = json_object<Json>();
json_set(result, "protocol", view_protocol);
json_set(result, "protocol_version", view_protocol_version);
json_set(result, "kind", "form");
json_set(result, "name", view.name);
json_set(result, "title", view.title);
json_set(result, "mode", std::string(form_mode_name(view.mode)));
json_set(result, "submit_label", view.submit_label);
json_set(result, "affix_footer", view.affix_footer);
json_set(result, "body", view_node_to_json<Json>(view.body));
return result;
}
inline std::string_view collection_mode_name(Collection_Mode value) noexcept {
switch(value) {
case Collection_Mode::table:
return "table";
case Collection_Mode::list:
return "list";
case Collection_Mode::cards:
return "cards";
}
return "table";
}
template <Json_Type Json>
void append_table_view_json(Json& result, const Table_View& view) {
json_set(result, "name", view.name);
json_set(result, "title", view.title);
json_set(result, "id_label", view.id_label);
json_set(result, "create_label", view.create_label);
json_set(result, "edit_label", view.edit_label);
json_set(result, "delete_label", view.delete_label);
json_set(result, "actions_label", view.actions_label);
json_set(result, "confirm_label", view.confirm_label);
json_set(result, "delete_confirm", view.delete_confirm);
json_set(result, "view_status_label", view.view_status_label);
json_set(result, "default_order_by", view.default_order_by);
json_set(result, "default_order_dir", view.default_order_dir);
json_set(result, "user_reorderable", view.user_reorderable);
json_set(result, "column_reorderable", view.column_reorderable);
Json columns = json_array<Json>();
for(const auto& column : view.columns) {
Json item = json_object<Json>();
json_set(item, "field", column.field);
if(!column.label.empty()) {
json_set(item, "label", column.label);
}
json_set(item, "sortable", column.sortable);
json_set(item, "searchable", column.searchable);
json_set(item, "filterable", column.filterable);
json_set(item, "fixed", std::string(table_fixed_name(column.fixed)));
json_append(columns, std::move(item));
}
json_set(result, "columns", std::move(columns));
json_set(result, "create_body", view_node_to_json<Json>(view.create_body));
json_set(result, "edit_body", view_node_to_json<Json>(view.edit_body));
}
template <Json_Type Json, Described_Type T>
Json to_view_json(const Table_View& view) {
validate_table_view<T>(view);
Json result = json_object<Json>();
json_set(result, "protocol", view_protocol);
json_set(result, "protocol_version", view_protocol_version);
json_set(result, "kind", "table");
append_table_view_json(result, view);
return result;
}
template <Json_Type Json, Described_Type T>
Json to_view_json(const Collection_View& view) {
validate_collection_view<T>(view);
Json result = json_object<Json>();
json_set(result, "protocol", view_protocol);
json_set(result, "protocol_version", view_protocol_version);
json_set(result, "kind", "collection");
json_set(result, "mode", std::string(collection_mode_name(view.mode)));
append_table_view_json(result, view.table);
if(view.mode != Collection_Mode::table) {
Json item = json_object<Json>();
json_set(item, "title_field", view.item.title_field);
if(!view.item.subtitle_field.empty()) {
json_set(item, "subtitle_field", view.item.subtitle_field);
}
if(!view.item.accent_field.empty()) {
json_set(item, "accent_field", view.item.accent_field);
}
Json fields = json_array<Json>();
for(const auto& field : view.item.fields) {
json_append(fields, field);
}
json_set(item, "fields", std::move(fields));
json_set(result, "item", std::move(item));
}
if(view.mode == Collection_Mode::cards) {
json_set(result, "cards_columns", view.cards_columns);
}
return result;
}
}