From 1dc7ede233ed3a537cbd9b52c16ed73ddfd33bcc Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 7 May 2019 12:45:49 +0200 Subject: [PATCH 1/4] Add some move semantics. --- libdevcore/Whiskers.cpp | 16 ++++++++-------- libdevcore/Whiskers.h | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp index a6db35c8e..5ef89028d 100644 --- a/libdevcore/Whiskers.cpp +++ b/libdevcore/Whiskers.cpp @@ -30,12 +30,12 @@ using namespace std; using namespace dev; -Whiskers::Whiskers(string const& _template): -m_template(_template) +Whiskers::Whiskers(string _template): + m_template(move(_template)) { } -Whiskers& Whiskers::operator ()(string const& _parameter, string const& _value) +Whiskers& Whiskers::operator()(string _parameter, string _value) { assertThrow( m_parameters.count(_parameter) == 0, @@ -47,14 +47,14 @@ Whiskers& Whiskers::operator ()(string const& _parameter, string const& _value) WhiskersError, _parameter + " already set as list parameter." ); - m_parameters[_parameter] = _value; + m_parameters[move(_parameter)] = move(_value); return *this; } -Whiskers& Whiskers::operator ()( - string const& _listParameter, - vector> const& _values +Whiskers& Whiskers::operator()( + string _listParameter, + vector> _values ) { assertThrow( @@ -67,7 +67,7 @@ Whiskers& Whiskers::operator ()( WhiskersError, _listParameter + " already set as value parameter." ); - m_listParameters[_listParameter] = _values; + m_listParameters[move(_listParameter)] = move(_values); return *this; } diff --git a/libdevcore/Whiskers.h b/libdevcore/Whiskers.h index 21d46af4c..2ec4f88d2 100644 --- a/libdevcore/Whiskers.h +++ b/libdevcore/Whiskers.h @@ -57,14 +57,14 @@ public: using StringMap = std::map; using StringListMap = std::map>; - explicit Whiskers(std::string const& _template); + explicit Whiskers(std::string _template); /// Sets a single parameter, . - Whiskers& operator()(std::string const& _parameter, std::string const& _value); + Whiskers& operator()(std::string _parameter, std::string _value); /// Sets a list parameter, <#listName> . Whiskers& operator()( - std::string const& _listParameter, - std::vector const& _values + std::string _listParameter, + std::vector _values ); std::string render() const; From dfe07867e04b6025735018265cb553142783a651 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 7 May 2019 14:40:53 +0200 Subject: [PATCH 2/4] Refactor. --- libdevcore/Whiskers.cpp | 38 ++++++++++++++++---------------------- libdevcore/Whiskers.h | 2 ++ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp index 5ef89028d..f3198e322 100644 --- a/libdevcore/Whiskers.cpp +++ b/libdevcore/Whiskers.cpp @@ -37,18 +37,8 @@ Whiskers::Whiskers(string _template): Whiskers& Whiskers::operator()(string _parameter, string _value) { - assertThrow( - m_parameters.count(_parameter) == 0, - WhiskersError, - _parameter + " already set." - ); - assertThrow( - m_listParameters.count(_parameter) == 0, - WhiskersError, - _parameter + " already set as list parameter." - ); + checkParameterUnknown(_parameter); m_parameters[move(_parameter)] = move(_value); - return *this; } @@ -57,18 +47,8 @@ Whiskers& Whiskers::operator()( vector> _values ) { - assertThrow( - m_listParameters.count(_listParameter) == 0, - WhiskersError, - _listParameter + " already set." - ); - assertThrow( - m_parameters.count(_listParameter) == 0, - WhiskersError, - _listParameter + " already set as value parameter." - ); + checkParameterUnknown(_listParameter); m_listParameters[move(_listParameter)] = move(_values); - return *this; } @@ -77,6 +57,20 @@ string Whiskers::render() const return replace(m_template, m_parameters, m_listParameters); } +void Whiskers::checkParameterUnknown(string const& _parameter) +{ + assertThrow( + !m_parameters.count(_parameter), + WhiskersError, + _parameter + " already set as value parameter." + ); + assertThrow( + !m_listParameters.count(_parameter), + WhiskersError, + _parameter + " already set as list parameter." + ); +} + string Whiskers::replace( string const& _template, StringMap const& _parameters, diff --git a/libdevcore/Whiskers.h b/libdevcore/Whiskers.h index 2ec4f88d2..a4aef50b4 100644 --- a/libdevcore/Whiskers.h +++ b/libdevcore/Whiskers.h @@ -70,6 +70,8 @@ public: std::string render() const; private: + void checkParameterUnknown(std::string const& _parameter); + static std::string replace( std::string const& _template, StringMap const& _parameters, From 606551e4ddc8ed499ae4d51890c1b58d6cab270a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 7 May 2019 14:41:42 +0200 Subject: [PATCH 3/4] Add condition parameters. --- libdevcore/Whiskers.cpp | 39 +++++++++++++++++++++++++----- libdevcore/Whiskers.h | 53 +++++++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp index f3198e322..8a20f2cd7 100644 --- a/libdevcore/Whiskers.cpp +++ b/libdevcore/Whiskers.cpp @@ -42,6 +42,13 @@ Whiskers& Whiskers::operator()(string _parameter, string _value) return *this; } +Whiskers& Whiskers::operator()(string _parameter, bool _value) +{ + checkParameterUnknown(_parameter); + m_conditions[move(_parameter)] = _value; + return *this; +} + Whiskers& Whiskers::operator()( string _listParameter, vector> _values @@ -54,7 +61,7 @@ Whiskers& Whiskers::operator()( string Whiskers::render() const { - return replace(m_template, m_parameters, m_listParameters); + return replace(m_template, m_parameters, m_conditions, m_listParameters); } void Whiskers::checkParameterUnknown(string const& _parameter) @@ -64,6 +71,11 @@ void Whiskers::checkParameterUnknown(string const& _parameter) WhiskersError, _parameter + " already set as value parameter." ); + assertThrow( + !m_conditions.count(_parameter), + WhiskersError, + _parameter + " already set as condition parameter." + ); assertThrow( !m_listParameters.count(_parameter), WhiskersError, @@ -74,14 +86,17 @@ void Whiskers::checkParameterUnknown(string const& _parameter) string Whiskers::replace( string const& _template, StringMap const& _parameters, + map const& _conditions, map> const& _listParameters ) { using namespace boost; - static regex listOrTag("<([^#/>]+)>|<#([^>]+)>(.*?)"); + static regex listOrTag("<([^#/?!>]+)>|<#([^>]+)>(.*?)|<\\?([^>]+)>(.*?)((.*?))?"); return regex_replace(_template, listOrTag, [&](match_results _match) -> string { string tagName(_match[1]); + string listName(_match[2]); + string conditionName(_match[4]); if (!tagName.empty()) { assertThrow( @@ -93,20 +108,32 @@ string Whiskers::replace( ); return _parameters.at(tagName); } - else + else if (!listName.empty()) { - string listName(_match[2]); string templ(_match[3]); - assertThrow(!listName.empty(), WhiskersError, ""); assertThrow( _listParameters.count(listName), WhiskersError, "List parameter " + listName + " not set." ); string replacement; for (auto const& parameters: _listParameters.at(listName)) - replacement += replace(templ, joinMaps(_parameters, parameters)); + replacement += replace(templ, joinMaps(_parameters, parameters), _conditions); return replacement; } + else + { + assertThrow(!conditionName.empty(), WhiskersError, ""); + assertThrow( + _conditions.count(conditionName), + WhiskersError, "Condition parameter " + conditionName + " not set." + ); + return replace( + _conditions.at(conditionName) ? _match[5] : _match[7], + _parameters, + _conditions, + _listParameters + ); + } }); } diff --git a/libdevcore/Whiskers.h b/libdevcore/Whiskers.h index a4aef50b4..7944358a0 100644 --- a/libdevcore/Whiskers.h +++ b/libdevcore/Whiskers.h @@ -34,23 +34,35 @@ namespace dev DEV_SIMPLE_EXCEPTION(WhiskersError); -/// -/// Moustache-like templates. -/// -/// Usage: -/// std::vector> listValues(2); -/// listValues[0]["k"] = "key1"; -/// listValues[0]["v"] = "value1"; -/// listValues[1]["k"] = "key2"; -/// listValues[1]["v"] = "value2"; -/// auto s = Whiskers("\n<#list> -> \n") -/// ("p1", "HEAD") -/// ("list", listValues) -/// .render(); -/// -/// results in s == "HEAD\nkey1 -> value1\nkey2 -> value2\n" -/// -/// Note that lists cannot themselves contain lists - this would be a future feature. +/** + * Moustache-like templates. + * + * Usage: + * std::vector> listValues(2); + * listValues[0]["k"] = "key1"; + * listValues[0]["v"] = "value1"; + * listValues[1]["k"] = "key2"; + * listValues[1]["v"] = "value2"; + * auto s = Whiskers("y\n<#list> -> \n") + * ("p1", "HEAD") + * ("c", true) + * ("list", listValues) + * .render(); + * + * results in s == "HEAD\nkey1 -> value1\nkey2 -> value2\n" + * + * Note that lists cannot themselves contain lists - this would be a future feature. + * + * The elements are: + * - Regular parameter: + * just replaced + * - Condition parameter: ......, where "" is optional + * replaced (and recursively expanded) by the first part if the condition is true + * and by the second (or empty string if missing) if the condition is false + * - List parameter: <#list>... + * The part between the tags is repeated as often as values are provided + * in the mapping. Each list element can have its own parameter -> value mapping. + */ class Whiskers { public: @@ -59,8 +71,11 @@ public: explicit Whiskers(std::string _template); - /// Sets a single parameter, . + /// Sets a single regular parameter, . Whiskers& operator()(std::string _parameter, std::string _value); + Whiskers& operator()(std::string _parameter, char const* _value) { return (*this)(_parameter, std::string{_value}); } + /// Sets a condition parameter, ...... + Whiskers& operator()(std::string _parameter, bool _value); /// Sets a list parameter, <#listName> . Whiskers& operator()( std::string _listParameter, @@ -75,6 +90,7 @@ private: static std::string replace( std::string const& _template, StringMap const& _parameters, + std::map const& _conditions, StringListMap const& _listParameters = StringListMap() ); @@ -83,6 +99,7 @@ private: std::string m_template; StringMap m_parameters; + std::map m_conditions; StringListMap m_listParameters; }; From a060dce9a82037ce5842071da863c8bb19357182 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 7 May 2019 14:41:47 +0200 Subject: [PATCH 4/4] Tests. --- test/libdevcore/Whiskers.cpp | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/libdevcore/Whiskers.cpp b/test/libdevcore/Whiskers.cpp index b12acdd70..8a785774e 100644 --- a/test/libdevcore/Whiskers.cpp +++ b/test/libdevcore/Whiskers.cpp @@ -55,6 +55,65 @@ BOOST_AUTO_TEST_CASE(tag_unavailable) BOOST_CHECK_THROW(m.render(), WhiskersError); } +BOOST_AUTO_TEST_CASE(list_unavailable) +{ + string templ = "<#b>"; + Whiskers m(templ); + BOOST_CHECK_THROW(m.render(), WhiskersError); +} + +BOOST_AUTO_TEST_CASE(name_type_collision) +{ + string templ = "<#b>"; + Whiskers m(templ); + m("b", "x"); + BOOST_CHECK_THROW(m("b", vector>{}), WhiskersError); +} + +BOOST_AUTO_TEST_CASE(conditional) +{ + string templ = "X"; + BOOST_CHECK_EQUAL(Whiskers(templ)("b", true).render(), "X"); + BOOST_CHECK_EQUAL(Whiskers(templ)("b", false).render(), ""); +} + +BOOST_AUTO_TEST_CASE(conditional_with_else) +{ + string templ = "XY"; + BOOST_CHECK_EQUAL(Whiskers(templ)("b", true).render(), "X"); + BOOST_CHECK_EQUAL(Whiskers(templ)("b", false).render(), "Y"); +} + +BOOST_AUTO_TEST_CASE(conditional_plus_params) +{ + string templ = " - _^ - "; + Whiskers m1(templ); + m1("b", true); + m1("r", "R"); + m1("t", "T"); + BOOST_CHECK_EQUAL(m1.render(), " - _R - "); + + Whiskers m2(templ); + m2("b", false); + m2("r", "R"); + m2("t", "T"); + BOOST_CHECK_EQUAL(m2.render(), " - ^T - "); +} + +BOOST_AUTO_TEST_CASE(conditional_plus_list) +{ + string templ = " - _<#l><#l> - "; + Whiskers m(templ); + m("b", false); + vector> list(2); + list[0]["x"] = "1"; + list[0]["y"] = "a"; + list[1]["x"] = "2"; + list[1]["y"] = "b"; + m("l", list); + BOOST_CHECK_EQUAL(m.render(), " - ab - "); +} + BOOST_AUTO_TEST_CASE(complicated_replacement) { string templ = "a x \n >.";