1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-23 15:52:55 +01:00

Add FmtString class

This commit is contained in:
Ted John
2020-10-09 19:48:15 +01:00
parent 8cb3103cc7
commit b07bc6b0ab
3 changed files with 201 additions and 0 deletions

View File

@@ -19,6 +19,134 @@
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 &current;
}
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()
{
return ',';

View File

@@ -23,6 +23,48 @@ namespace OpenRCT2
{
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);
std::pair<std::string_view, uint32_t> FormatNextPart(std::string_view& fmt);

View File

@@ -10,12 +10,42 @@
#include "openrct2/localisation/Formatting.h"
#include <gtest/gtest.h>
#include <openrct2/core/String.hpp>
#include <openrct2/Context.h>
#include <openrct2/OpenRCT2.h>
#include <openrct2/config/Config.h>
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
{
private:
@@ -48,6 +78,7 @@ protected:
};
std::shared_ptr<IContext> FormattingTests::_context;
TEST_F(FormattingTests, no_args)
{
auto actual = FormatString("test string");