mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Whiskers template system
This commit is contained in:
		
							parent
							
								
									f90a514f80
								
							
						
					
					
						commit
						cb7021881a
					
				| @ -10,6 +10,7 @@ function(eth_apply TARGET REQUIRED SUBMODULE) | |||||||
| 		target_link_libraries(${TARGET} ${Boost_RANDOM_LIBRARIES}) | 		target_link_libraries(${TARGET} ${Boost_RANDOM_LIBRARIES}) | ||||||
| 		target_link_libraries(${TARGET} ${Boost_FILESYSTEM_LIBRARIES}) | 		target_link_libraries(${TARGET} ${Boost_FILESYSTEM_LIBRARIES}) | ||||||
| 		target_link_libraries(${TARGET} ${Boost_SYSTEM_LIBRARIES}) | 		target_link_libraries(${TARGET} ${Boost_SYSTEM_LIBRARIES}) | ||||||
|  | 		target_link_libraries(${TARGET} ${Boost_REGEX_LIBRARIES}) | ||||||
| 
 | 
 | ||||||
| 		if (DEFINED MSVC) | 		if (DEFINED MSVC) | ||||||
| 			target_link_libraries(${TARGET} ${Boost_CHRONO_LIBRARIES}) | 			target_link_libraries(${TARGET} ${Boost_CHRONO_LIBRARIES}) | ||||||
|  | |||||||
							
								
								
									
										127
									
								
								libdevcore/Whiskers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								libdevcore/Whiskers.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | /** @file Whiskers.cpp
 | ||||||
|  |  * @author Chris <chis@ethereum.org> | ||||||
|  |  * @date 2017 | ||||||
|  |  * | ||||||
|  |  * Moustache-like templates. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/Whiskers.h> | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/Assertions.h> | ||||||
|  | 
 | ||||||
|  | #include <boost/regex.hpp> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace dev; | ||||||
|  | 
 | ||||||
|  | Whiskers::Whiskers(string const& _template): | ||||||
|  | m_template(_template) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Whiskers& Whiskers::operator ()(string const& _parameter, string const& _value) | ||||||
|  | { | ||||||
|  | 	assertThrow( | ||||||
|  | 		m_parameters.count(_parameter) == 0, | ||||||
|  | 		WhiskersError, | ||||||
|  | 		_parameter + " already set." | ||||||
|  | 	); | ||||||
|  | 	assertThrow( | ||||||
|  | 		m_listParameters.count(_parameter) == 0, | ||||||
|  | 		WhiskersError, | ||||||
|  | 		_parameter + " already set as list parameter." | ||||||
|  | 	); | ||||||
|  | 	m_parameters[_parameter] = _value; | ||||||
|  | 
 | ||||||
|  | 	return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Whiskers& Whiskers::operator ()( | ||||||
|  | 	string const& _listParameter, | ||||||
|  | 	vector<map<string, string>> const& _values | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	assertThrow( | ||||||
|  | 		m_listParameters.count(_listParameter) == 0, | ||||||
|  | 		WhiskersError, | ||||||
|  | 		_listParameter + " already set." | ||||||
|  | 	); | ||||||
|  | 	assertThrow( | ||||||
|  | 		m_parameters.count(_listParameter) == 0, | ||||||
|  | 		WhiskersError, | ||||||
|  | 		_listParameter + " already set as value parameter." | ||||||
|  | 	); | ||||||
|  | 	m_listParameters[_listParameter] = _values; | ||||||
|  | 
 | ||||||
|  | 	return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string Whiskers::render() const | ||||||
|  | { | ||||||
|  | 	return replace(m_template, m_parameters, m_listParameters); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string Whiskers::replace( | ||||||
|  | 	string const& _template, | ||||||
|  | 	StringMap const& _parameters, | ||||||
|  | 	map<string, vector<StringMap>> const& _listParameters | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	using namespace boost; | ||||||
|  | 	static regex listOrTag("<([^#/>]+)>|<#([^>]+)>(.*?)</\\2>"); | ||||||
|  | 	return regex_replace(_template, listOrTag, [&](match_results<string::const_iterator> _match) -> string | ||||||
|  | 	{ | ||||||
|  | 		string tagName(_match[1]); | ||||||
|  | 		if (!tagName.empty()) | ||||||
|  | 		{ | ||||||
|  | 			assertThrow(_parameters.count(tagName), WhiskersError, "Tag " + tagName + " not found."); | ||||||
|  | 			return _parameters.at(tagName); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			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)); | ||||||
|  | 			return replacement; | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Whiskers::StringMap Whiskers::joinMaps( | ||||||
|  | 	Whiskers::StringMap const& _a, | ||||||
|  | 	Whiskers::StringMap const& _b | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	Whiskers::StringMap ret = _a; | ||||||
|  | 	for (auto const& x: _b) | ||||||
|  | 		assertThrow( | ||||||
|  | 			ret.insert(x).second, | ||||||
|  | 			WhiskersError, | ||||||
|  | 			"Parameter collision" | ||||||
|  | 		); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										87
									
								
								libdevcore/Whiskers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								libdevcore/Whiskers.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,87 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | /** @file Whiskers.h
 | ||||||
|  |  * @author Chris <chis@ethereum.org> | ||||||
|  |  * @date 2017 | ||||||
|  |  * | ||||||
|  |  * Moustache-like templates. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/Exceptions.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | namespace dev | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | DEV_SIMPLE_EXCEPTION(WhiskersError); | ||||||
|  | 
 | ||||||
|  | ///
 | ||||||
|  | /// Moustache-like templates.
 | ||||||
|  | ///
 | ||||||
|  | /// Usage:
 | ||||||
|  | ///     std::vector<std::map<std::string, std::string>> listValues(2);
 | ||||||
|  | ///     listValues[0]["k"] = "key1";
 | ||||||
|  | ///     listValues[0]["v"] = "value1";
 | ||||||
|  | ///     listValues[1]["k"] = "key2";
 | ||||||
|  | ///     listValues[1]["v"] = "value2";
 | ||||||
|  | ///     auto s = Whiskers("<p1>\n<#list><k> -> <v>\n</list>")
 | ||||||
|  | ///         ("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.
 | ||||||
|  | class Whiskers | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	using StringMap = std::map<std::string, std::string>; | ||||||
|  | 	using StringListMap = std::map<std::string, std::vector<StringMap>>; | ||||||
|  | 
 | ||||||
|  | 	explicit Whiskers(std::string const& _template); | ||||||
|  | 
 | ||||||
|  | 	/// Sets a single parameter, <paramName>.
 | ||||||
|  | 	Whiskers& operator()(std::string const& _parameter, std::string const& _value); | ||||||
|  | 	/// Sets a list parameter, <#listName> </listName>.
 | ||||||
|  | 	Whiskers& operator()( | ||||||
|  | 		std::string const& _listParameter, | ||||||
|  | 		std::vector<StringMap> const& _values | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	std::string render() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	static std::string replace( | ||||||
|  | 		std::string const& _template, | ||||||
|  | 		StringMap const& _parameters, | ||||||
|  | 		StringListMap const& _listParameters = StringListMap() | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	/// Joins the two maps throwing an exception if two keys are equal.
 | ||||||
|  | 	static StringMap joinMaps(StringMap const& _a, StringMap const& _b); | ||||||
|  | 
 | ||||||
|  | 	std::string m_template; | ||||||
|  | 	StringMap m_parameters; | ||||||
|  | 	StringListMap m_listParameters; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										127
									
								
								test/libdevcore/MiniMoustache.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								test/libdevcore/MiniMoustache.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | /**
 | ||||||
|  |  * Unit tests for the mini moustache class. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <libdevcore/Whiskers.h> | ||||||
|  | 
 | ||||||
|  | #include "../TestHelper.h" | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | 
 | ||||||
|  | namespace dev | ||||||
|  | { | ||||||
|  | namespace test | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_SUITE(WhiskersTest) | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(no_templates) | ||||||
|  | { | ||||||
|  | 	string templ = "this text does not contain templates"; | ||||||
|  | 	BOOST_CHECK_EQUAL(Whiskers(templ).render(), templ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(basic_replacement) | ||||||
|  | { | ||||||
|  | 	string templ = "a <b> x <c> -> <d>."; | ||||||
|  | 	string result = Whiskers(templ) | ||||||
|  | 		("b", "BE") | ||||||
|  | 		("c", "CE") | ||||||
|  | 		("d", "DE") | ||||||
|  | 		.render(); | ||||||
|  | 	BOOST_CHECK_EQUAL(result, "a BE x CE -> DE."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(tag_unavailable) | ||||||
|  | { | ||||||
|  | 	string templ = "<b>"; | ||||||
|  | 	Whiskers m(templ); | ||||||
|  | 	BOOST_CHECK_THROW(m.render(), WhiskersError); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(complicated_replacement) | ||||||
|  | { | ||||||
|  | 	string templ = "a <b> x <complicated> \n <nes<ted>>."; | ||||||
|  | 	string result = Whiskers(templ) | ||||||
|  | 		("b", "BE") | ||||||
|  | 		("complicated", "CO<M>PL") | ||||||
|  | 		("nes<ted", "NEST") | ||||||
|  | 		.render(); | ||||||
|  | 	BOOST_CHECK_EQUAL(result, "a BE x CO<M>PL \n NEST>."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(non_existing_list) | ||||||
|  | { | ||||||
|  | 	string templ = "a <#b></b>"; | ||||||
|  | 	Whiskers m(templ); | ||||||
|  | 	BOOST_CHECK_THROW(m.render(), WhiskersError); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(empty_list) | ||||||
|  | { | ||||||
|  | 	string templ = "a <#b></b>x"; | ||||||
|  | 	string result = Whiskers(templ)("b", vector<Whiskers::StringMap>{}).render(); | ||||||
|  | 	BOOST_CHECK_EQUAL(result, "a x"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(list) | ||||||
|  | { | ||||||
|  | 	string templ = "a<#b>( <g> - <h> )</b>x"; | ||||||
|  | 	vector<map<string, string>> list(2); | ||||||
|  | 	list[0]["g"] = "GE"; | ||||||
|  | 	list[0]["h"] = "H"; | ||||||
|  | 	list[1]["g"] = "2GE"; | ||||||
|  | 	list[1]["h"] = "2H"; | ||||||
|  | 	string result = Whiskers(templ)("b", list).render(); | ||||||
|  | 	BOOST_CHECK_EQUAL(result, "a( GE - H )( 2GE - 2H )x"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(recursive_list) | ||||||
|  | { | ||||||
|  | 	// Check that templates resulting from lists are not expanded again
 | ||||||
|  | 	string templ = "a<#b> 1<g>3 </b><x>"; | ||||||
|  | 	vector<map<string, string>> list(1); | ||||||
|  | 	list[0]["g"] = "<x>"; | ||||||
|  | 	string result = Whiskers(templ)("x", "X")("b", list).render(); | ||||||
|  | 	BOOST_CHECK_EQUAL(result, "a 1<x>3 X"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(list_can_access_upper) | ||||||
|  | { | ||||||
|  | 	string templ = "<#b>(<a>)</b>"; | ||||||
|  | 	vector<map<string, string>> list(2); | ||||||
|  | 	Whiskers m(templ); | ||||||
|  | 	string result = m("a", "A")("b", list).render(); | ||||||
|  | 	BOOST_CHECK_EQUAL(result, "(A)(A)"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_CASE(parameter_collision) | ||||||
|  | { | ||||||
|  | 	string templ = "a <#b></b>"; | ||||||
|  | 	vector<map<string, string>> list(1); | ||||||
|  | 	list[0]["a"] = "x"; | ||||||
|  | 	Whiskers m(templ); | ||||||
|  | 	m("a", "X")("b", list); | ||||||
|  | 	BOOST_CHECK_THROW(m.render(), WhiskersError); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | BOOST_AUTO_TEST_SUITE_END() | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user