From 45e9e943115badd1a10ce18dc660408564e4aac9 Mon Sep 17 00:00:00 2001 From: Treeston Date: Sun, 30 Aug 2020 02:50:25 +0200 Subject: Core/ChatCommands: C++17 cleanup (again) (PR #25323) (cherry picked from commit 2f7d2ef3e979ecd0536f3a3713e56c8e59652a47) --- src/server/game/Chat/ChatCommands/ChatCommand.h | 259 +++++++-------------- .../game/Chat/ChatCommands/ChatCommandArgs.cpp | 43 ++-- .../game/Chat/ChatCommands/ChatCommandArgs.h | 154 ++++++------ .../game/Chat/ChatCommands/ChatCommandHelpers.h | 36 ++- .../game/Chat/ChatCommands/ChatCommandTags.h | 112 +++++---- 5 files changed, 281 insertions(+), 323 deletions(-) (limited to 'src/server/game/Chat/ChatCommands') diff --git a/src/server/game/Chat/ChatCommands/ChatCommand.h b/src/server/game/Chat/ChatCommands/ChatCommand.h index c54f7f52cb5..dac914485d0 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommand.h +++ b/src/server/game/Chat/ChatCommands/ChatCommand.h @@ -33,199 +33,106 @@ class ChatHandler; class CommandArgs; -template -struct CommandArgsConsumerSingle +namespace Trinity::Impl::ChatCommands { - using arginfo = Trinity::ChatCommands::ArgInfo; - static char const* TryConsumeTo(T& val, char const* args) + template + struct SingleConsumer { - return arginfo::TryConsume(val, args); - } -}; - -struct CommandArgsVariantConsumer -{ - template - static char const* TryConsumeTo(V& val, char const* args) - { - T1 v; - if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(v, args)) + static Optional TryConsumeTo(T& val, std::string_view args) { - val = std::move(v); - return next; + return ArgInfo::TryConsume(val, args); } - else if constexpr (sizeof...(Ts) > 0) - return TryConsumeTo(val, args); - else - return nullptr; - } -}; - -template -struct CommandArgsConsumerSingle> -{ - static char const* TryConsumeTo(Trinity::ChatCommands::Variant& val, char const* args) - { - return CommandArgsVariantConsumer::TryConsumeTo, Ts...>(val, args); - } -}; + }; -template -struct CommandArgsConsumerSingle> -{ - static char const* TryConsumeTo(std::vector& val, char const* args) + template + struct SingleConsumer> { - char const* last; - val.clear(); - - do val.emplace_back(); - while ((args = CommandArgsConsumerSingle::TryConsumeTo(val.back(), (last = args)))); - - val.pop_back(); - return last; - } -}; + using V = std::variant; + static constexpr size_t N = std::variant_size_v; -template -struct CommandArgsConsumerSingle> -{ - static char const* TryConsumeTo(std::array& val, char const* args) - { - for (T& t : val) + template + static Optional TryAtIndex(Trinity::ChatCommands::Variant& val, [[maybe_unused]] std::string_view args) { - args = CommandArgsConsumerSingle::TryConsumeTo(t, args); - - if (!args) - return nullptr; + if constexpr (I < N) + { + if (Optional next = ArgInfo>::TryConsume(val.template emplace(), args)) + return next; + else + return TryAtIndex(val, args); + } + else + return std::nullopt; } - return args; - } -}; - -template <> -struct CommandArgsConsumerSingle -{ - static char const* TryConsumeTo(CommandArgs*&, char const* args) { return args; } -}; - -template <> -struct CommandArgsConsumerSingle -{ - static char const* TryConsumeTo(char const*&, char const* args) { return args; } -}; - -template -struct CommandArgsConsumerNext; - -template -struct CommandArgsConsumerMulti -{ - static char const* TryConsumeTo(Tuple& tuple, char const* args) - { - if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(std::get(tuple), args)) - return CommandArgsConsumerNext::GoNext(tuple, next); - else - return nullptr; - } -}; - -template -struct CommandArgsConsumerMulti, offset> -{ - static char const* TryConsumeTo(Tuple& tuple, char const* args) + static Optional TryConsumeTo(Trinity::ChatCommands::Variant& val, std::string_view args) + { + return TryAtIndex<0>(val, args); + } + }; + + /* + for backwards compatibility, consumes the rest of the string + new code should use the Tail/WTail tags defined in ChatCommandTags + */ + template <> + struct SingleConsumer { - // try with the argument - auto& myArg = std::get(tuple); - myArg.emplace(); - if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(myArg.value(), args)) - if ((next = CommandArgsConsumerNext::GoNext(tuple, next))) - return next; - // try again omitting the argument - myArg = std::nullopt; - if (char const* next = CommandArgsConsumerNext::GoNext(tuple, args)) - return next; - return nullptr; - } -}; + static Optional TryConsumeTo(char const*& arg, std::string_view args) { arg = args.data(); return std::string_view(); } + }; -template -struct CommandArgsConsumerNext, offset> -{ - using tuple_type = std::tuple; - template - static std::enable_if_t GoNext(tuple_type& tuple, char const* args) - { - return CommandArgsConsumerMulti, offset>::TryConsumeTo(tuple, args); - } + // forward declaration + // ConsumeFromOffset contains the bounds check for offset, then hands off to MultiConsumer + // the call stack is MultiConsumer -> ConsumeFromOffset -> MultiConsumer -> ConsumeFromOffset etc + // MultiConsumer calls SingleConsumer in each iteration + template + Optional ConsumeFromOffset(Tuple&, std::string_view args); - template - static std::enable_if_t GoNext(tuple_type&, char const* args) + template + struct MultiConsumer { - return args; - } -}; - -class TC_GAME_API CommandArgs -{ - public: - CommandArgs(char const* args) : _original(args), _args(args) {} - - template - auto TryConsume() + static Optional TryConsumeTo(Tuple& tuple, std::string_view args) { - Optional, advstd::remove_cvref_t, advstd::remove_cvref_t...>> rv; - rv.emplace(); - if (!TryConsumeToTuple<0>(rv.value())) - rv = std::nullopt; - return rv; - } - - template - auto TryConsume() - { - using T = advstd::remove_cvref_t; - Optional rv; - rv.emplace(); - if (char const* next = CommandArgsConsumerSingle::TryConsumeTo(rv.value(), _args)) - _args = next; + if (Optional next = SingleConsumer::TryConsumeTo(std::get(tuple), args)) + return ConsumeFromOffset(tuple, *next); else - rv = std::nullopt; - return rv; + return std::nullopt; } + }; - template - bool TryConsumeToTuple(T& tuple) + template + struct MultiConsumer, offset> + { + static Optional TryConsumeTo(Tuple& tuple, std::string_view args) { - if (char const* next = CommandArgsConsumerNext::GoNext(tuple, _args)) - { - _args = next; - return true; - } - else - return false; + // try with the argument + auto& myArg = std::get(tuple); + myArg.emplace(); + if (Optional next = SingleConsumer::TryConsumeTo(myArg.value(), args)) + if ((next = ConsumeFromOffset(tuple, *next))) + return next; + // try again omitting the argument + myArg = std::nullopt; + return ConsumeFromOffset(tuple, args); } + }; - void Reset() { _args = _original; } - - char const* GetFullArgs() const { return _original; } - char const* GetRemainingArgs() const { return _args; } - - bool IsEmpty() const { return !!*_args; } - explicit operator bool() const { return IsEmpty(); } - - private: - char const* const _original; - char const* _args; -}; + template + Optional ConsumeFromOffset(Tuple& tuple, std::string_view args) + { + if constexpr (offset < std::tuple_size_v) + return MultiConsumer, offset>::TryConsumeTo(tuple, args); + else if (!args.empty()) /* the entire string must be consumed */ + return std::nullopt; + else + return args; + } -template struct ChatCommandHandlerToTuple { static_assert(!std::is_same_v, "Invalid command handler signature"); }; -template struct ChatCommandHandlerToTuple { using type = std::tuple...>; }; + template struct HandlerToTuple { static_assert(Trinity::dependant_false_v, "Invalid command handler signature"); }; + template struct HandlerToTuple { using type = std::tuple...>; }; + template using TupleType = typename HandlerToTuple::type; +} -template struct ChatCommandStoreLastArg { static void store(T&, CommandArgs&) {} }; -template <> struct ChatCommandStoreLastArg { static void store(char const*& arg, CommandArgs& args) { arg = args.GetRemainingArgs(); } }; -template <> struct ChatCommandStoreLastArg { static void store(CommandArgs*& arg, CommandArgs& args) { arg = &args; } }; class TC_GAME_API ChatCommand { @@ -238,18 +145,12 @@ class TC_GAME_API ChatCommand { _wrapper = [](void* handler, ChatHandler* chatHandler, char const* argsStr) { - using tuple_type = typename ChatCommandHandlerToTuple::type; + using Tuple = Trinity::Impl::ChatCommands::TupleType; - tuple_type arguments; + Tuple arguments; std::get<0>(arguments) = chatHandler; - - CommandArgs args(argsStr); - if (args.TryConsumeToTuple<1>(arguments)) - { - auto& last = std::get-1>(arguments); - ChatCommandStoreLastArg>::store(last, args); + if (Trinity::Impl::ChatCommands::ConsumeFromOffset(arguments, argsStr)) return std::apply(reinterpret_cast(handler), std::move(arguments)); - } else return false; }; @@ -272,7 +173,7 @@ class TC_GAME_API ChatCommand bool HasHandler() const { return !!_handler; } char const* Name; - uint32 Permission; // function pointer required correct align (use uint32) + uint32 Permission; bool AllowConsole; std::string Help; std::vector ChildCommands; diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp index 9f62839d612..df74ac40e26 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp +++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.cpp @@ -20,6 +20,7 @@ #include "DB2Stores.h" #include "ObjectMgr.h" #include "SpellMgr.h" +#include "Util.h" using namespace Trinity::ChatCommands; @@ -29,12 +30,13 @@ struct AchievementVisitor value_type operator()(Hyperlink achData) const { return achData->Achievement; } value_type operator()(uint32 achId) const { return sAchievementStore.LookupEntry(achId); } }; -char const* Trinity::ChatCommands::ArgInfo::TryConsume(AchievementEntry const*& data, char const* args) +Optional Trinity::Impl::ChatCommands::ArgInfo::TryConsume(AchievementEntry const*& data, std::string_view args) { Variant, uint32> val; - if ((args = CommandArgsConsumerSingle::TryConsumeTo(val, args))) + Optional next = SingleConsumer::TryConsumeTo(val, args); + if (next) data = val.visit(AchievementVisitor()); - return args; + return next; } struct CurrencyTypesVisitor @@ -43,10 +45,11 @@ struct CurrencyTypesVisitor value_type operator()(Hyperlink currency) const { return currency->Currency; } value_type operator()(uint32 currencyId) const { return sCurrencyTypesStore.LookupEntry(currencyId); } }; -char const* Trinity::ChatCommands::ArgInfo::TryConsume(CurrencyTypesEntry const*& data, char const* args) +Optional Trinity::Impl::ChatCommands::ArgInfo::TryConsume(CurrencyTypesEntry const*& data, std::string_view args) { Variant, uint32> val; - if ((args = CommandArgsConsumerSingle::TryConsumeTo(val, args))) + Optional next = SingleConsumer::TryConsumeTo(val, args); + if (next) data = val.visit(CurrencyTypesVisitor()); return args; } @@ -57,12 +60,13 @@ struct GameTeleVisitor value_type operator()(Hyperlink tele) const { return sObjectMgr->GetGameTele(tele); } value_type operator()(std::string const& tele) const { return sObjectMgr->GetGameTele(tele); } }; -char const* Trinity::ChatCommands::ArgInfo::TryConsume(GameTele const*& data, char const* args) +Optional Trinity::Impl::ChatCommands::ArgInfo::TryConsume(GameTele const*& data, std::string_view args) { Variant, std::string> val; - if ((args = CommandArgsConsumerSingle::TryConsumeTo(val, args))) + Optional next = SingleConsumer::TryConsumeTo(val, args); + if (next) data = val.visit(GameTeleVisitor()); - return args; + return next; } struct SpellInfoVisitor @@ -80,26 +84,11 @@ struct SpellInfoVisitor value_type operator()(uint32 spellId) const { return sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE); } }; -char const* Trinity::ChatCommands::ArgInfo::TryConsume(SpellInfo const*& data, char const* args) +Optional Trinity::Impl::ChatCommands::ArgInfo::TryConsume(SpellInfo const*& data, std::string_view args) { Variant, Hyperlink, Hyperlink, Hyperlink, Hyperlink, Hyperlink, Hyperlink, Hyperlink, uint32> val; - if ((args = CommandArgsConsumerSingle::TryConsumeTo(val, args))) + Optional next = SingleConsumer::TryConsumeTo(val, args); + if (next) data = val.visit(SpellInfoVisitor()); - return args; -} - -char const* Trinity::ChatCommands::ArgInfo::TryConsume(bool& data, char const* args) -{ - std::string val; - if ((args = CommandArgsConsumerSingle::TryConsumeTo(val, args))) - { - strToLower(val); - if (val == "on" || val == "yes" || val == "true" || val == "1" || val == "y") - data = true; - else if (val == "off" || val == "no" || val == "false" || val == "0" || val == "n") - data = false; - else - return nullptr; - } - return args; + return next; } diff --git a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h index e2270c0259f..22e96b1ccda 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandArgs.h +++ b/src/server/game/Chat/ChatCommands/ChatCommandArgs.h @@ -30,17 +30,15 @@ struct GameTele; -namespace Trinity -{ -namespace ChatCommands +namespace Trinity::Impl::ChatCommands { /************************** ARGUMENT HANDLERS *******************************************\ |* Define how to extract contents of a certain requested type from a string *| |* Must implement the following: *| -|* - TryConsume: T&, char const* -> char const* *| -|* returns nullptr if no match, otherwise pointer to first character of next token *| -|* - if nullptr is returned, state of T& is indeterminate *| +|* - TryConsume: T&, std::string_view -> Optional *| +|* returns nullopt if no match, otherwise tail of argument string *| +|* - if nullopt is returned, state of T& is indeterminate *| |* - otherwise, T& should be initialized to the intended return value *| |* *| \****************************************************************************************/ @@ -51,16 +49,17 @@ struct ArgInfo { static_assert(!std::is_same_v, "Invalid command parameter template struct ArgInfo>> { - static char const* TryConsume(T& val, char const* args) + static Optional TryConsume(T& val, std::string_view args) { - char const* next = args; - std::string_view token(args, Trinity::Impl::ChatCommands::tokenize(next)); + auto [token, tail] = tokenize(args); + if (token.empty()) + return std::nullopt; if (Optional v = StringTo(token, 0)) val = *v; else - return nullptr; - return next; + return std::nullopt; + return tail; } }; @@ -68,20 +67,26 @@ struct ArgInfo>> template struct ArgInfo>> { - static char const* TryConsume(T& val, char const* args) + static Optional TryConsume(T& val, std::string_view args) { - char const* next = args; - std::string token(args, Trinity::Impl::ChatCommands::tokenize(next)); + auto [token, tail] = tokenize(args); + if (token.empty()) + return std::nullopt; + try { // @todo replace this once libc++ supports double args to from_chars for required minimum size_t processedChars = 0; - val = std::stold(token, &processedChars); + val = std::stold(std::string(token), &processedChars); if (processedChars != token.length()) - return nullptr; + return std::nullopt; } - catch (...) { return nullptr; } - return std::isfinite(val) ? next : nullptr; + catch (...) { return std::nullopt; } + + if (std::isfinite(val)) + return tail; + else + return std::nullopt; } }; @@ -89,16 +94,13 @@ struct ArgInfo>> template <> struct ArgInfo { - static char const* TryConsume(std::string_view& val, char const* args) + static Optional TryConsume(std::string_view& val, std::string_view args) { - char const* next = args; - if (size_t len = Trinity::Impl::ChatCommands::tokenize(next)) - { - val = std::string_view(args, len); - return next; - } - else - return nullptr; + auto [token, next] = tokenize(args); + if (token.empty()) + return std::nullopt; + val = token; + return next; } }; @@ -106,13 +108,13 @@ struct ArgInfo template <> struct ArgInfo { - static char const* TryConsume(std::string& val, char const* args) + static Optional TryConsume(std::string& val, std::string_view args) { std::string_view view; - args = ArgInfo::TryConsume(view, args); - if (args) + Optional next = ArgInfo::TryConsume(view, args); + if (next) val.assign(view); - return args; + return next; } }; @@ -120,18 +122,15 @@ struct ArgInfo template <> struct ArgInfo { - static char const* TryConsume(std::wstring& val, char const* args) + static Optional TryConsume(std::wstring& val, std::string_view args) { std::string_view utf8view; - char const* ret = ArgInfo::TryConsume(utf8view, args); - - if (!ret) - return nullptr; - - if (!Utf8toWStr(utf8view, val)) - return nullptr; + Optional next = ArgInfo::TryConsume(utf8view, args); - return ret; + if (next && Utf8toWStr(utf8view, val)) + return next; + else + return std::nullopt; } }; @@ -189,32 +188,31 @@ struct ArgInfo>> return nullptr; } - static char const* TryConsume(T& val, char const* args) + static Optional TryConsume(T& val, std::string_view args) { std::string strVal; - char const* ret = ArgInfo::TryConsume(strVal, args); - - if (!ret) - return nullptr; + Optional next = ArgInfo::TryConsume(strVal, args); - if (T const* tmpVal = Match(strVal)) + if (next) { - val = *tmpVal; - return ret; + if (T const* match = Match(strVal)) + { + val = *match; + return next; + } } // Value not found. Try to parse arg as underlying type and cast it to enum type using U = std::underlying_type_t; U uVal = 0; - ret = ArgInfo::TryConsume(uVal, args); - if (ret) + next = ArgInfo::TryConsume(uVal, args); + if (next && EnumUtils::IsValid(uVal)) { val = static_cast(uVal); - if (!EnumUtils::IsValid(val)) - return nullptr; + return next; } - return ret; + return std::nullopt; } }; @@ -222,48 +220,72 @@ struct ArgInfo>> template struct ArgInfo>> { - static char const* TryConsume(T& tag, char const* args) + static Optional TryConsume(T& tag, std::string_view args) { return tag.TryConsume(args); } }; +template +struct ArgInfo, void> +{ + static Optional TryConsume(std::vector& val, std::string_view args) + { + val.clear(); + Optional next = ArgInfo::TryConsume(val.emplace_back(), args); + + if (!next) + return std::nullopt; + + while (Optional next2 = ArgInfo::TryConsume(val.emplace_back(), *next)) + next = next2; + + val.pop_back(); + return next; + } +}; + +template +struct ArgInfo, void> +{ + static Optional TryConsume(std::array& val, std::string_view args) + { + Optional next = args; + for (T& t : val) + if (!(next = ArgInfo::TryConsume(t, *next))) + return std::nullopt; + return next; + } +}; + // AchievementEntry* from numeric id or link template <> struct TC_GAME_API ArgInfo { - static char const* TryConsume(AchievementEntry const*&, char const*); + static Optional TryConsume(AchievementEntry const*&, std::string_view); }; // CurrencyTypesEntry* from numeric id or link template <> struct TC_GAME_API ArgInfo { - static char const* TryConsume(CurrencyTypesEntry const*&, char const*); + static Optional TryConsume(CurrencyTypesEntry const*&, std::string_view); }; // GameTele* from string name or link template <> struct TC_GAME_API ArgInfo { - static char const* TryConsume(GameTele const*&, char const*); + static Optional TryConsume(GameTele const*&, std::string_view); }; // SpellInfo const* from spell id or link template <> struct TC_GAME_API ArgInfo { - static char const* TryConsume(SpellInfo const*&, char const*); + static Optional TryConsume(SpellInfo const*&, std::string_view); }; -// bool from 1/0 or on/off -template <> -struct TC_GAME_API ArgInfo -{ - static char const* TryConsume(bool&, char const*); -}; - -} } #endif diff --git a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h index e22823e5d35..c6cf4f8ad94 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h +++ b/src/server/game/Chat/ChatCommands/ChatCommandHelpers.h @@ -18,10 +18,15 @@ #ifndef TRINITY_CHATCOMMANDHELPERS_H #define TRINITY_CHATCOMMANDHELPERS_H +#include #include -namespace Trinity::ChatCommands +namespace Trinity::Impl::ChatCommands { + /***************** HELPERS *************************\ + |* These really aren't for outside use... *| + \***************************************************/ + static constexpr char COMMAND_DELIMITER = ' '; template @@ -32,19 +37,26 @@ namespace Trinity::ChatCommands template using tag_base_t = typename tag_base::type; -} -namespace Trinity::Impl::ChatCommands -{ - /***************** HELPERS *************************\ - |* These really aren't for outside use... *| - \***************************************************/ - inline std::size_t tokenize(char const*& end) + struct TokenizeResult { + explicit operator bool() { return !token.empty(); } + std::string_view token; + std::string_view tail; + }; + + inline TokenizeResult tokenize(std::string_view args) { - std::size_t len = 0; - for (; *end && *end != Trinity::ChatCommands::COMMAND_DELIMITER; ++end, ++len); - for (; *end && *end == Trinity::ChatCommands::COMMAND_DELIMITER; ++end); - return len; + TokenizeResult result; + if (size_t delimPos = args.find(COMMAND_DELIMITER); delimPos != std::string_view::npos) + { + result.token = args.substr(0, delimPos); + if (size_t tailPos = args.find_first_not_of(COMMAND_DELIMITER, delimPos); tailPos != std::string_view::npos) + result.tail = args.substr(tailPos); + } + else + result.token = args; + + return result; } template diff --git a/src/server/game/Chat/ChatCommands/ChatCommandTags.h b/src/server/game/Chat/ChatCommands/ChatCommandTags.h index 78f49673794..9255ef18fd3 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommandTags.h +++ b/src/server/game/Chat/ChatCommands/ChatCommandTags.h @@ -22,59 +22,95 @@ #include "ChatCommandHelpers.h" #include "Hyperlinks.h" #include "Optional.h" +#include "Util.h" #include #include #include #include +#include #include #include #include #include +namespace Trinity::Impl::ChatCommands +{ + struct ContainerTag {}; + template + struct tag_base>> + { + using type = typename T::value_type; + }; +} + namespace Trinity::ChatCommands { /************************** CONTAINER TAGS **********************************************\ |* Simple holder classes to differentiate between extraction methods *| - |* Should inherit from ContainerTag for template identification *| + |* Must inherit from Trinity::Impl::ChatCommands::ContainerTag *| |* Must implement the following: *| - |* - TryConsume: char const* -> char const* *| - |* returns nullptr if no match, otherwise pointer to first character of next token *| + |* - TryConsume: std::string_view -> Optional *| + |* returns nullopt if no match, otherwise the tail of the provided argument string *| |* - typedef value_type of type that is contained within the tag *| |* - cast operator to value_type *| |* *| \****************************************************************************************/ - struct ContainerTag {}; - template - struct tag_base>> - { - using type = typename T::value_type; - }; template - struct ExactSequence : public ContainerTag + struct ExactSequence : Trinity::Impl::ChatCommands::ContainerTag { using value_type = void; - static char const* _TryConsume(char const* pos) + static constexpr size_t N = (sizeof...(chars) + 1); + + static bool Match(char const* pos) + { + if (*(pos++) != c1) + return false; + else if constexpr (sizeof...(chars) > 0) + return ExactSequence::Match(pos); + else + return true; + } + + Optional TryConsume(std::string_view args) const { - if (*(pos++) == c1) + if ((N <= args.length()) && ExactSequence::Match(args.data())) { - if constexpr (sizeof...(chars) > 0) - return ExactSequence::_TryConsume(pos); - else if (Trinity::Impl::ChatCommands::tokenize(pos)) /* we did not consume the entire token */ - return nullptr; - else - return pos; + auto [remainingToken, tail] = Trinity::Impl::ChatCommands::tokenize(args.substr(N)); + if (remainingToken.empty()) // if this is not empty, then we did not consume the full token + return tail; } - else - return nullptr; + return std::nullopt; + } + }; + + struct Tail : std::string_view, Trinity::Impl::ChatCommands::ContainerTag + { + using value_type = std::string_view; + + Optional TryConsume(std::string_view args) + { + std::string_view::operator=(args); + return std::string_view(); } + }; - char const* TryConsume(char const* pos) const { return ExactSequence::_TryConsume(pos); } + struct WTail : std::wstring, Trinity::Impl::ChatCommands::ContainerTag + { + using value_type = std::wstring; + + Optional TryConsume(std::string_view args) + { + if (Utf8toWStr(args, *this)) + return std::string_view(); + else + return std::nullopt; + } }; template - struct Hyperlink : public ContainerTag + struct Hyperlink : Trinity::Impl::ChatCommands::ContainerTag { using value_type = typename linktag::value_type; using storage_type = advstd::remove_cvref_t; @@ -84,29 +120,27 @@ namespace Trinity::ChatCommands value_type operator*() const { return val; } storage_type const* operator->() const { return &val; } - char const* TryConsume(char const* pos) + Optional TryConsume(std::string_view args) { - Trinity::Hyperlinks::HyperlinkInfo info = Trinity::Hyperlinks::ParseHyperlink(pos); + Trinity::Hyperlinks::HyperlinkInfo info = Trinity::Hyperlinks::ParseSingleHyperlink(args); // invalid hyperlinks cannot be consumed if (!info) - return nullptr; + return std::nullopt; // check if we got the right tag - if (info.tag.second != strlen(linktag::tag())) - return nullptr; - if (strncmp(info.tag.first, linktag::tag(), strlen(linktag::tag())) != 0) - return nullptr; + if (info.tag != linktag::tag()) + return std::nullopt; // store value - if (!linktag::StoreTo(val, info.data.first, info.data.second)) - return nullptr; + if (!linktag::StoreTo(val, info.data)) + return std::nullopt; - // finally, skip to end of token - pos = info.next; - Trinity::Impl::ChatCommands::tokenize(pos); - - // return final pos - return pos; + // finally, skip any potential delimiters + auto [token, next] = Trinity::Impl::ChatCommands::tokenize(info.tail); + if (token.empty()) /* empty token = first character is delimiter, skip past it */ + return next; + else + return info.tail; } private: @@ -138,8 +172,8 @@ namespace Trinity::ChatCommands { using base = std::variant; - using first_type = tag_base_t; - static constexpr bool have_operators = Trinity::Impl::ChatCommands::are_all_assignable...>::value; + using first_type = Trinity::Impl::ChatCommands::tag_base_t; + static constexpr bool have_operators = Trinity::Impl::ChatCommands::are_all_assignable...>::value; template std::enable_if_t operator*() const -- cgit v1.2.3