TrinityCore
ChatCommandTags.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef TRINITY_CHATCOMMANDTAGS_H
19#define TRINITY_CHATCOMMANDTAGS_H
20
21#include "ChatCommandHelpers.h"
22#include "Hyperlinks.h"
23#include "ObjectGuid.h"
24#include "Optional.h"
25#include "Util.h"
26#include <boost/preprocessor/repetition/repeat.hpp>
27#include <boost/preprocessor/punctuation/comma_if.hpp>
28#include <fmt/ostream.h>
29#include <string>
30#include <string_view>
31#include <tuple>
32#include <type_traits>
33#include <utility>
34#include <variant>
35
36class ChatHandler;
37class Player;
38class WorldSession;
39
41{
43 {
45 };
46
47 template <typename T>
48 struct tag_base<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
49 {
50 using type = typename T::value_type;
51 };
52
53 template <size_t N>
54 inline constexpr char GetChar(char const (&s)[N], size_t i)
55 {
56 static_assert(N <= 25, "The EXACT_SEQUENCE macro can only be used with up to 25 character long literals. Specify them char-by-char (null terminated) as parameters to ExactSequence<> instead.");
57 return i >= N ? '\0' : s[i];
58 }
59
60#define CHATCOMMANDS_IMPL_SPLIT_LITERAL_EXTRACT_CHAR(z, i, strliteral) \
61 BOOST_PP_COMMA_IF(i) Trinity::Impl::ChatCommands::GetChar(strliteral, i)
62
63#define CHATCOMMANDS_IMPL_SPLIT_LITERAL_CONSTRAINED(maxlen, strliteral) \
64 BOOST_PP_REPEAT(maxlen, CHATCOMMANDS_IMPL_SPLIT_LITERAL_EXTRACT_CHAR, strliteral)
65
66 // this creates always 25 elements - "abc" -> 'a', 'b', 'c', '\0', '\0', ... up to 25
67#define CHATCOMMANDS_IMPL_SPLIT_LITERAL(strliteral) CHATCOMMANDS_IMPL_SPLIT_LITERAL_CONSTRAINED(25, strliteral)
68}
69
71{
72 /************************** CONTAINER TAGS **********************************************\
73 |* Simple holder classes to differentiate between extraction methods *|
74 |* Must inherit from Trinity::Impl::ChatCommands::ContainerTag *|
75 |* Must implement the following: *|
76 |* - TryConsume: ChatHandler const*, std::string_view -> ChatCommandResult *|
77 |* - on match, returns tail of the provided argument string (as std::string_view) *|
78 |* - on specific error, returns error message (as std::string&& or char const*) *|
79 |* - on generic error, returns std::nullopt (this will print command usage) *|
80 |* *|
81 |* - typedef value_type of type that is contained within the tag *|
82 |* - cast operator to value_type *|
83 |* *|
84 \****************************************************************************************/
85
86 template <char... chars>
88 {
89 using value_type = void;
90
91 ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args) const
92 {
93 if (args.empty())
94 return std::nullopt;
95 std::string_view start = args.substr(0, _string.length());
96 if (StringEqualI(start, _string))
97 {
98 auto [remainingToken, tail] = Trinity::Impl::ChatCommands::tokenize(args.substr(_string.length()));
99 if (remainingToken.empty()) // if this is not empty, then we did not consume the full token
100 return tail;
101 start = args.substr(0, _string.length() + remainingToken.length());
102 }
104 }
105
106 private:
107 static constexpr std::array<char, sizeof...(chars)> _storage = { chars... };
108 static_assert(!_storage.empty() && (_storage.back() == '\0'), "ExactSequence parameters must be null terminated! Use the EXACT_SEQUENCE macro to make this easier!");
109 static constexpr std::string_view _string = { _storage.data(), std::string_view::traits_type::length(_storage.data()) };
110 };
111
112#define EXACT_SEQUENCE(str) Trinity::ChatCommands::ExactSequence<CHATCOMMANDS_IMPL_SPLIT_LITERAL(str)>
113
115 {
116 using value_type = std::string_view;
117
118 using std::string_view::operator=;
119
120 ChatCommandResult TryConsume(ChatHandler const*,std::string_view args)
121 {
122 std::string_view::operator=(args);
123 return std::string_view();
124 }
125 };
126
128 {
129 using value_type = std::wstring;
130
131 using std::wstring::operator=;
132
133 ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args)
134 {
135 if (Utf8toWStr(args, *this))
136 return std::string_view();
137 else
139 }
140 };
141
143 {
144 using value_type = std::string;
145
146 TC_GAME_API ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
147 };
148
150 {
152
153 AccountIdentifier() : _id(), _name(), _session(nullptr) {}
155
156 operator uint32() const { return _id; }
157 operator std::string const& () const { return _name; }
158 operator std::string_view() const { return _name; }
159
160 uint32 GetID() const { return _id; }
161 std::string const& GetName() const { return _name; }
162 bool IsConnected() { return _session != nullptr; }
163 WorldSession* GetConnectedSession() { return _session; }
164
165 ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
166
167 static Optional<AccountIdentifier> FromTarget(ChatHandler* handler);
168
169 private:
171 std::string _name;
173 };
174
176 {
178
179 PlayerIdentifier() : _name(), _guid(), _player(nullptr) {}
180 PlayerIdentifier(Player& player);
181
182 operator ObjectGuid() const { return _guid; }
183 operator std::string const&() const { return _name; }
184 operator std::string_view() const { return _name; }
185
186 std::string const& GetName() const { return _name; }
187 ObjectGuid GetGUID() const { return _guid; }
188 bool IsConnected() const { return (_player != nullptr); }
189 Player* GetConnectedPlayer() const { return _player; }
190
191 ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args);
192
193 static Optional<PlayerIdentifier> FromTarget(ChatHandler* handler);
194 static Optional<PlayerIdentifier> FromSelf(ChatHandler* handler);
196 {
197 if (Optional<PlayerIdentifier> fromTarget = FromTarget(handler))
198 return fromTarget;
199 else
200 return FromSelf(handler);
201 }
202
203 private:
204 std::string _name;
207 };
208
209 template <typename linktag>
211 {
212 using value_type = typename linktag::value_type;
213 using storage_type = std::remove_cvref_t<value_type>;
214
215 operator value_type() const { return val; }
216 value_type operator*() const { return val; }
217 storage_type const* operator->() const { return &val; }
218
219 ChatCommandResult TryConsume(ChatHandler const* handler, std::string_view args)
220 {
222 // invalid hyperlinks cannot be consumed
223 if (!info)
224 return std::nullopt;
225
226 // check if we got the right tag
227 if (info.tag != linktag::tag())
228 return std::nullopt;
229
230 // store value
231 if (!linktag::StoreTo(val, info.data))
233
234 // finally, skip any potential delimiters
235 auto [token, next] = Trinity::Impl::ChatCommands::tokenize(info.tail);
236 if (token.empty()) /* empty token = first character is delimiter, skip past it */
237 return next;
238 else
239 return info.tail;
240 }
241
242 private:
244 };
245
246 // pull in link tags for user convenience
247 using namespace ::Trinity::Hyperlinks::LinkTags;
248}
249
250namespace Trinity::Impl
251{
252 template <typename T>
254 {
255 template <typename U>
256 T operator()(U const& v) const { return v; }
257 };
258}
259
260namespace Trinity::ChatCommands
261{
262 template <typename T1, typename... Ts>
263 struct Variant : public std::variant<T1, Ts...>
264 {
265 using base = std::variant<T1, Ts...>;
266
269
270 template <bool C = have_operators>
271 std::enable_if_t<C, first_type> operator*() const
272 {
274 }
275
276 template <bool C = have_operators>
277 operator std::enable_if_t<C, first_type>() const
278 {
279 return operator*();
280 }
281
282 template <bool C = have_operators>
283 std::enable_if_t<C, bool> operator!() const { return !**this; }
284
285 template <typename T>
286 Variant& operator=(T&& arg) { base::operator=(std::forward<T>(arg)); return *this; }
287
288 template <size_t index>
289 constexpr decltype(auto) get() { return std::get<index>(static_cast<base&>(*this)); }
290 template <size_t index>
291 constexpr decltype(auto) get() const { return std::get<index>(static_cast<base const&>(*this)); }
292 template <typename type>
293 constexpr decltype(auto) get() { return std::get<type>(static_cast<base&>(*this)); }
294 template <typename type>
295 constexpr decltype(auto) get() const { return std::get<type>(static_cast<base const&>(*this)); }
296
297 template <typename T>
298 constexpr decltype(auto) visit(T&& arg) { return std::visit(std::forward<T>(arg), static_cast<base&>(*this)); }
299 template <typename T>
300 constexpr decltype(auto) visit(T&& arg) const { return std::visit(std::forward<T>(arg), static_cast<base const&>(*this)); }
301
302 template <typename T>
303 constexpr bool holds_alternative() const { return std::holds_alternative<T>(static_cast<base const&>(*this)); }
304
305 template <bool C = have_operators>
306 friend std::enable_if_t<C, std::ostream&> operator<<(std::ostream& os, Trinity::ChatCommands::Variant<T1, Ts...> const& v)
307 {
308 return (os << *v);
309 }
310 };
311}
312
313template <typename T1, typename... Ts>
314struct fmt::formatter<Trinity::ChatCommands::Variant<T1, Ts...>> : ostream_formatter {};
315
316template <typename T1, typename... Ts>
317struct fmt::printf_formatter<Trinity::ChatCommands::Variant<T1, Ts...>> : formatter<T1>
318{
319 template <typename T, typename OutputIt>
320 auto format(T const& value, basic_format_context<OutputIt, char>& ctx) const -> OutputIt
321 {
322 return formatter<T1>::format(*value, ctx);
323 }
324};
325
326#endif
Trinity::Impl::ChatCommands::ChatCommandResult ChatCommandResult
#define TC_GAME_API
Definition: Define.h:123
#define STRING_VIEW_FMT_ARG(str)
Definition: Define.h:135
uint32_t uint32
Definition: Define.h:142
@ LANG_CMDPARSER_EXACT_SEQ_MISMATCH
Definition: Language.h:1013
@ LANG_CMDPARSER_LINKDATA_INVALID
Definition: Language.h:1002
@ LANG_CMDPARSER_INVALID_UTF8
Definition: Language.h:1001
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
bool StringEqualI(std::string_view a, std::string_view b)
Definition: Util.cpp:891
bool Utf8toWStr(char const *utf8str, size_t csize, wchar_t *wstr, size_t &wsize)
Definition: Util.cpp:383
Player session in the World.
Definition: WorldSession.h:963
TokenizeResult tokenize(std::string_view args)
typename tag_base< T >::type tag_base_t
std::string FormatTrinityString(ChatHandler const *handler, TrinityStrings which, Ts &&... args)
TC_GAME_API char const * GetTrinityString(ChatHandler const *handler, TrinityStrings which)
constexpr char GetChar(char const (&s)[N], size_t i)
STL namespace.
std::string const & GetName() const
static constexpr std::string_view _string
static constexpr std::array< char, sizeof...(chars)> _storage
ChatCommandResult TryConsume(ChatHandler const *handler, std::string_view args) const
std::string const & GetName() const
static Optional< PlayerIdentifier > FromTargetOrSelf(ChatHandler *handler)
TC_GAME_API ChatCommandResult TryConsume(ChatHandler const *handler, std::string_view args)
ChatCommandResult TryConsume(ChatHandler const *, std::string_view args)
constexpr bool holds_alternative() const
constexpr decltype(auto) visit(T &&arg)
Trinity::Impl::ChatCommands::tag_base_t< T1 > first_type
std::enable_if_t< C, first_type > operator*() const
constexpr decltype(auto) visit(T &&arg) const
constexpr decltype(auto) get()
std::enable_if_t< C, bool > operator!() const
std::variant< T1, Ts... > base
static constexpr bool have_operators
friend std::enable_if_t< C, std::ostream & > operator<<(std::ostream &os, Trinity::ChatCommands::Variant< T1, Ts... > const &v)
constexpr decltype(auto) get() const
ChatCommandResult TryConsume(ChatHandler const *handler, std::string_view args)
T operator()(U const &v) const
auto format(T const &value, basic_format_context< OutputIt, char > &ctx) const -> OutputIt