88#include < sstream>
99
1010#include < boost/algorithm/string/trim_all.hpp>
11+ #include < boost/algorithm/string/replace.hpp>
1112
1213namespace ba = boost::algorithm;
1314
@@ -52,13 +53,13 @@ template<typename Fn>
5253struct GenericStringEncoder : public StringEncoder <GenericStringEncoder<Fn>>
5354{
5455 GenericStringEncoder (Fn fn) : m_fn(std::move(fn)) {}
55-
56+
5657 template <typename CharT, typename AppendFn>
5758 void EncodeChar (CharT ch, AppendFn&& fn) const
5859 {
5960 m_fn (ch, std::forward<AppendFn>(fn));
60- }
61-
61+ }
62+
6263 mutable Fn m_fn;
6364};
6465
@@ -158,28 +159,48 @@ struct StringConverterImpl : public visitors::BaseVisitor<>
158159 const Fn& m_fn;
159160};
160161
162+ template <typename CharT>
163+ struct SameStringGetter : public visitors ::BaseVisitor<std::basic_string<CharT>>
164+ {
165+ using ResultString = std::basic_string<CharT>;
166+ using visitors::BaseVisitor<ResultString>::operator ();
167+
168+ ResultString operator ()(const ResultString& str) const
169+ {
170+ return str;
171+ }
172+ };
173+
161174template <template <typename > class Cvt = StringConverterImpl, typename Fn>
162175auto ApplyConverter (const InternalValue& str, Fn&& fn)
163176{
164177 return Apply<Cvt<Fn>>(str, std::forward<Fn>(fn));
165178}
166179
180+ template <typename CharT>
181+ auto GetAsSameString (const std::basic_string<CharT>& s, const InternalValue& val)
182+ {
183+ return Apply<SameStringGetter<CharT>>(val);
184+ }
167185
168186StringConverter::StringConverter (FilterParams params, StringConverter::Mode mode)
169187 : m_mode(mode)
170188{
171189 switch (m_mode)
172190 {
173191 case ReplaceMode:
174- ParseParams ({{" old" , true }, {" new" , true }, {" count" , false }}, params);
192+ ParseParams ({{" old" , true }, {" new" , true }, {" count" , false , static_cast <int64_t >(0 )}}, params);
193+ break ;
194+ case TruncateMode:
195+ ParseParams ({{" length" , false , static_cast <int64_t >(255 )}, {" killwords" , false , false }, {" end" , false , std::string (" ..." )}, {" leeway" , false }}, params);
175196 break ;
176197 }
177198}
178199
179200InternalValue StringConverter::Filter (const InternalValue& baseVal, RenderContext& context)
180201{
181202 InternalValue result;
182-
203+
183204 auto isAlpha = ba::is_alpha ();
184205 auto isAlNum = ba::is_alnum ();
185206
@@ -199,7 +220,7 @@ InternalValue StringConverter::Filter(const InternalValue& baseVal, RenderContex
199220 fn (std::toupper (ch, std::locale ()));
200221 return ;
201222 }
202-
223+
203224 isDelim = !isAlNum (ch);
204225 fn (ch);
205226 });
@@ -217,7 +238,7 @@ InternalValue StringConverter::Filter(const InternalValue& baseVal, RenderContex
217238 isDelim = !isAlNum (ch);
218239 });
219240 result = wc;
220- break ;
241+ break ;
221242 }
222243 case UpperMode:
223244 result = ApplyConverter<GenericStringEncoder>(baseVal, [&isAlpha](auto ch, auto && fn) mutable {
@@ -236,6 +257,57 @@ InternalValue StringConverter::Filter(const InternalValue& baseVal, RenderContex
236257 });
237258 break ;
238259 case ReplaceMode:
260+ result = ApplyConverter (baseVal, [this , &context](auto str) {
261+ auto oldStr = GetAsSameString (str, this ->GetArgumentValue (" old" , context));
262+ auto newStr = GetAsSameString (str, this ->GetArgumentValue (" new" , context));
263+ auto count = ConvertToInt (this ->GetArgumentValue (" count" , context));
264+ if (count == 0 )
265+ ba::replace_all (str, oldStr, newStr);
266+ else
267+ {
268+ for (int64_t n = 0 ; n < count; ++ n)
269+ ba::replace_first (str, oldStr, newStr);
270+ }
271+ return str;
272+ });
273+ break ;
274+ case TruncateMode:
275+ result = ApplyConverter (baseVal, [this , &context, &isAlNum](auto str) {
276+ auto length = ConvertToInt (this ->GetArgumentValue (" length" , context));
277+ auto killWords = ConvertToBool (this ->GetArgumentValue (" killwords" , context));
278+ auto end = GetAsSameString (str, this ->GetArgumentValue (" end" , context));
279+ auto leeway = ConvertToInt (this ->GetArgumentValue (" leeway" , context), 5 );
280+ if (str.size () <= length)
281+ return str;
282+
283+ if (killWords)
284+ {
285+ if (str.size () > (length + leeway))
286+ {
287+ str.erase (str.begin () + length, str.end ());
288+ str += end;
289+ }
290+ return str;
291+ }
292+
293+ auto p = str.begin () + length;
294+ if (leeway != 0 )
295+ {
296+ for (; leeway != 0 && p != str.end () && isAlNum (*p); -- leeway, ++ p);
297+ if (p == str.end ())
298+ return str;
299+ }
300+
301+ if (isAlNum (*p))
302+ {
303+ for (; p != str.begin () && isAlNum (*p); -- p);
304+ }
305+ str.erase (p, str.end ());
306+ ba::trim_right (str);
307+ str += end;
308+
309+ return str;
310+ });
239311 break ;
240312 case UrlEncodeMode:
241313 result = Apply<UrlStringEncoder>(baseVal);
0 commit comments