Support old message placeholder syntax

This allows the next release of libloot to be used with existing
masterlists and userlists, and they can then be converted to the new
syntax before a future libloot release drops support for the old syntax.

While it's based on Boost.Format's syntax, which is more complex than
supported by this commit, the masterlist syntax docs only mentioned and
gave examples of what is supported by this commit, and the masterlists
only contain placeholders %1%, %2% and %3%.
This commit is contained in:
Oliver Hamlet
2023-08-29 22:59:10 +01:00
parent dc95b18805
commit dc8bb0f854
2 changed files with 52 additions and 2 deletions
+30 -2
View File
@@ -110,10 +110,38 @@ struct convert<loot::Message> {
formatArgStore.push_back(sub);
}
static const std::regex boostSyntax(
"%(\\d+)%", std::regex::ECMAScript | std::regex::icase);
for (auto& mc : content) {
// Replace the old Boost.Format placeholder syntax for backward
// compatibility. To be removed after at least one major release of
// libloot to allow migration to the new syntax.
auto text = mc.GetText();
std::smatch match;
while (
std::regex_search(text.cbegin(), text.cend(), match, boostSyntax)) {
if (match.size() > 1) {
const auto n = std::stoul(match[1]);
if (n > 0) {
const auto newPlaceholder = "{" + std::to_string(n - 1) + "}";
text.replace(match[0].first, match[0].second, newPlaceholder);
} else {
throw RepresentationException(
node.Mark(),
"bad conversion: found zero-indexed placeholder using old "
"syntax");
}
} else {
throw RepresentationException(node.Mark(),
"bad conversion: only partially "
"matched old placeholder syntax");
}
}
try {
const auto text = fmt::vformat(mc.GetText(), formatArgStore);
mc = loot::MessageContent(text, mc.GetLanguage());
const auto formattedText = fmt::vformat(text, formatArgStore);
mc = loot::MessageContent(formattedText, mc.GetLanguage());
} catch (const fmt::format_error& e) {
throw RepresentationException(
node.Mark(),
@@ -669,6 +669,28 @@ TEST_P(MessageTest,
message.GetContent());
}
TEST_P(MessageTest, decodingFromYamlShouldAcceptPercentagePlaceholderSyntax) {
YAML::Node node = YAML::Load(
"type: say\n"
"content: content %1% %2% %3% %4% %5% %6% %7% %8% %9% %10% %11%\n"
"subs:\n"
" - a\n"
" - b\n"
" - c\n"
" - d\n"
" - e\n"
" - f\n"
" - g\n"
" - h\n"
" - i\n"
" - j\n"
" - k");
Message message = node.as<Message>();
ASSERT_EQ(1, message.GetContent().size());
EXPECT_EQ("content a b c d e f g h i j k", message.GetContent()[0].GetText());
}
TEST_P(MessageTest, decodingFromYamlShouldThrowIfAnInvalidConditionIsGiven) {
YAML::Node node = YAML::Load(
"type: say\n"