diff options
author | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2016-04-11 11:54:22 +0900 |
---|---|---|
committer | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2016-09-07 22:17:51 +0900 |
commit | f09b42ca73759207c7d63587c0d41c09ad691c47 (patch) | |
tree | 54422a0a3efcbea20cf5d545ef6ffb77e1f67ca6 /events | |
parent | 6aee89052be72b57fe4d93f5576bdf99293f5953 (diff) | |
download | libquotient-f09b42ca73759207c7d63587c0d41c09ad691c47.tar.gz libquotient-f09b42ca73759207c7d63587c0d41c09ad691c47.zip |
A generic lookup(), and its usage in Event and RoomMessageEvent
Feel free to use whenever you need to convert another JSON key to some C++ object, or dispatch anything based on a JSON key.
Diffstat (limited to 'events')
-rw-r--r-- | events/event.cpp | 34 | ||||
-rw-r--r-- | events/event.h | 48 | ||||
-rw-r--r-- | events/roommessageevent.cpp | 72 |
3 files changed, 91 insertions, 63 deletions
diff --git a/events/event.cpp b/events/event.cpp index c7a94e9e..95084ddb 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -90,27 +90,19 @@ Event* make(const QJsonObject& obj) Event* Event::fromJson(const QJsonObject& obj) { - struct Factory { - QString type; - Event* (*make)(const QJsonObject& obj); - }; - const Factory evTypes[] { - { "m.room.message", make<RoomMessageEvent> }, - { "m.room.name", make<RoomNameEvent> }, - { "m.room.aliases", make<RoomAliasesEvent> }, - { "m.room.canonical_alias", make<RoomCanonicalAliasEvent> }, - { "m.room.member", make<RoomMemberEvent> }, - { "m.room.topic", make<RoomTopicEvent> }, - { "m.typing", make<TypingEvent> }, - { "m.receipt", make<ReceiptEvent> }, - // Insert new types before this line - }; - for (auto e: evTypes) - { - if (obj["type"].toString() == e.type) - return e.make(obj); - } - return UnknownEvent::fromJson(obj); + auto delegate = lookup(obj.value("type").toString(), + "m.room.message", make<RoomMessageEvent>, + "m.room.name", make<RoomNameEvent>, + "m.room.aliases", make<RoomAliasesEvent>, + "m.room.canonical_alias", make<RoomCanonicalAliasEvent>, + "m.room.member", make<RoomMemberEvent>, + "m.room.topic", make<RoomTopicEvent>, + "m.typing", make<TypingEvent>, + "m.receipt", make<ReceiptEvent>, + /* Insert new event types BEFORE this line */ + make<UnknownEvent> + ); + return delegate(obj); } bool Event::parseJson(const QJsonObject& obj) diff --git a/events/event.h b/events/event.h index d9316747..a66f5e68 100644 --- a/events/event.h +++ b/events/event.h @@ -83,6 +83,54 @@ namespace QMatrixClient } ); } + + /** + * @brief Lookup a value by a key in a varargs list + * + * The below overloaded function template takes the value of its first + * argument (selector) as a key and searches for it in the key-value map + * passed in a varargs list (every next pair of arguments forms a key-value + * pair). If a match is found, the respective value is returned; otherwise, + * the last value (fallback) is returned. + * + * All options should be of the same type or implicitly castable to the + * type of the first option. Note that pointers to methods of different + * classes are of different object types, in particular. + * + * Below is an example of usage to select a parser depending on contents of + * a JSON object: + * {@code + * auto parser = lookup(obj.value["type"].toString(), + * "type1", fn1, + * "type2", fn2, + * fallbackFn); + * parser(obj); + * } + * + * The implementation is based on tail recursion; every recursion step + * removes 2 arguments (match and option). There's no selector value for the + * fallback option (the last one); therefore, the total number of lookup() + * arguments should be even: selector + n key-value pairs + fallback + * + * @note Beware of calling lookup() with a <code>const char*</code> selector + * (the first parameter) - most likely it won't do what you expect because + * of shallow comparison. + */ + template <typename ValueT, typename SelectorT, typename KeyT, typename... Ts> + ValueT lookup(SelectorT selector, KeyT key, ValueT value, Ts... remainingMapping) + { + if( selector == key ) + return value; + + // Drop the failed key-value pair and recurse with 2 arguments less. + return lookup(selector, remainingMapping...); + } + + template <typename SelectorT, typename ValueT> + ValueT lookup(SelectorT/*unused*/, ValueT fallback) + { + return fallback; + } } #endif // QMATRIXCLIENT_EVENT_H diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index 09465238..d6166dad 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -72,25 +72,35 @@ Base* RoomMessageEvent::content() const return d->content; } -template <class ContentT> -Base* make(const QJsonObject& json) +using ContentPair = std::pair<MessageEventType, MessageEventContent::Base*>; + +template <MessageEventType EnumType, typename ContentT> +ContentPair make(const QJsonObject& json) { - return new ContentT(json); + return { EnumType, new ContentT(json) }; } -Base* makeVideoContent(const QJsonObject& json) +ContentPair makeVideo(const QJsonObject& json) { auto c = new VideoContent(json); // Only for m.video, the spec puts a thumbnail inside "info" JSON key. Once // this is fixed, VideoContent creation will switch to make<>(). const QJsonObject infoJson = json["info"].toObject(); if (infoJson.contains("thumbnail_url")) + { c->thumbnail = ImageInfo(infoJson["thumbnail_url"].toString(), - infoJson["thumbnail_info"].toObject()); - - return c; + infoJson["thumbnail_info"].toObject()); + } + return { MessageEventType::Video, c }; }; +ContentPair makeUnknown(const QJsonObject& json) +{ + qDebug() << "RoomMessageEvent: couldn't resolve msgtype, JSON follows:"; + qDebug() << json; + return { MessageEventType::Unknown, new Base }; +} + RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj) { RoomMessageEvent* e = new RoomMessageEvent(); @@ -108,41 +118,19 @@ RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj) { e->d->plainBody = content["body"].toString(); - struct Factory - { - QString jsonTag; - MessageEventType enumTag; - Base*(*make)(const QJsonObject& json); - }; - - const Factory factories[] { - { "m.text", MessageEventType::Text, make<TextContent> }, - { "m.emote", MessageEventType::Emote, make<TextContent> }, - { "m.notice", MessageEventType::Notice, make<TextContent> }, - { "m.image", MessageEventType::Image, make<ImageContent> }, - { "m.file", MessageEventType::File, make<FileContent> }, - { "m.location", MessageEventType::Location, make<LocationContent> }, - { "m.video", MessageEventType::Video, makeVideoContent }, - { "m.audio", MessageEventType::Audio, make<AudioContent> }, - // Insert new message types before this line - }; - - QString msgtype = content.value("msgtype").toString(); - for (auto f: factories) - { - if (msgtype == f.jsonTag) - { - e->d->msgtype = f.enumTag; - e->d->content = f.make(content); - break; - } - } - if (e->d->msgtype == MessageEventType::Unknown) - { - qDebug() << "RoomMessageEvent: unknown msgtype: " << msgtype; - qDebug() << obj; - e->d->content = new Base; - } + auto delegate = lookup(content.value("msgtype").toString(), + "m.text", make<MessageEventType::Text, TextContent>, + "m.emote", make<MessageEventType::Emote, TextContent>, + "m.notice", make<MessageEventType::Notice, TextContent>, + "m.image", make<MessageEventType::Image, ImageContent>, + "m.file", make<MessageEventType::File, FileContent>, + "m.location", make<MessageEventType::Location, LocationContent>, + "m.video", makeVideo, + "m.audio", make<MessageEventType::Audio, AudioContent>, + // Insert new message types before this line + makeUnknown + ); + std::tie(e->d->msgtype, e->d->content) = delegate(content); } else { |