Files
TCPlayerbotCore/src/server/game/Chat/ChatCommands/ChatCommandArgs.h
T
Peter Keresztes Schmidt 3923650aeb Core/ChatCommands: Add support for enum type arguments (PR #25242)
(cherry picked from commit 5e40eb20e2789b86d4786d86b4bbb8ae83de5e1e)
2022-01-26 22:13:06 +01:00

230 lines
6.6 KiB
C++

/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_CHATCOMMANDARGS_H
#define TRINITY_CHATCOMMANDARGS_H
#include "ChatCommandHelpers.h"
#include "ChatCommandTags.h"
#include "SmartEnum.h"
#include "Util.h"
#include <map>
struct GameTele;
namespace Trinity
{
namespace 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 *|
|* - otherwise, T& should be initialized to the intended return value *|
|* *|
\****************************************************************************************/
template <typename T, typename = void>
struct ArgInfo { static_assert(!std::is_same_v<T,T>, "Invalid command parameter type - see ChatCommandArgs.h for possible types"); };
// catch-all for signed integral types
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T> && std::is_signed_v<T>>>
{
static char const* TryConsume(T& val, char const* args)
{
char const* next = args;
std::string token(args, tokenize(next));
try { val = std::stoll(token); }
catch (...) { return nullptr; }
return next;
}
};
// catch-all for unsigned integral types
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_integral_v<T> && std::is_unsigned_v<T>>>
{
static char const* TryConsume(T& val, char const* args)
{
char const* next = args;
std::string token(args, tokenize(next));
try { val = std::stoull(token); }
catch (...) { return nullptr; }
return next;
}
};
// catch-all for floating point types
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_floating_point_v<T>>>
{
static char const* TryConsume(T& val, char const* args)
{
char const* next = args;
std::string token(args, tokenize(next));
try { val = std::stold(token); }
catch (...) { return nullptr; }
return std::isfinite(val) ? next : nullptr;
}
};
// string
template <>
struct ArgInfo<std::string, void>
{
static char const* TryConsume(std::string& val, char const* args)
{
char const* next = args;
if (size_t len = tokenize(next))
{
val.assign(args, len);
return next;
}
else
return nullptr;
}
};
// enum
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_enum_v<T>>>
{
static std::map<std::string, Optional<T>> MakeSearchMap()
{
std::map<std::string, Optional<T>> map;
for (T val : EnumUtils::Iterate<T>())
{
EnumText text = EnumUtils::ToString(val);
std::string title(text.Title);
strToLower(title);
std::string constant(text.Constant);
strToLower(constant);
auto [constantIt, constantNew] = map.try_emplace(constant, val);
if (!constantNew)
constantIt->second = std::nullopt;
if (title != constant)
{
auto [titleIt, titleNew] = map.try_emplace(title, val);
if (!titleNew)
titleIt->second = std::nullopt;
}
}
return map;
}
static inline std::map<std::string, Optional<T>> const SearchMap = MakeSearchMap();
static T const* Match(std::string s)
{
strToLower(s);
auto it = SearchMap.lower_bound(s);
if (it == SearchMap.end() || !StringStartsWith(it->first, s)) // not a match
return nullptr;
auto it2 = it;
++it2;
if (it2 != SearchMap.end() && StringStartsWith(it2->first, s)) // not unique
return nullptr;
if (it->second)
return &*it->second;
else
return nullptr;
}
static char const* TryConsume(T& val, char const* args)
{
std::string strVal;
char const* ret = ArgInfo<std::string>::TryConsume(strVal, args);
if (!ret)
return nullptr;
if (T const* tmpVal = Match(strVal))
{
val = *tmpVal;
return ret;
}
// Value not found. Try to parse arg as underlying type and cast it to enum type
using U = std::underlying_type_t<T>;
U uVal = 0;
ret = ArgInfo<U>::TryConsume(uVal, args);
if (ret)
val = static_cast<T>(uVal);
return ret;
}
};
// a container tag
template <typename T>
struct ArgInfo<T, std::enable_if_t<std::is_base_of_v<ContainerTag, T>>>
{
static char const* TryConsume(T& tag, char const* args)
{
return tag.TryConsume(args);
}
};
// AchievementEntry* from numeric id or link
template <>
struct TC_GAME_API ArgInfo<AchievementEntry const*>
{
static char const* TryConsume(AchievementEntry const*&, char const*);
};
// CurrencyTypesEntry* from numeric id or link
template <>
struct TC_GAME_API ArgInfo<CurrencyTypesEntry const*>
{
static char const* TryConsume(CurrencyTypesEntry const*&, char const*);
};
// GameTele* from string name or link
template <>
struct TC_GAME_API ArgInfo<GameTele const*>
{
static char const* TryConsume(GameTele const*&, char const*);
};
// SpellInfo const* from spell id or link
template <>
struct TC_GAME_API ArgInfo<SpellInfo const*>
{
static char const* TryConsume(SpellInfo const*&, char const*);
};
// bool from 1/0 or on/off
template <>
struct TC_GAME_API ArgInfo<bool>
{
static char const* TryConsume(bool&, char const*);
};
}
}
#endif