192 lines
5.9 KiB
C++
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
|