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_FILESYSTEM_LIBRARIES}) | ||||
| 		target_link_libraries(${TARGET} ${Boost_SYSTEM_LIBRARIES}) | ||||
| 		target_link_libraries(${TARGET} ${Boost_REGEX_LIBRARIES}) | ||||
| 
 | ||||
| 		if (DEFINED MSVC) | ||||
| 			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