Compare commits

..

No commits in common. "8019371d466d6bcd82a9cf1d108946b378bdf054" and "fe5b6522401e4a93538196660126a9c17a6f7ba2" have entirely different histories.

3 changed files with 104 additions and 178 deletions

View file

@ -30,43 +30,77 @@ struct Body {
namespace type { namespace type {
template <typename T> struct Chat {
struct TypeImpl { static constexpr auto TryParse(std::string_view value) -> std::optional<Chat> {
static constexpr auto TryParse(std::string_view value) -> std::optional<T> { return value == "chat" ? std::optional{Chat{}} : std::nullopt;
return value == T::kName ? std::optional{T{}} : std::nullopt;
} }
friend constexpr auto ToString(const T&) -> std::string { friend constexpr auto ToString(const Chat&) -> std::string {
return T::kName; return "chat";
} }
friend constexpr auto operator==(const T&, const T&) -> bool { constexpr auto operator==(const Chat&) const -> bool {
return true; return true;
}; };
}; };
struct Chat : TypeImpl<Chat> { static constexpr auto kChat = Chat{};
static constexpr auto kName = "chat";
} constexpr kChat{};
struct Error : TypeImpl<Error> { struct Error {
static constexpr auto kName = "error"; static constexpr auto TryParse(std::string_view value) -> std::optional<Error> {
} constexpr kError{}; return value == "error" ? std::optional{Error{}} : std::nullopt;
struct GroupChat : TypeImpl<GroupChat> {
static constexpr auto kName = "groupchat";
} constexpr kGroupChat{};
struct Headline : TypeImpl<Headline> {
static constexpr auto kName = "headline";
} constexpr kHeadline{};
struct Normal : TypeImpl<Normal> {
static constexpr auto kName = "normal";
static constexpr auto Parse(std::string_view value) -> Normal {
return value == kName ? Normal{}
: throw std::runtime_error(
std::format(R"(message::type::Normal Parsing error: [ expected "normal" but "{}" found ])", value));
} }
} constexpr kNormal; friend constexpr auto ToString(const Error&) -> std::string {
return "error";
}
constexpr auto operator==(const Error&) const -> bool {
return true;
};
};
static constexpr auto kError = Error{};
struct GroupChat {
static constexpr auto TryParse(std::string_view value) -> std::optional<GroupChat> {
return value == "groupchat" ? std::optional{GroupChat{}} : std::nullopt;
}
friend constexpr auto ToString(const GroupChat&) -> std::string {
return "groupchat";
}
constexpr auto operator==(const GroupChat&) const -> bool {
return true;
};
};
static constexpr auto kGroupChat = GroupChat{};
struct Headline {
static constexpr auto TryParse(std::string_view value) -> std::optional<Headline> {
return value == "headline" ? std::optional{Headline{}} : std::nullopt;
}
friend constexpr auto ToString(const Headline&) -> std::string {
return "headline";
}
constexpr auto operator==(const Headline&) const -> bool {
return true;
};
};
static constexpr auto kHeadline = Headline{};
struct Normal {
static constexpr auto Parse(std::string_view value) -> Normal {
return value == "normal" ? Normal{}
: throw std::runtime_error(
std::format(R"(message::type::Normal Parsing error: [ expected "normal" but "{}" found ])", value));
}
friend constexpr auto ToString(const Normal&) -> std::string {
return "normal";
}
constexpr auto operator==(const Normal&) const -> bool {
return true;
};
};
static constexpr auto kNormal = Normal{};
} // namespace type } // namespace type

View file

@ -1,81 +1,21 @@
#pragma once #pragma once
#include <libxml++/libxml++.h> #include <libxml++/libxml++.h>
#include <larra/serialization/auto.hpp>
#include <larra/utils.hpp> #include <larra/utils.hpp>
#include <optional> #include <optional>
#include <variant> #include <variant>
namespace larra::xmpp::stanza::error { namespace larra::xmpp::stanza::error {
namespace type { struct StanzaBaseError : std::exception {};
// 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 // 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 :( // C++20 modules very unstable in clangd :(
template <typename T, typename Default> template <typename T>
struct StanzaErrorImpl : StanzaErrorBase { struct StanzaErrorImpl : StanzaBaseError {
static constexpr auto kDefaultName = "error"; static constexpr auto kDefaultName = "error";
static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas"; static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-stanzas";
static inline const auto kKebabCaseName = static_cast<std::string>(utils::ToKebabCaseName<T>()); 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 kErrorMessage = [] -> std::string_view {
static constexpr auto name = nameof::nameof_short_type<T>(); static constexpr auto name = nameof::nameof_short_type<T>();
@ -87,33 +27,37 @@ struct StanzaErrorImpl : StanzaErrorBase {
}(); }();
std::optional<std::string> by{}; std::optional<std::string> by{};
Type type = Default{}; std::string type;
// TODO(unknown): Add "optional text children" support for stanza error. Check "XML Stanzas" -> "Syntax" for more details // 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> { static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
if(!element) { if(not element) {
return std::nullopt; return std::nullopt;
} }
auto by = element->get_attribute("by"); auto by = element->get_attribute("by");
auto type = element->get_attribute("type"); auto type = element->get_attribute("type");
if(!type) { if(not type) {
return std::nullopt; return std::nullopt;
} }
auto node = element->get_first_child(kKebabCaseName); auto node = element->get_first_child(kKebabCaseName);
if(!node) { if(not node) {
return std::nullopt; return std::nullopt;
} }
return T{by ? std::optional{by->get_value()} : std::nullopt, // T obj;
serialization::AttributeSerializer<Type, FieldInfo>::Parse(element)}; obj.type = type->get_value();
if(by) {
obj.by = std::optional{by->get_value()};
}
return obj;
} }
static constexpr auto Parse(xmlpp::Element* element) -> T { static constexpr auto Parse(xmlpp::Element* element) -> T {
return TryParse(element).value(); return TryParse(element).value();
} }
friend constexpr auto operator<<(xmlpp::Element* element, const T& obj) -> void { friend constexpr auto operator<<(xmlpp::Element* element, const T& obj) -> void {
element->set_attribute("type", ToString(obj.type)); element->set_attribute("type", obj.type);
if(obj.by) { if(obj.by) {
element->set_attribute("by", *obj.by); element->set_attribute("by", *obj.by);
} }
@ -121,7 +65,7 @@ struct StanzaErrorImpl : StanzaErrorBase {
auto node = element->add_child_element(kKebabCaseName); auto node = element->add_child_element(kKebabCaseName);
node->set_namespace_declaration(kDefaultNamespace); node->set_namespace_declaration(kDefaultNamespace);
} }
constexpr auto operator==(const StanzaErrorImpl&) const -> bool { constexpr auto operator==(const StanzaErrorImpl<T>&) const -> bool {
return true; return true;
}; };
[[nodiscard]] constexpr auto what() const noexcept -> const char* override { [[nodiscard]] constexpr auto what() const noexcept -> const char* override {
@ -130,7 +74,7 @@ struct StanzaErrorImpl : StanzaErrorBase {
}; };
// Helper class to prevent parsing response stream into an expected return type if its name is an 'error' // Helper class to prevent parsing response stream into an expected return type if its name is an 'error'
struct UnknownStanzaError : StanzaErrorBase { struct UnknownStanzaError : StanzaBaseError {
static constexpr auto kDefaultName = "stream:error"; static constexpr auto kDefaultName = "stream:error";
static constexpr std::string_view kErrorMessage = "Unknown XMPP stream error"; static constexpr std::string_view kErrorMessage = "Unknown XMPP stream error";
@ -151,72 +95,28 @@ struct UnknownStanzaError : StanzaErrorBase {
} }
}; };
struct BadRequest : StanzaErrorImpl<BadRequest, type::Modify> { struct BadRequest : StanzaErrorImpl<BadRequest> {};
using StanzaErrorImpl<BadRequest, type::Modify>::StanzaErrorImpl; struct Conflict : StanzaErrorImpl<Conflict> {};
}; struct FeatureNotImplemented : StanzaErrorImpl<FeatureNotImplemented> {};
struct Conflict : StanzaErrorImpl<Conflict, type::Cancel> { struct Forbidden : StanzaErrorImpl<Forbidden> {};
using StanzaErrorImpl<Conflict, type::Cancel>::StanzaErrorImpl; struct Gone : StanzaErrorImpl<Gone> {};
}; struct InternalServerError : StanzaErrorImpl<InternalServerError> {};
struct FeatureNotImplemented : StanzaErrorImpl<FeatureNotImplemented, type::Cancel> { struct ItemNotFound : StanzaErrorImpl<ItemNotFound> {};
using StanzaErrorImpl<FeatureNotImplemented, type::Cancel>::StanzaErrorImpl; struct JidMalformed : StanzaErrorImpl<JidMalformed> {};
}; struct NotAcceptable : StanzaErrorImpl<NotAcceptable> {};
struct Forbidden : StanzaErrorImpl<Forbidden, type::Auth> { struct NotAllowed : StanzaErrorImpl<NotAllowed> {};
using StanzaErrorImpl<Forbidden, type::Auth>::StanzaErrorImpl; struct NotAuthorized : StanzaErrorImpl<NotAuthorized> {};
}; struct PolicyViolation : StanzaErrorImpl<PolicyViolation> {};
struct Gone : StanzaErrorImpl<Gone, type::Cancel> { struct RecipientUnavailable : StanzaErrorImpl<RecipientUnavailable> {};
using StanzaErrorImpl<Gone, type::Cancel>::StanzaErrorImpl; struct Redirect : StanzaErrorImpl<Redirect> {};
}; struct RegistrationRequired : StanzaErrorImpl<RegistrationRequired> {};
struct InternalServerError : StanzaErrorImpl<InternalServerError, type::Cancel> { struct RemoteServerNotFound : StanzaErrorImpl<RemoteServerNotFound> {};
using StanzaErrorImpl<InternalServerError, type::Cancel>::StanzaErrorImpl; struct RemoteServerTimeout : StanzaErrorImpl<RemoteServerTimeout> {};
}; struct ResourceConstraint : StanzaErrorImpl<ResourceConstraint> {};
struct ItemNotFound : StanzaErrorImpl<ItemNotFound, type::Cancel> { struct ServiceUnavailable : StanzaErrorImpl<ServiceUnavailable> {};
using StanzaErrorImpl<ItemNotFound, type::Cancel>::StanzaErrorImpl; struct SubscriptionRequired : StanzaErrorImpl<SubscriptionRequired> {};
}; struct UndefinedCondition : StanzaErrorImpl<UndefinedCondition> {};
struct JidMalformed : StanzaErrorImpl<JidMalformed, type::Modify> { struct UnexpectedRequest : StanzaErrorImpl<UnexpectedRequest> {};
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, using StanzaError = std::variant<BadRequest,
Conflict, Conflict,
@ -246,11 +146,3 @@ static_assert(std::is_same_v<typename std::variant_alternative_t<std::variant_si
"'UnknownStanzaError' must be at the end of 'StanzaError' variant"); "'UnknownStanzaError' must be at the end of 'StanzaError' variant");
} // namespace larra::xmpp::stanza::error } // 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

View file

@ -87,7 +87,7 @@ TEST(IQ, IqErrThrowVisitorThrow) {
static constexpr auto throwErrMsg = "Stanza IQ Error: Forbidden"; static constexpr auto throwErrMsg = "Stanza IQ Error: Forbidden";
try { try {
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{visitorErrMsg}), std::move(iqRes)); std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{visitorErrMsg}), std::move(iqRes));
} catch(const stanza::error::StanzaErrorBase& err) { } catch(const stanza::error::StanzaBaseError& err) {
ASSERT_STREQ(throwErrMsg, err.what()); ASSERT_STREQ(throwErrMsg, err.what());
return; return;
} catch(const std::runtime_error& err) { } 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'"; static constexpr auto throwErrMsg = "Test Error: 'Get' is an invalid type for IQ result. Expected 'Result' or 'Error'";
try { try {
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{"Test Error"}), std::move(iqRes)); std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{"Test Error"}), std::move(iqRes));
} catch(const stanza::error::StanzaErrorBase& err) { } catch(const stanza::error::StanzaBaseError& err) {
ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw"; ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw";
} catch(const std::runtime_error& err) { } catch(const std::runtime_error& err) {
ASSERT_STREQ(throwErrMsg, err.what()); 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'"; static constexpr auto throwErrMsg = "Test Error: 'Set' is an invalid type for IQ result. Expected 'Result' or 'Error'";
try { try {
std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{"Test Error"}), std::move(iqRes)); std::visit(utempl::Overloaded([](iq::Result<SomeStruct> r) {}, IqErrThrowVisitor{"Test Error"}), std::move(iqRes));
} catch(const stanza::error::StanzaErrorBase& err) { } catch(const stanza::error::StanzaBaseError& err) {
ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw"; ASSERT_TRUE(false) << "\tERROR: Invalid throw type throw";
} catch(const std::runtime_error& err) { } catch(const std::runtime_error& err) {
ASSERT_STREQ(throwErrMsg, err.what()); ASSERT_STREQ(throwErrMsg, err.what());