8000 Implement 'title', 'upper', 'lower' and 'wordcount' filters · jinja2cpp/Jinja2Cpp@7c12a62 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7c12a62

Browse files
committed
Implement 'title', 'upper', 'lower' and 'wordcount' filters
1 parent 3ee8a8d commit 7c12a62

File tree

4 files changed

+101
-4
lines changed

4 files changed

+101
-4
lines changed

src/filters.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ std::unordered_map<std::string, ExpressionFilter::FilterFactoryFn> s_filters = {
4949
{"last", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::LastItemMode)},
5050
{"length", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::LengthMode)},
5151
{"list", FilterFactory<filters::ValueConverter>::MakeCreator(filters::ValueConverter::ToListMode)},
52+
{"lower", FilterFactory<filters::StringConverter>::MakeCreator(filters::StringConverter::LowerMode)},
5253
{"map", &FilterFactory<filters::Map>::Create},
5354
{"max", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::MaxItemMode)},
5455
{"min", FilterFactory<filters::SequenceAccessor>::MakeCreator(filters::SequenceAccessor::MinItemMode)},

src/filters.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ class StringConverter : public FilterBase
166166
CamelMode,
167167
EscapeCppMode,
168168
EscapeHtmlMode,
169+
LowerMode,
169170
ReplaceMode,
170171
TitleMode,
171172
TrimMode,

src/string_converter_filter.cpp

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ struct StringEncoder : public visitors::BaseVisitor<>
2929

3030
for (auto& ch : str)
3131
{
32-
D::EncodeChar(ch, [&result](auto ... chs) {AppendChar(result, chs...);});
32+
static_cast<const D*>(this)->EncodeChar(ch, [&result](auto ... chs) {AppendChar(result, chs...);});
3333
}
3434

3535
return result;
@@ -46,13 +46,26 @@ struct StringEncoder : public visitors::BaseVisitor<>
4646
str.push_back(static_cast<typename Str::value_type>(ch));
4747
AppendChar(str, chs...);
4848
}
49+
};
4950

51+
template<typename Fn>
52+
struct GenericStringEncoder : public StringEncoder<GenericStringEncoder<Fn>>
53+
{
54+
GenericStringEncoder(Fn fn) : m_fn(std::move(fn)) {}
55+
56+
template<typename CharT, typename AppendFn>
57+
void EncodeChar(CharT ch, AppendFn&& fn) const
58+
{
59+
m_fn(ch, std::forward<AppendFn>(fn));
60+
}
61+
62+
mutable Fn m_fn;
5063
};
5164

5265
struct UrlStringEncoder : public StringEncoder<UrlStringEncoder>
5366
{
5467
template<typename CharT, typename Fn>
55-
static void EncodeChar(CharT ch, Fn&& fn)
68+
void EncodeChar(CharT ch, Fn&& fn) const
5669
{
5770
switch (ch)
5871
{
@@ -145,12 +158,13 @@ struct StringConverterImpl : public visitors::BaseVisitor<>
145158
const Fn& m_fn;
146159
};
147160

148-
template<typename Fn>
161+
template<template<typename> class Cvt = StringConverterImpl, typename Fn>
149162
auto ApplyConverter(const InternalValue& str, Fn&& fn)
150163
{
151-
return Apply<StringConverterImpl<Fn>>(str, std::forward<Fn>(fn));
164+
return Apply<Cvt<Fn>>(str, std::forward<Fn>(fn));
152165
}
153166

167+
154168
StringConverter::StringConverter(FilterParams params, StringConverter::Mode mode)
155169
: m_mode(mode)
156170
{
@@ -165,6 +179,9 @@ StringConverter::StringConverter(FilterParams params, StringConverter::Mode mode
165179
InternalValue StringConverter::Filter(const InternalValue& baseVal, RenderContext& context)
166180
{
167181
InternalValue result;
182+
183+
auto isAlpha = ba::is_alpha();
184+
auto isAlNum = ba::is_alnum();
168185

169186
switch (m_mode)
170187
{
@@ -174,6 +191,50 @@ InternalValue StringConverter::Filter(const InternalValue& baseVal, RenderContex
174191
return str;
175192
});
176193
break;
194+
case TitleMode:
195+
result = ApplyConverter<GenericStringEncoder>(baseVal, [isDelim = true, &isAlpha, &isAlNum](auto ch, auto&& fn) mutable {
196+
if (isDelim && isAlpha(ch))
197+
{
198+
isDelim = false;
199+
fn(std::toupper(ch, std::locale()));
200+
return;
201+
}
202+
203+
isDelim = !isAlNum(ch);
204+
fn(ch);
205+
});
206+
break;
207+
case WordCountMode:
208+
{
209+
int64_t wc = 0;
210+
ApplyConverter<GenericStringEncoder>(baseVal, [isDelim = true, &wc, &isAlpha, &isAlNum](auto ch, auto&& fn) mutable {
211+
if (isDelim && isAlNum(ch))
212+
{
213+
isDelim = false;
214+
wc ++;
215+
return;
216+
}
217+
isDelim = !isAlNum(ch);
218+
});
219+
result = wc;
220+
break;
221+
}
222+
case UpperMode:
223+
result = ApplyConverter<GenericStringEncoder>(baseVal, [&isAlpha](auto ch, auto&& fn) mutable {
224+
if (isAlpha(ch))
225+
fn(std::toupper(ch, std::locale()));
226+
else
227+
fn(ch);
228+
});
229+
break;
230+
case LowerMode:
231+
result = ApplyConverter<GenericStringEncoder>(baseVal, [&isAlpha](auto ch, auto&& fn) mutable {
232+
if (isAlpha(ch))
233+
fn(std::tolower(ch, std::locale()));
234+
else
235+
fn(ch);
236+
});
237+
break;
177238
case ReplaceMode:
178239
break;
179240
case UrlEncodeMode:

test/filters_test.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,37 @@ INSTANTIATE_TEST_CASE_P(Trim, FilterGenericTest, ::testing::Values(
283283
InputOutputPair{"' string ' | trim | pprint", "'string'"}/*,
284284
InputOutputPair{"wstringValue | trim", "'hello world'"}*/
285285
));
286+
287+
INSTANTIATE_TEST_CASE_P(Title, FilterGenericTest, ::testing::Values(
288+
InputOutputPair{"'string' | title | pprint", "'String'"},
289+
InputOutputPair{"'1234string' | title | pprint", "'1234string'"},
290+
InputOutputPair{"'hello world' | title | pprint", "'Hello World'"},
291+
InputOutputPair{"'hello123ooo, world!' | title | pprint", "'Hello123ooo, World!'"}/*,
292+
InputOutputPair{"wstringValue | trim", "'hello world'"}*/
293+
));
294+
295+
INSTANTIATE_TEST_CASE_P(Upper, FilterGenericTest, ::testing::Values(
296+
InputOutputPair{"'string' | upper | pprint", "'STRING'"},
297+
InputOutputPair{"'1234string' | upper | pprint", "'1234STRING'"},
298+
InputOutputPair{"'hello world' | upper | pprint", "'HELLO WORLD'"},
299+
InputOutputPair{"'hello123ooo, world!' | upper | pprint", "'HELLO123OOO, WORLD!'"}/*,
300+
InputOutputPair{"wstringValue | trim", "'hello world'"}*/
301+
));
302+
303+
304+
INSTANTIATE_TEST_CASE_P(Lower, FilterGenericTest, ::testing::Values(
305+
InputOutputPair{"'String' | lower | pprint", "'string'"},
306+
InputOutputPair{"'1234String' | lower | pprint", "'1234string'"},
307+
InputOutputPair{"'Hello World' | lower | pprint", "'hello world'"},
308+
InputOutputPair{"'Hello123OOO, World!' | lower | pprint", "'hello123ooo, world!'"}/*,
309+
InputOutputPair{"wstringValue | trim", "'hello world'"}*/
310+
));
311+
312+
313+
INSTANTIATE_TEST_CASE_P(WordCount, FilterGenericTest, ::testing::Values(
314+
InputOutputPair{"'string' | wordcount", "1"},
315+
InputOutputPair{"'1234string' | wordcount", "1"},
316+
InputOutputPair{"'hello world' | wordcount", "2"},
317+
InputOutputPair{"'hello123ooo, world!' | wordcount", "2"}/*,
318+
InputOutputPair{"wstringValue | trim", "'hello world'"}*/
319+
));

0 commit comments

Comments
 (0)
0