larra/library/include/larra/serialization.hpp

192 lines
5.9 KiB
C++

#pragma once
#include <libxml++/libxml++.h>
#include <spdlog/spdlog.h>
#include <nameof.hpp>
#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 std::runtime_error("StartCheck failed");
}
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(
[&](const auto& object) {
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 {
}
};
} // namespace larra::xmpp