77#include < numeric>
88#include < sstream>
99
10+ #include < boost/algorithm/string/trim_all.hpp>
11+
12+ namespace ba = boost::algorithm;
13+
1014namespace jinja2
1115{
1216
1317namespace filters
1418{
15-
19+
1620template <typename D>
1721struct StringEncoder : public visitors ::BaseVisitor<>
1822{
@@ -22,15 +26,15 @@ struct StringEncoder : public visitors::BaseVisitor<>
2226 InternalValue operator () (const std::basic_string<CharT>& str) const
2327 {
2428 std::basic_string<CharT> result;
25-
29+
2630 for (auto & ch : str)
2731 {
28- D:: EncodeChar (ch, [&result](auto ... chs) {AppendChar (result, chs...);});
32+ static_cast < const D*>( this )-> EncodeChar (ch, [&result](auto ... chs) {AppendChar (result, chs...);});
2933 }
30-
34+
3135 return result;
3236 }
33-
37+
3438 template <typename Str, typename CharT>
3539 static void AppendChar (Str& str, CharT ch)
3640 {
@@ -42,13 +46,26 @@ struct StringEncoder : public visitors::BaseVisitor<>
4246 str.push_back (static_cast <typename Str::value_type>(ch));
4347 AppendChar (str, chs...);
4448 }
45-
49+ };
50+
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;
4663};
4764
4865struct UrlStringEncoder : public StringEncoder <UrlStringEncoder>
4966{
5067 template <typename CharT, typename Fn>
51- static void EncodeChar (CharT ch, Fn&& fn)
68+ void EncodeChar (CharT ch, Fn&& fn) const
5269 {
5370 switch (ch)
5471 {
@@ -121,22 +138,105 @@ struct UrlStringEncoder : public StringEncoder<UrlStringEncoder>
121138 default :
122139 fn (ch);
123140 break ;
124- }
141+ }
142+ }
143+ };
144+
145+ template <typename Fn>
146+ struct StringConverterImpl : public visitors ::BaseVisitor<>
147+ {
148+ using BaseVisitor::operator ();
149+
150+ StringConverterImpl (const Fn& fn) : m_fn(fn) {}
151+
152+ template <typename CharT>
153+ InternalValue operator ()(const std::basic_string<CharT>& str) const
154+ {
155+ return m_fn (str);
125156 }
126- };
157+
158+ const Fn& m_fn;
159+ };
160+
161+ template <template <typename > class Cvt = StringConverterImpl, typename Fn>
162+ auto ApplyConverter (const InternalValue& str, Fn&& fn)
163+ {
164+ return Apply<Cvt<Fn>>(str, std::forward<Fn>(fn));
165+ }
166+
127167
128168StringConverter::StringConverter (FilterParams params, StringConverter::Mode mode)
129169 : m_mode(mode)
130170{
131-
171+ switch (m_mode)
172+ {
173+ case ReplaceMode:
174+ ParseParams ({{" old" , true }, {" new" , true }, {" count" , false }}, params);
175+ break ;
176+ }
132177}
133178
134179InternalValue StringConverter::Filter (const InternalValue& baseVal, RenderContext& context)
135180{
136181 InternalValue result;
137182
183+ auto isAlpha = ba::is_alpha ();
184+ auto isAlNum = ba::is_alnum ();
185+
138186 switch (m_mode)
139187 {
188+ case TrimMode:
189+ result = ApplyConverter (baseVal, [](auto str) {
190+ ba::trim_all (str);
191+ return str;
192+ });
193+ 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 ;
238+ case ReplaceMode:
239+ break ;
140240 case UrlEncodeMode:
141241 result = Apply<UrlStringEncoder>(baseVal);
142242 break ;
@@ -147,4 +247,4 @@ InternalValue StringConverter::Filter(const InternalValue& baseVal, RenderContex
147247}
148248
149249}
150- }
250+ }
0 commit comments