.
This commit is contained in:
parent
fe5b652240
commit
e7ca1d08e9
2 changed files with 149 additions and 41 deletions
|
@ -1,21 +1,81 @@
|
|||
#pragma once
|
||||
#include <libxml++/libxml++.h>
|
||||
|
||||
#include <larra/serialization/auto.hpp>
|
||||
#include <larra/utils.hpp>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
namespace larra::xmpp::stanza::error {
|
||||
|
||||
struct StanzaBaseError : std::exception {};
|
||||
namespace type {
|
||||
|
||||
// Not move
|
||||
template <typename Self>
|
||||
struct TypeBaseImpl {
|
||||
static constexpr auto TryParse(std::string_view value) {
|
||||
return value == Self::kName ? std::optional{Self{}} : std::nullopt;
|
||||
}
|
||||
static constexpr auto Parse(std::string_view value) {
|
||||
return value == Self::kName ? Self{} : throw std::runtime_error(std::format("{}::Parse error", nameof::nameof_type<Self>()));
|
||||
}
|
||||
friend constexpr auto ToString(const Self&) -> std::string {
|
||||
return Self::kName;
|
||||
}
|
||||
};
|
||||
|
||||
struct Modify : TypeBaseImpl<Modify> {
|
||||
static constexpr auto kName = "modify";
|
||||
} constexpr kModify{};
|
||||
|
||||
struct Cancel : TypeBaseImpl<Cancel> {
|
||||
static constexpr auto kName = "cancel";
|
||||
} constexpr kCancel{};
|
||||
|
||||
struct Auth : TypeBaseImpl<Auth> {
|
||||
static constexpr auto kName = "auth";
|
||||
} constexpr kAuth{};
|
||||
|
||||
struct Wait : TypeBaseImpl<Wait> {
|
||||
static constexpr auto kName = "wait";
|
||||
} constexpr kWait{};
|
||||
|
||||
} // namespace type
|
||||
using TypeVariant = std::variant<type::Modify, type::Cancel, type::Auth>;
|
||||
|
||||
struct Type : TypeVariant {
|
||||
using TypeVariant::variant;
|
||||
constexpr Type(TypeVariant variant) : TypeVariant(std::move(variant)) {
|
||||
}
|
||||
|
||||
friend constexpr auto ToString(const Type& value) -> std::string {
|
||||
return std::visit(
|
||||
[](const auto& value) {
|
||||
return ToString(value);
|
||||
},
|
||||
value);
|
||||
}
|
||||
};
|
||||
|
||||
struct StanzaErrorBase : 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 {
|
||||
template <typename T, typename Default>
|
||||
struct StanzaErrorImpl : StanzaErrorBase {
|
||||
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>());
|
||||
constexpr StanzaErrorImpl(std::optional<std::string> by, Type type) : by(std::move(by)), type(std::move(type)) {
|
||||
}
|
||||
constexpr StanzaErrorImpl() = default;
|
||||
struct FieldInfo {
|
||||
using Main = StanzaErrorImpl;
|
||||
using Info = serialization::MetaInfo<Main>;
|
||||
using Type = Type;
|
||||
static inline const std::string kName = "type";
|
||||
static inline const std::string kNamespace = "";
|
||||
};
|
||||
|
||||
static constexpr auto kErrorMessage = [] -> std::string_view {
|
||||
static constexpr auto name = nameof::nameof_short_type<T>();
|
||||
|
@ -27,37 +87,33 @@ struct StanzaErrorImpl : StanzaBaseError {
|
|||
}();
|
||||
|
||||
std::optional<std::string> by{};
|
||||
std::string type;
|
||||
Type type = Default{};
|
||||
|
||||
// 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) {
|
||||
if(!element) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto by = element->get_attribute("by");
|
||||
auto type = element->get_attribute("type");
|
||||
if(not type) {
|
||||
if(!type) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto node = element->get_first_child(kKebabCaseName);
|
||||
if(not node) {
|
||||
if(!node) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
T obj;
|
||||
obj.type = type->get_value();
|
||||
if(by) {
|
||||
obj.by = std::optional{by->get_value()};
|
||||
}
|
||||
return obj;
|
||||
return T{by ? std::optional{by->get_value()} : std::nullopt, //
|
||||
serialization::AttributeSerializer<Type, FieldInfo>::Parse(element)};
|
||||
}
|
||||
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);
|
||||
element->set_attribute("type", ToString(obj.type));
|
||||
if(obj.by) {
|
||||
element->set_attribute("by", *obj.by);
|
||||
}
|
||||
|
@ -65,7 +121,7 @@ struct StanzaErrorImpl : StanzaBaseError {
|
|||
auto node = element->add_child_element(kKebabCaseName);
|
||||
node->set_namespace_declaration(kDefaultNamespace);
|
||||
}
|
||||
constexpr auto operator==(const StanzaErrorImpl<T>&) const -> bool {
|
||||
constexpr auto operator==(const StanzaErrorImpl&) const -> bool {
|
||||
return true;
|
||||
};
|
||||
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
|
||||
|
@ -74,7 +130,7 @@ struct StanzaErrorImpl : StanzaBaseError {
|
|||
};
|
||||
|
||||
// Helper class to prevent parsing response stream into an expected return type if its name is an 'error'
|
||||
struct UnknownStanzaError : StanzaBaseError {
|
||||
struct UnknownStanzaError : StanzaErrorBase {
|
||||
static constexpr auto kDefaultName = "stream:error";
|
||||
static constexpr std::string_view kErrorMessage = "Unknown XMPP stream error";
|
||||
|
||||
|
@ -95,28 +151,72 @@ struct UnknownStanzaError : StanzaBaseError {
|
|||
}
|
||||
};
|
||||
|
||||
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> {};
|
||||
struct BadRequest : StanzaErrorImpl<BadRequest, type::Modify> {
|
||||
using StanzaErrorImpl<BadRequest, type::Modify>::StanzaErrorImpl;
|
||||
};
|
||||
struct Conflict : StanzaErrorImpl<Conflict, type::Cancel> {
|
||||
using StanzaErrorImpl<Conflict, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct FeatureNotImplemented : StanzaErrorImpl<FeatureNotImplemented, type::Cancel> {
|
||||
using StanzaErrorImpl<FeatureNotImplemented, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct Forbidden : StanzaErrorImpl<Forbidden, type::Auth> {
|
||||
using StanzaErrorImpl<Forbidden, type::Auth>::StanzaErrorImpl;
|
||||
};
|
||||
struct Gone : StanzaErrorImpl<Gone, type::Cancel> {
|
||||
using StanzaErrorImpl<Gone, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct InternalServerError : StanzaErrorImpl<InternalServerError, type::Cancel> {
|
||||
using StanzaErrorImpl<InternalServerError, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct ItemNotFound : StanzaErrorImpl<ItemNotFound, type::Cancel> {
|
||||
using StanzaErrorImpl<ItemNotFound, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct JidMalformed : StanzaErrorImpl<JidMalformed, type::Modify> {
|
||||
using StanzaErrorImpl<JidMalformed, type::Modify>::StanzaErrorImpl;
|
||||
};
|
||||
struct NotAcceptable : StanzaErrorImpl<NotAcceptable, type::Modify> {
|
||||
using StanzaErrorImpl<NotAcceptable, type::Modify>::StanzaErrorImpl;
|
||||
};
|
||||
struct NotAllowed : StanzaErrorImpl<NotAllowed, type::Cancel> {
|
||||
using StanzaErrorImpl<NotAllowed, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct NotAuthorized : StanzaErrorImpl<NotAuthorized, type::Auth> {
|
||||
using StanzaErrorImpl<NotAuthorized, type::Auth>::StanzaErrorImpl;
|
||||
};
|
||||
struct PolicyViolation : StanzaErrorImpl<PolicyViolation, type::Modify> {
|
||||
using StanzaErrorImpl<PolicyViolation, type::Modify>::StanzaErrorImpl;
|
||||
};
|
||||
struct RecipientUnavailable : StanzaErrorImpl<RecipientUnavailable, type::Wait> {
|
||||
using StanzaErrorImpl<RecipientUnavailable, type::Wait>::StanzaErrorImpl;
|
||||
};
|
||||
struct Redirect : StanzaErrorImpl<Redirect, type::Modify> {
|
||||
using StanzaErrorImpl<Redirect, type::Modify>::StanzaErrorImpl;
|
||||
};
|
||||
struct RegistrationRequired : StanzaErrorImpl<RegistrationRequired, type::Auth> {
|
||||
using StanzaErrorImpl<RegistrationRequired, type::Auth>::StanzaErrorImpl;
|
||||
};
|
||||
struct RemoteServerNotFound : StanzaErrorImpl<RemoteServerNotFound, type::Cancel> {
|
||||
using StanzaErrorImpl<RemoteServerNotFound, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct RemoteServerTimeout : StanzaErrorImpl<RemoteServerTimeout, type::Wait> {
|
||||
using StanzaErrorImpl<RemoteServerTimeout, type::Wait>::StanzaErrorImpl;
|
||||
};
|
||||
struct ResourceConstraint : StanzaErrorImpl<ResourceConstraint, type::Wait> {
|
||||
using StanzaErrorImpl<ResourceConstraint, type::Wait>::StanzaErrorImpl;
|
||||
};
|
||||
struct ServiceUnavailable : StanzaErrorImpl<ServiceUnavailable, type::Cancel> {
|
||||
using StanzaErrorImpl<ServiceUnavailable, type::Cancel>::StanzaErrorImpl;
|
||||
};
|
||||
struct SubscriptionRequired : StanzaErrorImpl<SubscriptionRequired, type::Auth> {
|
||||
using StanzaErrorImpl<SubscriptionRequired, type::Auth>::StanzaErrorImpl;
|
||||
};
|
||||
struct UndefinedCondition : StanzaErrorImpl<UndefinedCondition, type::Modify> {
|
||||
using StanzaErrorImpl<UndefinedCondition, type::Modify>::StanzaErrorImpl;
|
||||
};
|
||||
struct UnexpectedRequest : StanzaErrorImpl<UnexpectedRequest, type::Modify> {
|
||||
using StanzaErrorImpl<UnexpectedRequest, type::Modify>::StanzaErrorImpl;
|
||||
};
|
||||
|
||||
using StanzaError = std::variant<BadRequest,
|
||||
Conflict,
|
||||
|
@ -146,3 +246,11 @@ static_assert(std::is_same_v<typename std::variant_alternative_t<std::variant_si
|
|||
"'UnknownStanzaError' must be at the end of 'StanzaError' variant");
|
||||
|
||||
} // namespace larra::xmpp::stanza::error
|
||||
|
||||
namespace larra::xmpp {
|
||||
|
||||
template <typename Info>
|
||||
struct serialization::AttributeSerializer<stanza::error::Type, Info>
|
||||
: serialization::AttributeSerializer<stanza::error::TypeVariant, Info> {};
|
||||
|
||||
} // namespace larra::xmpp
|
||||
|
|
|
@ -87,7 +87,7 @@ TEST(IQ, IqErrThrowVisitorThrow) {
|
|||
static constexpr auto throwErrMsg = "Stanza IQ Error: Forbidden";
|
||||
try {
|
||||
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{visitorErrMsg}), std::move(iqRes));
|
||||
} catch(const stanza::error::StanzaBaseError& err) {
|
||||
} catch(const stanza::error::StanzaErrorBase& err) {
|
||||
ASSERT_STREQ(throwErrMsg, err.what());
|
||||
return;
|
||||
} catch(const std::runtime_error& err) {
|
||||
|
@ -114,7 +114,7 @@ TEST(IQ, IqErrThrowVisitorThrowGet) {
|
|||
static constexpr auto throwErrMsg = "Test Error: 'Get' is an invalid type for IQ result. Expected 'Result' or 'Error'";
|
||||
try {
|
||||
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{"Test Error"}), std::move(iqRes));
|
||||
} catch(const stanza::error::StanzaBaseError& err) {
|
||||
} catch(const stanza::error::StanzaErrorBase& err) {
|
||||
ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw";
|
||||
} catch(const std::runtime_error& err) {
|
||||
ASSERT_STREQ(throwErrMsg, err.what());
|
||||
|
@ -141,7 +141,7 @@ TEST(IQ, IqErrThrowVisitorThrowSet) {
|
|||
static constexpr auto throwErrMsg = "Test Error: 'Set' is an invalid type for IQ result. Expected 'Result' or 'Error'";
|
||||
try {
|
||||
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{"Test Error"}), std::move(iqRes));
|
||||
} catch(const stanza::error::StanzaBaseError& err) {
|
||||
} catch(const stanza::error::StanzaErrorBase& err) {
|
||||
ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw";
|
||||
} catch(const std::runtime_error& err) {
|
||||
ASSERT_STREQ(throwErrMsg, err.what());
|
||||
|
|
Loading…
Reference in a new issue