From b5c0417f14dffe4d322bf2702c017fcdda4c4d9b Mon Sep 17 00:00:00 2001 From: bqv Date: Sat, 19 Mar 2022 23:12:53 +0000 Subject: [PATCH] fluent interface for stanza generation --- xmpp/node.hh | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/xmpp/node.hh b/xmpp/node.hh index 6b40933..bc5d3c2 100644 --- a/xmpp/node.hh +++ b/xmpp/node.hh @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -101,6 +102,86 @@ namespace xml { } }; + namespace builder { + template struct overloaded : Ts... { using Ts::operator()...; }; + + class spec { + protected: + explicit spec(std::string_view tag): tag(tag) {} + + void child(spec& ch) { + children.push_back(ch); + } + + void attr(std::string k, std::string_view v) { + attributes[k] = v; + } + + inline void text(std::string_view s) { + children.push_back(std::string(s)); + } + + private: + const std::string tag; + std::map attributes; + std::vector> children; + + public: + std::shared_ptr build(xmpp_ctx_t *context) { + auto stanza = xmpp_stanza_new(context); + xmpp_stanza_set_name(stanza, tag.data()); + for (auto& at : attributes) { + xmpp_stanza_set_attribute(stanza, at.first.data(), at.second.data()); + } + for (auto& ch : children) { + std::visit(overloaded { + [&](spec& child) { + xmpp_stanza_add_child(stanza, child.build(context).get()); + }, + [&](std::string s) { + auto child = xmpp_stanza_new(context); + xmpp_stanza_set_text(child, s.data()); + xmpp_stanza_add_child(stanza, child); + xmpp_stanza_release(child); + } + }, ch); + } + return { stanza, &xmpp_stanza_release }; + } + }; + + struct body : public spec { + body() : body("") {} + body(std::string_view s) : spec("body") { + text(s); + } + }; + + struct message : public spec { + message() : spec("message") {} + + message& id(std::string_view s) { attr("id", s); return *this; } + message& from(std::string_view s) { attr("from", s); return *this; } + message& to(std::string_view s) { attr("to", s); return *this; } + message& type(std::string_view s) { attr("type", s); return *this; } + + message& body(builder::body b) { child(b); return *this; } + message& body(std::string_view s) { return body(builder::body(s)); } + }; + + struct presence : public spec { + presence() : spec("presence") {} + }; + + struct iq : public spec { + iq() : spec("iq") {} + }; + + struct error : public spec { + error() : spec("error") {} + }; + } + } #include "xep-0027.inl"