148 lines
6 KiB
C++
148 lines
6 KiB
C++
#pragma once
|
|
#include <libxml++/libxml++.h>
|
|
|
|
#include <larra/utils.hpp>
|
|
#include <optional>
|
|
#include <variant>
|
|
|
|
namespace larra::xmpp::stanza::error {
|
|
|
|
struct StanzaBaseError : 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 StanzaErrorImpl : StanzaBaseError {
|
|
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 StanzaErrorImpl<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 UnknownStanzaError : StanzaBaseError {
|
|
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{UnknownStanzaError{}};
|
|
}
|
|
static constexpr auto Parse(xmlpp::Element* element) {
|
|
return TryParse(element).value();
|
|
}
|
|
friend constexpr auto operator<<(xmlpp::Element* element, const UnknownStanzaError& obj) -> void {
|
|
throw std::runtime_error{std::format("'{}' must never be written into the real stream!", kErrorMessage)};
|
|
}
|
|
constexpr auto operator==(const UnknownStanzaError&) const -> bool {
|
|
return true;
|
|
};
|
|
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
|
|
return kErrorMessage.data();
|
|
}
|
|
};
|
|
|
|
struct BadRequest : StanzaErrorImpl<BadRequest> {};
|
|
struct Conflict : StanzaErrorImpl<Conflict> {};
|
|
struct FeatureNotImplemented : StanzaErrorImpl<FeatureNotImplemented> {};
|
|
struct Forbidden : StanzaErrorImpl<Forbidden> {};
|
|
struct Gone : StanzaErrorImpl<Gone> {};
|
|
struct InternalServerError : StanzaErrorImpl<InternalServerError> {};
|
|
struct ItemNotFound : StanzaErrorImpl<ItemNotFound> {};
|
|
struct JidMalformed : StanzaErrorImpl<JidMalformed> {};
|
|
struct NotAcceptable : StanzaErrorImpl<NotAcceptable> {};
|
|
struct NotAllowed : StanzaErrorImpl<NotAllowed> {};
|
|
struct NotAuthorized : StanzaErrorImpl<NotAuthorized> {};
|
|
struct PolicyViolation : StanzaErrorImpl<PolicyViolation> {};
|
|
struct RecipientUnavailable : StanzaErrorImpl<RecipientUnavailable> {};
|
|
struct Redirect : StanzaErrorImpl<Redirect> {};
|
|
struct RegistrationRequired : StanzaErrorImpl<RegistrationRequired> {};
|
|
struct RemoteServerNotFound : StanzaErrorImpl<RemoteServerNotFound> {};
|
|
struct RemoteServerTimeout : StanzaErrorImpl<RemoteServerTimeout> {};
|
|
struct ResourceConstraint : StanzaErrorImpl<ResourceConstraint> {};
|
|
struct ServiceUnavailable : StanzaErrorImpl<ServiceUnavailable> {};
|
|
struct SubscriptionRequired : StanzaErrorImpl<SubscriptionRequired> {};
|
|
struct UndefinedCondition : StanzaErrorImpl<UndefinedCondition> {};
|
|
struct UnexpectedRequest : StanzaErrorImpl<UnexpectedRequest> {};
|
|
|
|
using StanzaError = std::variant<BadRequest,
|
|
Conflict,
|
|
FeatureNotImplemented,
|
|
Forbidden,
|
|
Gone,
|
|
InternalServerError,
|
|
ItemNotFound,
|
|
JidMalformed,
|
|
NotAcceptable,
|
|
NotAllowed,
|
|
NotAuthorized,
|
|
PolicyViolation,
|
|
RecipientUnavailable,
|
|
Redirect,
|
|
RegistrationRequired,
|
|
RemoteServerNotFound,
|
|
RemoteServerTimeout,
|
|
ResourceConstraint,
|
|
ServiceUnavailable,
|
|
SubscriptionRequired,
|
|
UndefinedCondition,
|
|
UnexpectedRequest,
|
|
UnknownStanzaError>;
|
|
|
|
static_assert(std::is_same_v<typename std::variant_alternative_t<std::variant_size_v<StanzaError> - 1, StanzaError>, UnknownStanzaError>,
|
|
"'UnknownStanzaError' must be at the end of 'StanzaError' variant");
|
|
|
|
} // namespace larra::xmpp::stanza::error
|