From e7ca1d08e91d6f6a9ae2dd13ee84c10855a73f08 Mon Sep 17 00:00:00 2001 From: sha512sum Date: Tue, 24 Dec 2024 21:50:40 +1100 Subject: [PATCH] . --- library/include/larra/stanza_error.hpp | 184 ++++++++++++++++++++----- tests/iq.cpp | 6 +- 2 files changed, 149 insertions(+), 41 deletions(-) diff --git a/library/include/larra/stanza_error.hpp b/library/include/larra/stanza_error.hpp index 9931ac1..5ba6e23 100644 --- a/library/include/larra/stanza_error.hpp +++ b/library/include/larra/stanza_error.hpp @@ -1,21 +1,81 @@ #pragma once #include +#include #include #include #include namespace larra::xmpp::stanza::error { -struct StanzaBaseError : std::exception {}; +namespace type { + +// Not move +template +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())); + } + friend constexpr auto ToString(const Self&) -> std::string { + return Self::kName; + } +}; + +struct Modify : TypeBaseImpl { + static constexpr auto kName = "modify"; +} constexpr kModify{}; + +struct Cancel : TypeBaseImpl { + static constexpr auto kName = "cancel"; +} constexpr kCancel{}; + +struct Auth : TypeBaseImpl { + static constexpr auto kName = "auth"; +} constexpr kAuth{}; + +struct Wait : TypeBaseImpl { + static constexpr auto kName = "wait"; +} constexpr kWait{}; + +} // namespace type +using TypeVariant = std::variant; + +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 -struct StanzaErrorImpl : StanzaBaseError { +template +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(utils::ToKebabCaseName()); + constexpr StanzaErrorImpl(std::optional by, Type type) : by(std::move(by)), type(std::move(type)) { + } + constexpr StanzaErrorImpl() = default; + struct FieldInfo { + using Main = StanzaErrorImpl; + using Info = serialization::MetaInfo
; + 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(); @@ -27,37 +87,33 @@ struct StanzaErrorImpl : StanzaBaseError { }(); std::optional 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 { - 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::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&) 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 {}; -struct Conflict : StanzaErrorImpl {}; -struct FeatureNotImplemented : StanzaErrorImpl {}; -struct Forbidden : StanzaErrorImpl {}; -struct Gone : StanzaErrorImpl {}; -struct InternalServerError : StanzaErrorImpl {}; -struct ItemNotFound : StanzaErrorImpl {}; -struct JidMalformed : StanzaErrorImpl {}; -struct NotAcceptable : StanzaErrorImpl {}; -struct NotAllowed : StanzaErrorImpl {}; -struct NotAuthorized : StanzaErrorImpl {}; -struct PolicyViolation : StanzaErrorImpl {}; -struct RecipientUnavailable : StanzaErrorImpl {}; -struct Redirect : StanzaErrorImpl {}; -struct RegistrationRequired : StanzaErrorImpl {}; -struct RemoteServerNotFound : StanzaErrorImpl {}; -struct RemoteServerTimeout : StanzaErrorImpl {}; -struct ResourceConstraint : StanzaErrorImpl {}; -struct ServiceUnavailable : StanzaErrorImpl {}; -struct SubscriptionRequired : StanzaErrorImpl {}; -struct UndefinedCondition : StanzaErrorImpl {}; -struct UnexpectedRequest : StanzaErrorImpl {}; +struct BadRequest : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct Conflict : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct FeatureNotImplemented : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct Forbidden : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct Gone : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct InternalServerError : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct ItemNotFound : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct JidMalformed : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct NotAcceptable : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct NotAllowed : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct NotAuthorized : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct PolicyViolation : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct RecipientUnavailable : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct Redirect : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct RegistrationRequired : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct RemoteServerNotFound : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct RemoteServerTimeout : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct ResourceConstraint : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct ServiceUnavailable : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct SubscriptionRequired : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct UndefinedCondition : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; +struct UnexpectedRequest : StanzaErrorImpl { + using StanzaErrorImpl::StanzaErrorImpl; +}; using StanzaError = std::variant +struct serialization::AttributeSerializer + : serialization::AttributeSerializer {}; + +} // namespace larra::xmpp diff --git a/tests/iq.cpp b/tests/iq.cpp index 3545e51..1ea35bc 100644 --- a/tests/iq.cpp +++ b/tests/iq.cpp @@ -87,7 +87,7 @@ TEST(IQ, IqErrThrowVisitorThrow) { static constexpr auto throwErrMsg = "Stanza IQ Error: Forbidden"; try { std::visit(utempl::Overloaded([](iq::Result 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 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 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());