mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-23 07:43:01 +01:00
Add FmtString class
This commit is contained in:
@@ -19,6 +19,134 @@
|
|||||||
|
|
||||||
namespace OpenRCT2
|
namespace OpenRCT2
|
||||||
{
|
{
|
||||||
|
FmtString::token::token(FormatToken k, std::string_view s)
|
||||||
|
: kind(k)
|
||||||
|
, text(s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FmtString::token::IsLiteral() const
|
||||||
|
{
|
||||||
|
return kind == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::iterator::iterator(std::string_view s, size_t i)
|
||||||
|
: str(s)
|
||||||
|
, index(i)
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FmtString::iterator::update()
|
||||||
|
{
|
||||||
|
auto i = index;
|
||||||
|
if (i >= str.size())
|
||||||
|
{
|
||||||
|
current = token();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str[i] == '{' && i + 1 < str.size() && str[i + 1] != '{')
|
||||||
|
{
|
||||||
|
// Move to end brace
|
||||||
|
do
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
} while (i < str.size() && str[i] != '}');
|
||||||
|
if (i < str.size() && str[i] == '}')
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
} while (i < str.size() && str[i] != '{');
|
||||||
|
}
|
||||||
|
current = CreateToken(i - index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FmtString::iterator::operator==(iterator& rhs)
|
||||||
|
{
|
||||||
|
return index == rhs.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FmtString::iterator::operator!=(iterator& rhs)
|
||||||
|
{
|
||||||
|
return index != rhs.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::token FmtString::iterator::CreateToken(size_t len)
|
||||||
|
{
|
||||||
|
std::string_view sztoken = str.substr(index, len);
|
||||||
|
if (sztoken.size() >= 2 && sztoken[0] == '{' && sztoken[1] != '{')
|
||||||
|
{
|
||||||
|
auto kind = format_get_code(sztoken.substr(1, len - 2));
|
||||||
|
return token(kind, sztoken);
|
||||||
|
}
|
||||||
|
return token(0, sztoken);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FmtString::token* FmtString::iterator::operator->() const
|
||||||
|
{
|
||||||
|
return ¤t;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FmtString::token& FmtString::iterator::operator*()
|
||||||
|
{
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::iterator& FmtString::iterator::operator++()
|
||||||
|
{
|
||||||
|
if (index < str.size())
|
||||||
|
{
|
||||||
|
index += current.text.size();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::FmtString(std::string&& s)
|
||||||
|
{
|
||||||
|
_strOwned = std::move(s);
|
||||||
|
_str = _strOwned;
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::FmtString(std::string_view s)
|
||||||
|
: _str(s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::FmtString(const char* s)
|
||||||
|
: FmtString(std::string_view(s))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::iterator FmtString::begin() const
|
||||||
|
{
|
||||||
|
return iterator(_str, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FmtString::iterator FmtString::end() const
|
||||||
|
{
|
||||||
|
return iterator(_str, _str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FmtString::WithoutFormatTokens() const
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
result.reserve(_str.size() * 4);
|
||||||
|
for (const auto& t : *this)
|
||||||
|
{
|
||||||
|
if (t.IsLiteral())
|
||||||
|
{
|
||||||
|
result += t.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
char GetDigitSeperator()
|
char GetDigitSeperator()
|
||||||
{
|
{
|
||||||
return ',';
|
return ',';
|
||||||
|
|||||||
@@ -23,6 +23,48 @@ namespace OpenRCT2
|
|||||||
{
|
{
|
||||||
using FormatToken = uint32_t;
|
using FormatToken = uint32_t;
|
||||||
|
|
||||||
|
class FmtString
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string_view _str;
|
||||||
|
std::string _strOwned;
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct token
|
||||||
|
{
|
||||||
|
FormatToken kind{};
|
||||||
|
std::string_view text;
|
||||||
|
|
||||||
|
token() = default;
|
||||||
|
token(FormatToken k, std::string_view s);
|
||||||
|
bool IsLiteral() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iterator
|
||||||
|
{
|
||||||
|
std::string_view str;
|
||||||
|
size_t index;
|
||||||
|
token current;
|
||||||
|
|
||||||
|
iterator(std::string_view s, size_t i);
|
||||||
|
void update();
|
||||||
|
bool operator==(iterator& rhs);
|
||||||
|
bool operator!=(iterator& rhs);
|
||||||
|
token CreateToken(size_t len);
|
||||||
|
const token* operator->() const;
|
||||||
|
const token& operator*();
|
||||||
|
iterator& operator++();
|
||||||
|
};
|
||||||
|
|
||||||
|
FmtString(std::string&& s);
|
||||||
|
FmtString(std::string_view s);
|
||||||
|
FmtString(const char* s);
|
||||||
|
iterator begin() const;
|
||||||
|
iterator end() const;
|
||||||
|
|
||||||
|
std::string WithoutFormatTokens() const;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg);
|
template<typename T> void FormatArgument(std::stringstream& ss, FormatToken token, T arg);
|
||||||
|
|
||||||
std::pair<std::string_view, uint32_t> FormatNextPart(std::string_view& fmt);
|
std::pair<std::string_view, uint32_t> FormatNextPart(std::string_view& fmt);
|
||||||
|
|||||||
@@ -10,12 +10,42 @@
|
|||||||
#include "openrct2/localisation/Formatting.h"
|
#include "openrct2/localisation/Formatting.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <openrct2/core/String.hpp>
|
||||||
#include <openrct2/Context.h>
|
#include <openrct2/Context.h>
|
||||||
#include <openrct2/OpenRCT2.h>
|
#include <openrct2/OpenRCT2.h>
|
||||||
#include <openrct2/config/Config.h>
|
#include <openrct2/config/Config.h>
|
||||||
|
|
||||||
using namespace OpenRCT2;
|
using namespace OpenRCT2;
|
||||||
|
|
||||||
|
class FmtStringTests : public testing::Test
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(FmtStringTests, string_owned)
|
||||||
|
{
|
||||||
|
auto fmt = FmtString(std::string("{BLACK}Guests: {INT32}"));
|
||||||
|
ASSERT_EQ("Guests: ", fmt.WithoutFormatTokens());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FmtStringTests, iteration)
|
||||||
|
{
|
||||||
|
std::string actual;
|
||||||
|
|
||||||
|
auto fmt = FmtString("{BLACK}Guests: {INT32}");
|
||||||
|
for (const auto &t : fmt)
|
||||||
|
{
|
||||||
|
actual += String::StdFormat("[%d:%s]", t.kind, std::string(t.text).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ("[142:{BLACK}][0:Guests: ][124:{INT32}]", actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FmtStringTests, without_format_tokens)
|
||||||
|
{
|
||||||
|
auto fmt = FmtString("{BLACK}Guests: {INT32}");
|
||||||
|
ASSERT_EQ("Guests: ", fmt.WithoutFormatTokens());
|
||||||
|
}
|
||||||
|
|
||||||
class FormattingTests : public testing::Test
|
class FormattingTests : public testing::Test
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -48,6 +78,7 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<IContext> FormattingTests::_context;
|
std::shared_ptr<IContext> FormattingTests::_context;
|
||||||
|
|
||||||
TEST_F(FormattingTests, no_args)
|
TEST_F(FormattingTests, no_args)
|
||||||
{
|
{
|
||||||
auto actual = FormatString("test string");
|
auto actual = FormatString("test string");
|
||||||
|
|||||||
Reference in New Issue
Block a user