diff options
-rw-r--r-- | .gdb_history | 6 | ||||
-rw-r--r-- | include/dchat/Cache.hpp | 13 | ||||
-rw-r--r-- | include/dchat/IncomingMessage.hpp | 22 | ||||
-rw-r--r-- | include/dchat/MessageComposer.hpp | 32 | ||||
-rw-r--r-- | include/dchat/OutgoingMessage.hpp | 33 | ||||
-rw-r--r-- | include/dchat/Range.hpp | 15 | ||||
-rw-r--r-- | include/dchat/Storage.hpp | 24 | ||||
-rw-r--r-- | src/Cache.cpp | 94 | ||||
-rw-r--r-- | src/IncomingMessage.cpp (renamed from src/MessageComposer.cpp) | 10 | ||||
-rw-r--r-- | src/OutgoingMessage.cpp | 136 | ||||
-rw-r--r-- | src/Room.cpp | 4 | ||||
-rw-r--r-- | src/Storage.cpp | 105 | ||||
-rw-r--r-- | tests/main.cpp | 206 |
13 files changed, 526 insertions, 174 deletions
diff --git a/.gdb_history b/.gdb_history new file mode 100644 index 0000000..1ef1965 --- /dev/null +++ b/.gdb_history @@ -0,0 +1,6 @@ +start +b src/OutgoingMessage.cpp:55 +info b +quit +start +b src/OutgoingMessage.cpp:55 diff --git a/include/dchat/Cache.hpp b/include/dchat/Cache.hpp index 4d37b54..8d3f28e 100644 --- a/include/dchat/Cache.hpp +++ b/include/dchat/Cache.hpp @@ -59,12 +59,11 @@ namespace dchat i64 lastAccessed; }; - using LoadBindsCallbackFunc = std::function<void(const std::string &key, const std::string &value)>; // @fileContent contains data allocated with new[], deallocate it with delete[] fileContent.data; // Returned gif should be allocated with @new using CreateGifFunc = std::function<Gif*(StringView fileContent)>; using CreateStaticImageFunc = std::function<StaticImage*(const boost::filesystem::path &filepath)>; - + class Cache { DISABLE_COPY(Cache) @@ -74,16 +73,6 @@ namespace dchat Cache(CreateGifFunc createGifFunc, CreateStaticImageFunc createStaticImageFunc); ~Cache(); - // Creates directory if it doesn't exist (recursively). Throws boost exception on failure - static boost::filesystem::path getDchatDir(); - - // Creates directory if it doesn't exist (recursively). Throws boost exception on failure - static boost::filesystem::path getImagesDir(); - - // @callbackFunc can't be nullptr - static void loadBindsFromFile(LoadBindsCallbackFunc callbackFunc); - static void replaceBindsInFile(const std::unordered_map<std::string, std::string> &binds); - // Get cached content or download it. // Default download file limit is 12MB // Returns ContentByUrlResult describing texture status. diff --git a/include/dchat/IncomingMessage.hpp b/include/dchat/IncomingMessage.hpp new file mode 100644 index 0000000..b20e4d1 --- /dev/null +++ b/include/dchat/IncomingMessage.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "types.hpp" +#include "Range.hpp" +#include <functional> + +namespace dchat +{ + struct IncomingMessagePart + { + enum class Type + { + TEXT, + EMOJI + }; + + Type type; + Range textRange; + }; + + void parseIncomingMessage(const char *text, usize size, std::function<void(IncomingMessagePart)> callbackFunc); +}
\ No newline at end of file diff --git a/include/dchat/MessageComposer.hpp b/include/dchat/MessageComposer.hpp deleted file mode 100644 index b4b55c2..0000000 --- a/include/dchat/MessageComposer.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "types.hpp" -#include <functional> - -namespace dchat -{ - struct Range - { - int start; - int end; - - int length() const - { - return end - start; - } - }; - - struct MessagePart - { - enum class Type - { - TEXT, - EMOJI - }; - - Type type; - Range textRange; - }; - - void compose(const char *text, usize size, std::function<void(MessagePart)> callbackFunc); -}
\ No newline at end of file diff --git a/include/dchat/OutgoingMessage.hpp b/include/dchat/OutgoingMessage.hpp new file mode 100644 index 0000000..c3e8e84 --- /dev/null +++ b/include/dchat/OutgoingMessage.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "types.hpp" +#include "Range.hpp" +#include <functional> +#include <unordered_map> +#include <string> + +namespace dchat +{ + struct OutgoingMessagePart + { + enum class Type + { + TEXT, + EMOJI + }; + + Type type; + union + { + Range textRange; + const std::string *emoji; + }; + + OutgoingMessagePart(Type _type, Range _textRange) : type(_type), textRange(_textRange) {} + OutgoingMessagePart(Type _type, const std::string *_emoji) : type(_type), emoji(_emoji) {} + }; + + using EmojiBindMap = std::unordered_map<std::string, std::string>; + + void parseOutgoingMessage(const char *text, usize size, const EmojiBindMap &emojiBindMap, std::function<void(OutgoingMessagePart)> callbackFunc); +}
\ No newline at end of file diff --git a/include/dchat/Range.hpp b/include/dchat/Range.hpp new file mode 100644 index 0000000..a3a9581 --- /dev/null +++ b/include/dchat/Range.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace dchat +{ + struct Range + { + int start; + int end; + + int length() const + { + return end - start; + } + }; +}
\ No newline at end of file diff --git a/include/dchat/Storage.hpp b/include/dchat/Storage.hpp new file mode 100644 index 0000000..4f3dfa2 --- /dev/null +++ b/include/dchat/Storage.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <functional> +#include <string> +#include <unordered_map> +#include <boost/filesystem/path.hpp> + +namespace dchat +{ + // Throws runtime exception on failure + boost::filesystem::path getHomeDir(); + + // Creates directory if it doesn't exist (recursively). Throws boost exception on failure + boost::filesystem::path getDchatDir(); + + // Creates directory if it doesn't exist (recursively). Throws boost exception on failure + boost::filesystem::path getImagesDir(); + + using LoadBindsCallbackFunc = std::function<void(const std::string &key, const std::string &value)>; + + // @callbackFunc can't be nullptr + void loadBindsFromFile(LoadBindsCallbackFunc callbackFunc); + void replaceBindsInFile(const std::unordered_map<std::string, std::string> &binds); +}
\ No newline at end of file diff --git a/src/Cache.cpp b/src/Cache.cpp index 84bee97..0d5dd05 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2,6 +2,7 @@ #include "../include/env.hpp" #include "../include/dchat/FileUtil.hpp" #include "../include/dchat/Gif.hpp" +#include "../include/dchat/Storage.hpp" #include <boost/filesystem/convenience.hpp> #include <process.hpp> #include <odhtdb/Hash.hpp> @@ -11,7 +12,6 @@ #include <gd.h> #if OS_FAMILY == OS_FAMILY_POSIX -#include <pwd.h> #define toNativeString(str) str #else #include <string> @@ -32,98 +32,6 @@ using namespace TinyProcessLib; namespace dchat { const i64 CONTENT_NOT_VISIBLE_AGE_MS = 30000; // Delete content from cache after a specified amount of time if the content is not visible on the screen - - static boost::filesystem::path getHomeDir() - { - #if OS_FAMILY == OS_FAMILY_POSIX - const char *homeDir = getenv("HOME"); - if(!homeDir) - { - passwd *pw = getpwuid(getuid()); - homeDir = pw->pw_dir; - } - return boost::filesystem::path(homeDir); - #elif OS_FAMILY == OS_FAMILY_WINDOWS - BOOL ret; - HANDLE hToken; - std::wstring homeDir; - DWORD homeDirLen = MAX_PATH; - homeDir.resize(homeDirLen); - - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) - throw std::runtime_error("Failed to open process token"); - - if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen)) - { - CloseHandle(hToken); - throw std::runtime_error("Failed to get home directory"); - } - - CloseHandle(hToken); - homeDir.resize(wcslen(homeDir.c_str())); - return boost::filesystem::path(homeDir); - #endif - } - - boost::filesystem::path Cache::getDchatDir() - { - boost::filesystem::path dchatHomeDir = getHomeDir() / ".local" / "share" / "dchat"; - boost::filesystem::create_directories(dchatHomeDir); - return dchatHomeDir; - } - - boost::filesystem::path Cache::getImagesDir() - { - boost::filesystem::path imagesDir = getDchatDir() / "images"; - boost::filesystem::create_directories(imagesDir); - return imagesDir; - } - - void Cache::loadBindsFromFile(LoadBindsCallbackFunc callbackFunc) - { - assert(callbackFunc); - StringView fileContent; - try - { - fileContent = getFileContent(getDchatDir() / "binds"); - sibs::SafeDeserializer deserializer((const u8*)fileContent.data, fileContent.size); - - while(!deserializer.empty()) - { - u8 keySize = deserializer.extract<u8>(); - string key; - key.resize(keySize); - deserializer.extract((u8*)&key[0], keySize); - - u8 valueSize = deserializer.extract<u8>(); - string value; - value.resize(valueSize); - deserializer.extract((u8*)&value[0], valueSize); - - callbackFunc(key, value); - } - } - catch(FileException &e) - { - fprintf(stderr, "Failed to read binds from file, reason: %s\n", e.what()); - } - - delete[] fileContent.data; - } - - void Cache::replaceBindsInFile(const unordered_map<string, string> &binds) - { - sibs::SafeSerializer serializer; - for(auto &it : binds) - { - serializer.add((u8)it.first.size()); - serializer.add((const u8*)it.first.data(), it.first.size()); - - serializer.add((u8)it.second.size()); - serializer.add((const u8*)it.second.data(), it.second.size()); - } - fileReplace(getDchatDir() / "binds", StringView((const char*)serializer.getBuffer().data(), serializer.getBuffer().size())); - } static bool downscaleImage(const boost::filesystem::path &filepath, void *data, const int size, const int newWidth, const int newHeight) { diff --git a/src/MessageComposer.cpp b/src/IncomingMessage.cpp index 71fa62e..e003a22 100644 --- a/src/MessageComposer.cpp +++ b/src/IncomingMessage.cpp @@ -1,4 +1,4 @@ -#include "../include/dchat/MessageComposer.hpp" +#include "../include/dchat/IncomingMessage.hpp" #include <assert.h> #include <string.h> @@ -131,7 +131,7 @@ namespace dchat Range typeDataRange; }; - void compose(const char *text, usize size, std::function<void(MessagePart)> callbackFunc) + void parseIncomingMessage(const char *text, usize size, std::function<void(IncomingMessagePart)> callbackFunc) { Tokenizer tokenizer(text, size); Token token = tokenizer.next(); @@ -139,19 +139,19 @@ namespace dchat { if(token == Token::TEXT) { - callbackFunc(MessagePart { MessagePart::Type::TEXT, tokenizer.identifierRange }); + callbackFunc(IncomingMessagePart { IncomingMessagePart::Type::TEXT, tokenizer.identifierRange }); token = tokenizer.next(); } else if(token == Token::TYPE) { if(tokenizer.typeRange.length() == 5 && memcmp(text + tokenizer.typeRange.start, "emoji", 5) == 0) { - callbackFunc(MessagePart { MessagePart::Type::EMOJI, tokenizer.typeDataRange }); + callbackFunc(IncomingMessagePart { IncomingMessagePart::Type::EMOJI, tokenizer.typeDataRange }); } else { Range typeToTextRange = { tokenizer.typeRange.start - 1, tokenizer.typeDataRange.end + 1 }; - callbackFunc(MessagePart{ MessagePart::Type::TEXT, typeToTextRange }); + callbackFunc(IncomingMessagePart{ IncomingMessagePart::Type::TEXT, typeToTextRange }); } token = tokenizer.next(); } diff --git a/src/OutgoingMessage.cpp b/src/OutgoingMessage.cpp new file mode 100644 index 0000000..e774940 --- /dev/null +++ b/src/OutgoingMessage.cpp @@ -0,0 +1,136 @@ +#include "../include/dchat/OutgoingMessage.hpp" +#include <assert.h> + +namespace dchat +{ + namespace OutgoingMessage + { + enum class Token + { + NONE, + END_OF_FILE, + + TEXT, + BIND + }; + + struct Tokenizer + { + Tokenizer(const char *_text, usize _size, const EmojiBindMap *_emojiBindMap) + { + assert(_text); + assert(_emojiBindMap); + text = _text; + size = _size; + index = 0; + emoji = nullptr; + emojiBindMap = _emojiBindMap; + textRange = { 0, 0 }; + } + + // returns -1 if character can't be found in the string + usize find(const char *str, usize size, char c) const + { + for(usize i = 0; i < size; ++i) + { + if(str[i] == c) + return i; + } + return -1; + } + + // Return nullptr if bind is not found + const std::string* getBindEmoji(const char *text, usize size) const + { + // TODO: unecessary conversion to string, convert emojibindmap to const char* and uses custom hasher + auto it = emojiBindMap->find(std::string(text, size)); + if(it != emojiBindMap->end()) + return &it->second; + return nullptr; + } + + Token next() + { + if(index >= size) + return Token::END_OF_FILE; + + char c = getChar(); + usize start = index; + ++index; + if(c == ':') + { + while(index < size) + { + c = getChar(); + ++index; + if(c == ':') + { + usize end = index - 1; + const std::string *_emoji = getBindEmoji(text + start + 1, end - (start + 1)); + if(_emoji) + { + emoji = _emoji; + return Token::BIND; + } + break; + } + } + + textRange.start = start; + textRange.end = index; + return Token::TEXT; + } + else + { + while(index < size) + { + c = getChar(); + if(c == ':') + break; + ++index; + } + + textRange.start = start; + textRange.end = index; + return Token::TEXT; + } + + assert(false); + return Token::NONE; + } + + char getChar() const + { + assert(index < size); + return text[index]; + } + + const char *text; + usize size; + usize index; + const std::string *emoji; + const EmojiBindMap *emojiBindMap; + + Range textRange; + }; + } + + void parseOutgoingMessage(const char *text, usize size, const EmojiBindMap &emojiBindMap, std::function<void(OutgoingMessagePart)> callbackFunc) + { + OutgoingMessage::Tokenizer tokenizer(text, size, &emojiBindMap); + OutgoingMessage::Token token = tokenizer.next(); + while(token != OutgoingMessage::Token::END_OF_FILE) + { + if(token == OutgoingMessage::Token::TEXT) + { + callbackFunc(OutgoingMessagePart { OutgoingMessagePart::Type::TEXT, tokenizer.textRange }); + token = tokenizer.next(); + } + else if(token == OutgoingMessage::Token::BIND) + { + callbackFunc(OutgoingMessagePart { OutgoingMessagePart::Type::EMOJI, tokenizer.emoji }); + token = tokenizer.next(); + } + } + } +}
\ No newline at end of file diff --git a/src/Room.cpp b/src/Room.cpp index c7135da..b526833 100644 --- a/src/Room.cpp +++ b/src/Room.cpp @@ -1,5 +1,5 @@ #include "../include/dchat/Room.hpp" -#include "../include/dchat/Cache.hpp" +#include "../include/dchat/Storage.hpp" #include "../include/dchat/FileUtil.hpp" #include "../include/dchat/RoomDataType.hpp" #include <odhtdb/Database.hpp> @@ -203,7 +203,7 @@ namespace dchat databaseCallbackFuncs.createNodeCallbackFunc = std::bind(&Rooms::createNodeCallbackFunc, this, std::placeholders::_1); databaseCallbackFuncs.addNodeCallbackFunc = std::bind(&Rooms::addNodeCallbackFunc, this, std::placeholders::_1); databaseCallbackFuncs.addUserCallbackFunc = std::bind(&Rooms::addUserCallbackFunc, this, std::placeholders::_1); - database = odhtdb::Database::connect(address, port, Cache::getDchatDir(), databaseCallbackFuncs).get(); + database = odhtdb::Database::connect(address, port, getDchatDir(), databaseCallbackFuncs).get(); } void Rooms::createNodeCallbackFunc(const odhtdb::DatabaseCreateNodeRequest &request) diff --git a/src/Storage.cpp b/src/Storage.cpp new file mode 100644 index 0000000..a0cc891 --- /dev/null +++ b/src/Storage.cpp @@ -0,0 +1,105 @@ +#include "../include/dchat/Storage.hpp" +#include "../include/dchat/env.hpp" +#include "../include/dchat/FileUtil.hpp" +#include <boost/filesystem/convenience.hpp> +#include <sibs/SafeSerializer.hpp> +#include <sibs/SafeDeserializer.hpp> + +#if OS_FAMILY == OS_FAMILY_POSIX +#include <pwd.h> +#endif + +namespace dchat +{ + boost::filesystem::path getHomeDir() + { + #if OS_FAMILY == OS_FAMILY_POSIX + const char *homeDir = getenv("HOME"); + if(!homeDir) + { + passwd *pw = getpwuid(getuid()); + homeDir = pw->pw_dir; + } + return boost::filesystem::path(homeDir); + #elif OS_FAMILY == OS_FAMILY_WINDOWS + BOOL ret; + HANDLE hToken; + std::wstring homeDir; + DWORD homeDirLen = MAX_PATH; + homeDir.resize(homeDirLen); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) + throw std::runtime_error("Failed to open process token"); + + if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen)) + { + CloseHandle(hToken); + throw std::runtime_error("Failed to get home directory"); + } + + CloseHandle(hToken); + homeDir.resize(wcslen(homeDir.c_str())); + return boost::filesystem::path(homeDir); + #endif + } + + boost::filesystem::path getDchatDir() + { + boost::filesystem::path dchatHomeDir = getHomeDir() / ".local" / "share" / "dchat"; + boost::filesystem::create_directories(dchatHomeDir); + return dchatHomeDir; + } + + boost::filesystem::path getImagesDir() + { + boost::filesystem::path imagesDir = getDchatDir() / "images"; + boost::filesystem::create_directories(imagesDir); + return imagesDir; + } + + void loadBindsFromFile(LoadBindsCallbackFunc callbackFunc) + { + assert(callbackFunc); + StringView fileContent; + try + { + fileContent = getFileContent(getDchatDir() / "binds"); + sibs::SafeDeserializer deserializer((const u8*)fileContent.data, fileContent.size); + + while(!deserializer.empty()) + { + u8 keySize = deserializer.extract<u8>(); + std::string key; + key.resize(keySize); + deserializer.extract((u8*)&key[0], keySize); + + u8 valueSize = deserializer.extract<u8>(); + std::string value; + value.resize(valueSize); + deserializer.extract((u8*)&value[0], valueSize); + + callbackFunc(key, value); + } + } + catch(FileException &e) + { + fprintf(stderr, "Failed to read binds from file, reason: %s\n", e.what()); + } + + delete[] fileContent.data; + } + + void replaceBindsInFile(const std::unordered_map<std::string, std::string> &binds) + { + sibs::SafeSerializer serializer; + for(auto &it : binds) + { + serializer.add((u8)it.first.size()); + serializer.add((const u8*)it.first.data(), it.first.size()); + + serializer.add((u8)it.second.size()); + serializer.add((const u8*)it.second.data(), it.second.size()); + } + fileReplace(getDchatDir() / "binds", StringView((const char*)serializer.getBuffer().data(), serializer.getBuffer().size())); + } +}
\ No newline at end of file diff --git a/tests/main.cpp b/tests/main.cpp index 9f33a05..64a0523 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,4 +1,5 @@ -#include <dchat/MessageComposer.hpp> +#include <dchat/IncomingMessage.hpp> +#include <dchat/OutgoingMessage.hpp> #include <vector> #include <stdio.h> #include <string.h> @@ -14,48 +15,48 @@ static void requireEqualValues(int a, int b, const char *file, int line) } #define REQUIRE_EQUAL(a, b) do { requireEqualValues((a), (b), __FILE__, __LINE__); } while(0) -int main(int argc, char **argv) +static void testIncomingMessage() { - std::vector<dchat::MessagePart> expect = + std::vector<dchat::IncomingMessagePart> expect = { - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, - dchat::MessagePart { dchat::MessagePart::Type::EMOJI, dchat::Range{ 12, 16 } }, - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 17, 21 } } + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::EMOJI, dchat::Range{ 12, 16 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 17, 21 } } }; - std::vector<const char*> expectText = + std::vector<std::string> expectText = { "abc ", "cool", " def" }; - int index = 0; + dchat::usize index = 0; const char *str = "abc [emoji](cool) def"; dchat::usize strSize = strlen(str); - auto compareFunc = [&str, &index, &expect, &expectText](dchat::MessagePart messagePart) + auto compareFunc = [&str, &index, &expect, &expectText](dchat::IncomingMessagePart incomingMessagePart) { REQUIRE(index < expect.size()); - REQUIRE_EQUAL((int)messagePart.type, (int)expect[index].type); - REQUIRE_EQUAL(messagePart.textRange.start, expect[index].textRange.start); - REQUIRE_EQUAL(messagePart.textRange.end, expect[index].textRange.end); - int length = messagePart.textRange.end - messagePart.textRange.start; - if(strncmp(str + messagePart.textRange.start, expectText[index], length) != 0) + REQUIRE_EQUAL((int)incomingMessagePart.type, (int)expect[index].type); + REQUIRE_EQUAL(incomingMessagePart.textRange.start, expect[index].textRange.start); + REQUIRE_EQUAL(incomingMessagePart.textRange.end, expect[index].textRange.end); + int length = incomingMessagePart.textRange.end - incomingMessagePart.textRange.start; + if(length != expectText[index].size() || strncmp(str + incomingMessagePart.textRange.start, expectText[index].c_str(), length) != 0) { - fprintf(stderr, "Assert failed: |%.*s| == |%.*s|\n", length, str + messagePart.textRange.start, length, expectText[index]); + fprintf(stderr, "Assert failed: |%.*s| == |%.*s|\n", length, str + incomingMessagePart.textRange.start, expectText[index].size(), expectText[index].c_str()); exit(EXIT_FAILURE); } ++index; }; - dchat::compose(str, strSize, compareFunc); + dchat::parseIncomingMessage(str, strSize, compareFunc); { expect = { - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 4, 20 } } + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 4, 20 } } }; expectText = @@ -67,15 +68,15 @@ int main(int argc, char **argv) index = 0; str = "abc [emoji](cool def"; strSize = strlen(str); - dchat::compose(str, strSize, compareFunc); + dchat::parseIncomingMessage(str, strSize, compareFunc); } { expect = { - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 4, 11 } }, - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 11, 22 } } + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 4, 11 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 11, 22 } } }; expectText = @@ -88,15 +89,15 @@ int main(int argc, char **argv) index = 0; str = "abc [emoji] (cool def)"; strSize = strlen(str); - dchat::compose(str, strSize, compareFunc); + dchat::parseIncomingMessage(str, strSize, compareFunc); } { expect = { - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 4, 20 } }, - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 20, 24 } } + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 4, 20 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 20, 24 } } }; expectText = @@ -109,14 +110,14 @@ int main(int argc, char **argv) index = 0; str = "abc [notemoji](cool) def"; strSize = strlen(str); - dchat::compose(str, strSize, compareFunc); + dchat::parseIncomingMessage(str, strSize, compareFunc); } { expect = { - dchat::MessagePart { dchat::MessagePart::Type::TEXT, dchat::Range{ 0, 12 } }, - dchat::MessagePart { dchat::MessagePart::Type::EMOJI, dchat::Range{ 20, 71 } } + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::TEXT, dchat::Range{ 0, 12 } }, + dchat::IncomingMessagePart { dchat::IncomingMessagePart::Type::EMOJI, dchat::Range{ 20, 71 } } }; expectText = @@ -128,8 +129,153 @@ int main(int argc, char **argv) index = 0; str = "Hello world [emoji](https://discordemoji.com/assets/emoji/PeepoHide.png)"; strSize = strlen(str); - dchat::compose(str, strSize, compareFunc); + dchat::parseIncomingMessage(str, strSize, compareFunc); + } +} + +static void testOutgoingMessage() +{ + const dchat::EmojiBindMap binds = { + { "thinking", "https://thinking" }, + { "sad", "https://sad" } + }; + + std::vector<dchat::OutgoingMessagePart> expect; + std::vector<std::string> expectText; + + dchat::usize index = 0; + const char *str = nullptr; + dchat::usize size = 0; + auto callbackFunc = [&expect, &expectText, &index, &str](dchat::OutgoingMessagePart outgoingMessagePart) + { + REQUIRE(index < expect.size()); + REQUIRE_EQUAL((int)outgoingMessagePart.type, (int)expect[index].type); + switch(outgoingMessagePart.type) + { + case dchat::OutgoingMessagePart::Type::TEXT: + { + REQUIRE_EQUAL(outgoingMessagePart.textRange.start, expect[index].textRange.start); + REQUIRE_EQUAL(outgoingMessagePart.textRange.end, expect[index].textRange.end); + int length = outgoingMessagePart.textRange.end - outgoingMessagePart.textRange.start; + if(length != expectText[index].size() || strncmp(str + outgoingMessagePart.textRange.start, expectText[index].c_str(), length) != 0) + { + fprintf(stderr, "Assert failed: |%.*s| == |%.*s|\n", length, str + outgoingMessagePart.textRange.start, expectText[index].size(), expectText[index].c_str()); + exit(EXIT_FAILURE); + } + break; + } + case dchat::OutgoingMessagePart::Type::EMOJI: + { + if(*outgoingMessagePart.emoji != expectText[index]) + { + fprintf(stderr, "Assert failed: |%s| == |%s|\n", outgoingMessagePart.emoji->c_str(), expectText[index].c_str()); + exit(EXIT_FAILURE); + } + break; + } + } + ++index; + }; + + { + expect = + { + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::EMOJI, nullptr }, + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::TEXT, dchat::Range{ 14, 18 } } + }; + + expectText = + { + "aaa ", + "https://thinking", + " bbb" + }; + + index = 0; + str = "aaa :thinking: bbb"; + size = strlen(str); + dchat::parseOutgoingMessage(str, size, binds, callbackFunc); + } + + { + expect = + { + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::EMOJI, nullptr }, + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::TEXT, dchat::Range{ 5, 9 } } + }; + + expectText = + { + "https://sad", + " bbb" + }; + + index = 0; + str = ":sad: bbb"; + size = strlen(str); + dchat::parseOutgoingMessage(str, size, binds, callbackFunc); + } + + { + expect = + { + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::EMOJI, nullptr }, + }; + + expectText = + { + "https://sad", + }; + + index = 0; + str = ":sad:"; + size = strlen(str); + dchat::parseOutgoingMessage(str, size, binds, callbackFunc); + } + + { + expect = + { + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::TEXT, dchat::Range{ 0, 9 } } + }; + + expectText = + { + ":nothing:", + }; + + index = 0; + str = ":nothing:"; + size = strlen(str); + dchat::parseOutgoingMessage(str, size, binds, callbackFunc); } + { + expect = + { + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::TEXT, dchat::Range{ 0, 4 } }, + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::TEXT, dchat::Range{ 4, 13 } }, + dchat::OutgoingMessagePart { dchat::OutgoingMessagePart::Type::TEXT, dchat::Range{ 13, 17 } } + }; + + expectText = + { + "aaa ", + ":nothing:", + " bbb" + }; + + index = 0; + str = "aaa :nothing: bbb"; + size = strlen(str); + dchat::parseOutgoingMessage(str, size, binds, callbackFunc); + } +} + +int main(int argc, char **argv) +{ + testIncomingMessage(); + testOutgoingMessage(); return 0; }
\ No newline at end of file |