larra/library/include/larra/serialization.hpp
sha512sum 13d063915e
All checks were successful
PR Check / on-push-commit-check (push) Successful in 11m28s
Add serialization and deserialization for vector
2024-11-10 14:58:47 +00:00

233 lines
7.7 KiB
C++

#pragma once
#include <libxml++/libxml++.h>
#include <spdlog/spdlog.h>
#include <larra/serialization/error.hpp>
#include <nameof.hpp>
#include <ranges>
#include <string>
#include <utempl/utils.hpp>
namespace larra::xmpp {
template <typename T>
concept AsXml = requires(xmlpp::Element* element, const T& obj) {
element << obj;
{ T::kDefaultName } -> std::convertible_to<const std::string&>;
};
template <typename T>
concept HasDefaultName = requires {
{ T::kDefaultName } -> std::convertible_to<const std::string&>;
};
template <typename T>
concept HasDefaultNamespace = requires {
{ T::kDefaultNamespace } -> std::convertible_to<const std::string&>;
};
template <typename T>
concept HasDefaultPrefix = requires {
{ T::kPrefix } -> std::convertible_to<const std::string&>;
};
template <typename T>
concept HasAddXmlDecl = requires {
{ T::kAddXmlDecl } -> std::convertible_to<bool>;
};
template <typename T>
concept HasRemoveEnd = requires {
{ T::kRemoveEnd } -> std::convertible_to<bool>;
};
template <typename T>
concept HasTryParse = requires(xmlpp::Element* element) {
{ T::TryParse(element) } -> std::same_as<std::optional<T>>;
};
template <typename T = void>
struct SerializationBase {
inline static const std::string kDefaultName = [] {
if constexpr(HasDefaultName<T>) {
return T::kDefaultName;
} else {
return "";
}
}();
inline static const std::string kDefaultNamespace = [] {
if constexpr(HasDefaultNamespace<T>) {
return T::kDefaultNamespace;
} else {
return "";
}
}();
inline static const std::string kPrefix = [] {
if constexpr(HasDefaultPrefix<T>) {
return T::kPrefix;
} else {
return "";
}
}();
static constexpr bool kAddXmlDecl = [] {
if constexpr(HasAddXmlDecl<T>) {
return T::kAddXmlDecl;
} else {
return false;
}
}();
static constexpr bool kRemoveEnd = [] {
if constexpr(HasRemoveEnd<T>) {
return T::kRemoveEnd;
} else {
return false;
}
}();
static constexpr auto StartCheck(xmlpp::Element* element) -> bool {
if constexpr(requires {
{ T::StartCheck(element) } -> std::same_as<bool>;
}) {
return T::StartCheck(element);
} else {
return element && element->get_name() == kDefaultName;
}
};
};
template <typename T>
struct Serialization : SerializationBase<T> {
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> T {
if(!Serialization::StartCheck(element)) {
throw serialization::ParsingError{
std::format("[{}: {}] parsing error: [ StartCheck failed ]", Serialization::kDefaultName, nameof::nameof_full_type<T>())};
}
return T::Parse(element);
}
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
if constexpr(HasTryParse<T>) {
return Serialization::StartCheck(element) ? T::TryParse(element) : std::nullopt;
} else {
try {
return Serialization::StartCheck(element) ? std::optional{T::Parse(element)} : std::nullopt;
} catch(const std::exception& e) {
SPDLOG_WARN("Type {}: Failed Parse but no TryParse found: {}", nameof::nameof_type<T>(), e.what());
} catch(...) {
SPDLOG_WARN("Type {}: Failed Parse but no TryParse found", nameof::nameof_type<T>());
}
return std::nullopt;
}
};
static constexpr auto Serialize(xmlpp::Element* element, const T& object) -> void {
element << object;
}
};
template <typename T>
struct Serialization<std::optional<T>> : SerializationBase<T> {
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> std::optional<T> {
return Serialization<T>::TryParse(element);
}
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<std::optional<T>> {
auto response = Serialization<T>::TryParse(element);
return response ? *response : std::nullopt;
}
static constexpr auto Serialize(xmlpp::Element* element, const T& object) -> void {
element << object;
}
};
template <typename... Ts>
struct Serialization<std::variant<Ts...>> : SerializationBase<> {
static constexpr auto StartCheck(xmlpp::Element* element) {
return true;
}
private:
template <typename T>
static constexpr auto FunctionForType(xmlpp::Element* element) {
return [=] -> std::optional<T> {
if(Serialization<T>::StartCheck(element)) {
return Serialization<T>::TryParse(element);
} else {
SPDLOG_DEBUG("StartCheck failed for type {}", nameof::nameof_type<T>());
return std::nullopt;
}
};
}
public:
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<std::variant<Ts...>> {
return utempl::FirstOf(utempl::Tuple{FunctionForType<Ts>(element)...}, std::optional<std::variant<Ts...>>{});
}
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) {
return [&]<typename... TTs>(utempl::TypeList<TTs...>) {
// operator* is safe because in or_else Parse returns Ts...[sizeof...(Ts) - 1] (not optional)
return *utempl::FirstOf(utempl::Tuple{FunctionForType<TTs>(element)...}, std::optional<std::variant<Ts...>>{})
.or_else([&] -> std::optional<std::variant<Ts...>> {
return Serialization<decltype(utempl::Get<sizeof...(Ts) - 1>(utempl::kTypeList<Ts...>))>::Parse(element);
});
}(utempl::TakeFrom<sizeof...(Ts) - 1>(utempl::kTypeList<Ts...>));
}
static constexpr auto Serialize(xmlpp::Element* element, const std::variant<Ts...>& object) -> void {
std::visit(
[&]<typename T>(const T& object) {
Serialization<T>::Serialize(element, object);
},
object);
}
};
template <>
struct Serialization<std::monostate> : SerializationBase<> {
static constexpr auto StartCheck(xmlpp::Element*) -> bool {
return true;
};
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element*) -> std::optional<std::monostate> {
return std::monostate{};
}
[[nodiscard]] static constexpr auto Parse(xmlpp::Element*) -> std::monostate {
return {};
}
static constexpr auto Serialize(xmlpp::Element*, const std::monostate&) -> void {
}
};
template <typename T>
struct Serialization<std::vector<T>> : SerializationBase<> {
static constexpr auto Parse(xmlpp::Element* element) -> std::vector<T> {
return element->get_children(Serialization<T>::kDefaultName) | std::views::transform([](xmlpp::Node* node) {
auto itemElement = dynamic_cast<xmlpp::Element*>(node);
if(!itemElement) {
throw serialization::ParsingError{"Can't convert xmlpp::Node to xmlpp::Element ]"};
}
return Serialization<T>::Parse(itemElement);
}) |
std::ranges::to<std::vector<T>>();
}
static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<std::vector<T>> {
auto chd = element->get_children(Serialization<T>::kDefaultName);
auto range = chd | std::views::transform([](xmlpp::Node* node) -> std::optional<T> {
auto itemElement = dynamic_cast<xmlpp::Element*>(node);
if(!itemElement) {
return std::nullopt;
}
return Serialization<T>::TryParse(itemElement);
});
std::vector<T> response;
response.reserve(chd.size());
for(auto& value : range) {
if(!value) {
return std::nullopt;
}
response.push_back(std::move(*value));
}
return response;
}
static constexpr auto Serialize(xmlpp::Element* node, const std::vector<T>& value) -> void {
for(const T& element : value) {
Serialization<T>::Serialize(node->add_child_element(Serialization<T>::kDefaultName), element);
}
}
};
} // namespace larra::xmpp