larra/library/include/larra/iq_error.hpp

152 lines
6 KiB
C++

#pragma once
#include <libxml++/libxml++.h>
#include <larra/utils.hpp>
#include <optional>
#include <variant>
namespace larra::xmpp::iq::error {
namespace impl {
struct IqBaseError : std::exception {};
// DO NOT MOVE TO ANOTHER NAMESPACE(where no heirs). VIA friend A FUNCTION IS ADDED THAT VIA ADL WILL BE SEARCHED FOR HEIRS
// C++20 modules very unstable in clangd :(
template <typename T>
struct IqErrorImpl : IqBaseError {
static constexpr auto kDefaultName = "error";
static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
static inline const auto kKebabCaseName = static_cast<std::string>(utils::ToKebabCaseName<T>());
static constexpr auto kErrorMessage = [] -> std::string_view {
static constexpr auto name = nameof::nameof_short_type<T>();
static constexpr auto str = [] {
return std::array{std::string_view{"Stanza IQ Error: "}, std::string_view{name}, std::string_view{"\0", 1}} | std::views::join;
};
static constexpr auto array = str() | std::ranges::to<utils::RangeToWrapper<std::array<char, std::ranges::distance(str())>>>();
return {array.data(), array.size() - 1};
}();
std::optional<std::string> by{};
std::string type;
// TODO(unknown): Add "optional text children" support for stanza error. Check "XML Stanzas" -> "Syntax" for more details
static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
if(not element) {
return std::nullopt;
}
auto by = element->get_attribute("by");
auto type = element->get_attribute("type");
if(not type) {
return std::nullopt;
}
auto node = element->get_first_child(kKebabCaseName);
if(not node) {
return std::nullopt;
}
T obj;
obj.type = type->get_value();
if(by) {
obj.by = std::optional{by->get_value()};
}
return obj;
}
static constexpr auto Parse(xmlpp::Element* element) -> T {
return TryParse(element).value();
}
friend constexpr auto operator<<(xmlpp::Element* element, const T& obj) -> void {
element->set_attribute("type", obj.type);
if(obj.by) {
element->set_attribute("by", *obj.by);
}
auto node = element->add_child_element(kKebabCaseName);
node->set_namespace_declaration(kDefaultNamespace);
}
constexpr auto operator==(const IqErrorImpl<T>&) const -> bool {
return true;
};
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
return kErrorMessage.data();
}
};
// Helper class to prevent parsing response stream into an expected return type if its name is an 'error'
struct UnknownIqError : IqBaseError {
static constexpr auto kDefaultName = "stream:error";
static constexpr std::string_view kErrorMessage = "Unknown XMPP stream error";
static constexpr auto TryParse(xmlpp::Element* element) {
return std::optional{UnknownIqError{}};
}
static constexpr auto Parse(xmlpp::Element* element) {
return TryParse(element).value();
}
friend constexpr auto operator<<(xmlpp::Element* element, const UnknownIqError& obj) -> void {
throw std::format("'{}' must never be written into the real stream!", kErrorMessage);
}
constexpr auto operator==(const UnknownIqError&) const -> bool {
return true;
};
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
return kErrorMessage.data();
}
};
struct BadRequest : IqErrorImpl<BadRequest> {};
struct Conflict : IqErrorImpl<Conflict> {};
struct FeatureNotImplemented : IqErrorImpl<FeatureNotImplemented> {};
struct Forbidden : IqErrorImpl<Forbidden> {};
struct Gone : IqErrorImpl<Gone> {};
struct InternalServerError : IqErrorImpl<InternalServerError> {};
struct ItemNotFound : IqErrorImpl<ItemNotFound> {};
struct JidMalformed : IqErrorImpl<JidMalformed> {};
struct NotAcceptable : IqErrorImpl<NotAcceptable> {};
struct NotAllowed : IqErrorImpl<NotAllowed> {};
struct NotAuthorized : IqErrorImpl<NotAuthorized> {};
struct PolicyViolation : IqErrorImpl<PolicyViolation> {};
struct RecipientUnavailable : IqErrorImpl<RecipientUnavailable> {};
struct Redirect : IqErrorImpl<Redirect> {};
struct RegistrationRequired : IqErrorImpl<RegistrationRequired> {};
struct RemoteServerNotFound : IqErrorImpl<RemoteServerNotFound> {};
struct RemoteServerTimeout : IqErrorImpl<RemoteServerTimeout> {};
struct ResourceConstraint : IqErrorImpl<ResourceConstraint> {};
struct ServiceUnavailable : IqErrorImpl<ServiceUnavailable> {};
struct SubscriptionRequired : IqErrorImpl<SubscriptionRequired> {};
struct UndefinedCondition : IqErrorImpl<UndefinedCondition> {};
struct UnexpectedRequest : IqErrorImpl<UnexpectedRequest> {};
} // namespace impl
using IqError = std::variant<impl::BadRequest,
impl::Conflict,
impl::FeatureNotImplemented,
impl::Forbidden,
impl::Gone,
impl::InternalServerError,
impl::ItemNotFound,
impl::JidMalformed,
impl::NotAcceptable,
impl::NotAllowed,
impl::NotAuthorized,
impl::PolicyViolation,
impl::RecipientUnavailable,
impl::Redirect,
impl::RegistrationRequired,
impl::RemoteServerNotFound,
impl::RemoteServerTimeout,
impl::ResourceConstraint,
impl::ServiceUnavailable,
impl::SubscriptionRequired,
impl::UndefinedCondition,
impl::UnexpectedRequest,
impl::UnknownIqError>;
static_assert(!std::is_same_v<typename std::variant_alternative_t<std::variant_size_v<IqError> - 1, IqError>, IqError>,
"'UnknownIqError' must be at the end of 'IqError' variant");
} // namespace larra::xmpp::iq::error