diff options
Diffstat (limited to 'lib/mstch')
213 files changed, 2483 insertions, 0 deletions
diff --git a/lib/mstch/CMakeLists.txt b/lib/mstch/CMakeLists.txt new file mode 100644 index 0000000..8d8e0c7 --- /dev/null +++ b/lib/mstch/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.0.2) +project(mstch) + +option(WITH_UNIT_TESTS "enable building unit test executable" OFF) +option(WITH_BENCHMARK "enable building benchmark executable" OFF) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) +set(CMAKE_BUILD_TYPE Release) + +set(mstch_VERSION 1.0.1) + +if(NOT MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra -O3") +endif() + +add_subdirectory(src) + +if(WITH_UNIT_TESTS) + enable_testing() + add_subdirectory(vendor/headerize) + add_subdirectory(test) +endif() + +if(WITH_BENCHMARK) + add_subdirectory(vendor/benchmark) + add_subdirectory(benchmark) +endif() diff --git a/lib/mstch/LICENSE b/lib/mstch/LICENSE new file mode 100644 index 0000000..b3011a2 --- /dev/null +++ b/lib/mstch/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Daniel Sipka + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/lib/mstch/README.md b/lib/mstch/README.md new file mode 100644 index 0000000..ff806c8 --- /dev/null +++ b/lib/mstch/README.md @@ -0,0 +1,281 @@ +# mstch - {{mustache}} templates in C++11 + + + +mstch is a complete implementation of [{{mustache}}](http://mustache.github.io/) +templates using modern C++. It's compliant with [specifications](https://github.com/mustache/spec) +v1.1.3, including the lambda module. + +[](http://melpon.org/wandbox/permlink/EqyOe7IBRYPGVk5f) +[](http://badge.fury.io/gh/no1msd%2Fmstch) +[](https://travis-ci.org/no1msd/mstch) +[](https://ci.appveyor.com/project/no1msd/mstch) + +## Supported features + +mstch supports the complete feature set described in the `mustache(5)` [manpage](http://mustache.github.com/mustache.5.html): + + - JSON-like data structure using [Boost.Variant](http://www.boost.org/libs/variant) + - variables, sections, inverted sections + - partials + - changing the delimiter + - C++11 lambdas + - C++ objects as view models + +## Basic usage + +```c++ +#include <iostream> +#include <mstch/mstch.hpp> + +int main() { + std::string view{"{{#names}}Hi {{name}}!\n{{/names}}"}; + mstch::map context{ + {"names", mstch::array{ + mstch::map{{"name", std::string{"Chris"}}}, + mstch::map{{"name", std::string{"Mark"}}}, + mstch::map{{"name", std::string{"Scott"}}}, + }} + }; + + std::cout << mstch::render(view, context) << std::endl; + + return 0; +} + +``` + +The output of this example will be: + +```html +Hi Chris! +Hi Mark! +Hi Scott! +``` + +### Data structure + +The types in the example above, `mstch::array` and `mstch::map` are actually +aliases for standard types: + +```c++ +using map = std::map<const std::string, node>; +using array = std::vector<node>; +``` + +`mstch::node` is a `boost::variant` that can hold a `std::string`, `int`, +`double`, `bool`, `mstch::lambda` or a `std::shared_ptr<mstch::object>` +(see below), also a map or an array recursively. Essentially it works just like +a JSON object. + +Note that when using a `std::string` as value you must explicitly specify the +type, since a `const char*` literal like `"foobar"` would be implicitly +converted to `bool`. Alternatively you can use [C++14 string_literals](http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s) +if your compiler supports it. + +## Advanced usage + +### Partials + +Partials can be passed in a `std::map` as the third parameter of the +`mstch::render` function: + +```c++ +std::string view{"{{#names}}{{> user}}{{/names}}"}; +std::string user_view{"<strong>{{name}}\n</strong>"}; +mstch::map context{ + {"names", mstch::array{ + mstch::map{{"name", std::string{"Chris"}}}, + mstch::map{{"name", std::string{"Mark"}}}, + mstch::map{{"name", std::string{"Scott"}}}, + }} +}; + +std::cout << mstch::render(view, context, {{"user", user_view}}) << std::endl; +``` + +Output: + +```html +<strong>Chris</strong> +<strong>Mark</strong> +<strong>Scott</strong> +``` + +### Lambdas + +C++11 lambda expressions can be used to add logic to your templates. Like a +`const char*` literal, lambdas can be implicitly converted to `bool`, so they +must be wrapped in a `mstch::lambda` object when used in a `mstch::node`. The +lambda expression passed to `mstch::lambda` must itself return a `mstch::node`. +The returned node will be rendered to a string, then it will be parsed as a +template. + +The lambda expression accepts either no parameters: + +```c++ +std::string view{"Hello {{lambda}}!"}; +mstch::map context{ + {"lambda", mstch::lambda{[]() -> mstch::node { + return std::string{"World"}; + }}} +}; + +std::cout << mstch::render(view, context) << std::endl; +``` + +Output: + +```html +Hello World! +``` + +Or it accepts a `const std::string&` that gets the unrendered literal block: + +```c++ +std::string view{"{{#bold}}{{yay}} :){{/bold}}"}; +mstch::map context{ + {"yay", std::string{"Yay!"}}, + {"bold", mstch::lambda{[](const std::string& text) -> mstch::node { + return "<b>" + text + "</b>"; + }}} +}; + +std::cout << mstch::render(view, context) << std::endl; +``` + +Output: + +```html +<b>Yay! :)</b> +``` + +### Objects + +Custom objects can also be used as context for rendering templates. The class +must inherit from `mstch::object`, and register it's exported methods with +`register_methods`. Exported methods must have the return type of `mstch::node`. +Objects must be created as a `std::shared_ptr`. + +```c++ +class example: public mstch::object { + public: + example(): m_value(1) { + register_methods(this, { + {"count", &example::count}, + {"names", &example::names} + }); + } + + mstch::node count() { + return m_value++; + } + + mstch::node names() { + return mstch::array{ + std::string{"Chris"}, std::string{"Mark"}, std::string{"Scott"}}; + } + + private: + int m_value; +}; + +std::string view{"{{#names}}<b>{{count}}</b>: {{.}}\n{{/names}}"}; +const auto context = std::make_shared<example>(); + +std::cout << mstch::render(view, context) << std::endl; +``` + +Output: + +```html +<b>1</b>: Chris +<b>2</b>: Mark +<b>3</b>: Scott +``` + +### Custom escape function + +By default, mstch uses HTML escaping on the output, as per specification. This +is not useful if your output is not HTML, so mstch provides a way to supply +your own escape implementation. Just assign any callable object to the static +`mstch::config::escape`, which is an initially empty +`std::function<std::string(const std::string&)>`. + +For example you can turn off escaping entirely with a lambda: + +```c++ +mstch::config::escape = [](const std::string& str) -> std::string { + return str; +}; +``` + +## Requirements + + - A C++ compiler with decent C++11 support. Currently tested with: + - GCC 4.7, 4.8, 4.9, 5.1 + - clang 3.5, 3.6, 3.7 (both libstdc++ and libc++ are supported) + - MSVC 2013, 2015 + - Boost 1.54+ for [Boost.Variant](http://www.boost.org/libs/variant) + - CMake 3.0+ for building + +## Using mstch in your project + +If you are using CMake, the easiest way to include mstch in your project is to +copy the whole directory to your source tree, and use `add_subdirectory` in your +CMakeLists.txt. This will set a variable named `mstch_INCLUDE_DIR` that contains +its include path, and add a static library target named `mstch`. For example: + +```cmake +add_subdirectory(external/mstch) +include_directories(${mstch_INCLUDE_DIR}) +target_link_libraries(your_project mstch) +``` + +If you prefer to install the library globally, you can simply do the following +from the root of the source tree: + +```bash + $ mkdir build + $ cd build + $ cmake .. + $ make + $ make install +``` + +The install command may require root privileges. This will also install CMake +config files, so you can use use `find_package` in your CMakeLists.txt: + +```cmake +find_package(mstch) +target_link_libraries(your_project mstch::mstch) +``` + +## Running the unit tests + +Unit tests are using the [Catch](https://github.com/philsquared/Catch) framework +and [rapidjson](http://rapidjson.org/) to parse the +[Mustache specifications](https://github.com/mustache/spec), all of which are +included in the repository as git submodules. Various +[Boost](http://www.boost.org/) libraries are also required to build them. + +Don't forget to initialize submodules: + +```bash + $ git submodule init + $ git submodule update +``` + +To build and run the unit tests: + +```bash + $ mkdir build + $ cd build + $ cmake -DWITH_UNIT_TESTS=ON .. + $ make + $ make test +``` + +## License + +mstch is licensed under the [MIT license](https://github.com/no1msd/mstch/blob/master/LICENSE). diff --git a/lib/mstch/benchmark/CMakeLists.txt b/lib/mstch/benchmark/CMakeLists.txt new file mode 100644 index 0000000..fa47f0d --- /dev/null +++ b/lib/mstch/benchmark/CMakeLists.txt @@ -0,0 +1,6 @@ +include_directories( + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/vendor/benchmark/include) + +add_executable(mstch_benchmark benchmark_main.cpp) +target_link_libraries(mstch_benchmark mstch benchmark) diff --git a/lib/mstch/benchmark/benchmark_main.cpp b/lib/mstch/benchmark/benchmark_main.cpp new file mode 100644 index 0000000..ad7f8d5 --- /dev/null +++ b/lib/mstch/benchmark/benchmark_main.cpp @@ -0,0 +1,26 @@ +#include <benchmark/benchmark.h> + +#include "mstch/mstch.hpp" + +static void basic_usage(benchmark::State& state) { + std::string comment_tmp{ + "<div class=\"comments\"><h3>{{header}}</h3><ul>" + "{{#comments}}<li class=\"comment\"><h5>{{name}}</h5>" + "<p>{{body}}</p></li>{{/comments}}</ul></div>"}; + + auto comment_view = mstch::map{ + {"header", std::string{"My Post Comments"}}, + {"comments", mstch::array{ + mstch::map{{"name", std::string{"Joe"}}, {"body", std::string{"Thanks for this post!"}}}, + mstch::map{{"name", std::string{"Sam"}}, {"body", std::string{"Thanks for this post!"}}}, + mstch::map{{"name", std::string{"Heather"}}, {"body", std::string{"Thanks for this post!"}}}, + mstch::map{{"name", std::string{"Kathy"}}, {"body", std::string{"Thanks for this post!"}}}, + mstch::map{{"name", std::string{"George"}}, {"body", std::string{"Thanks for this post!"}}}}}}; + + while (state.KeepRunning()) + mstch::render(comment_tmp, comment_view); +} + +BENCHMARK(basic_usage); + +BENCHMARK_MAIN(); diff --git a/lib/mstch/cmake/mstch-config.cmake b/lib/mstch/cmake/mstch-config.cmake new file mode 100644 index 0000000..b2a50e0 --- /dev/null +++ b/lib/mstch/cmake/mstch-config.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/mstch-targets.cmake")
\ No newline at end of file diff --git a/lib/mstch/include/mstch/mstch.hpp b/lib/mstch/include/mstch/mstch.hpp new file mode 100644 index 0000000..58d3330 --- /dev/null +++ b/lib/mstch/include/mstch/mstch.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include <vector> +#include <map> +#include <string> +#include <memory> +#include <functional> + +#include <boost/variant.hpp> + +namespace mstch { + +struct config { + static std::function<std::string(const std::string&)> escape; +}; + +namespace internal { + +template<class N> +class object_t { + public: + const N& at(const std::string& name) const { + cache[name] = (methods.at(name))(); + return cache[name]; + } + + bool has(const std::string name) const { + return methods.count(name) != 0; + } + + protected: + template<class S> + void register_methods(S* s, std::map<std::string,N(S::*)()> methods) { + for(auto& item: methods) + this->methods.insert({item.first, std::bind(item.second, s)}); + } + + private: + std::map<std::string, std::function<N()>> methods; + mutable std::map<std::string, N> cache; +}; + +template<class T, class N> +class is_fun { + private: + using not_fun = char; + using fun_without_args = char[2]; + using fun_with_args = char[3]; + template <typename U, U> struct really_has; + template <typename C> static fun_without_args& test( + really_has<N(C::*)() const, &C::operator()>*); + template <typename C> static fun_with_args& test( + really_has<N(C::*)(const std::string&) const, + &C::operator()>*); + template <typename> static not_fun& test(...); + + public: + static bool const no_args = sizeof(test<T>(0)) == sizeof(fun_without_args); + static bool const has_args = sizeof(test<T>(0)) == sizeof(fun_with_args); +}; + +template<class N> +using node_renderer = std::function<std::string(const N& n)>; + +template<class N> +class lambda_t { + public: + template<class F> + lambda_t(F f, typename std::enable_if<is_fun<F, N>::no_args>::type* = 0): + fun([f](node_renderer<N> renderer, const std::string&) { + return renderer(f()); + }) + { + } + + template<class F> + lambda_t(F f, typename std::enable_if<is_fun<F, N>::has_args>::type* = 0): + fun([f](node_renderer<N> renderer, const std::string& text) { + return renderer(f(text)); + }) + { + } + + std::string operator()(node_renderer<N> renderer, + const std::string& text = "") const + { + return fun(renderer, text); + } + + private: + std::function<std::string(node_renderer<N> renderer, const std::string&)> fun; +}; + +} + +using node = boost::make_recursive_variant< + std::nullptr_t, std::string, int, double, bool, + internal::lambda_t<boost::recursive_variant_>, + std::shared_ptr<internal::object_t<boost::recursive_variant_>>, + std::map<const std::string, boost::recursive_variant_>, + std::vector<boost::recursive_variant_>>::type; +using object = internal::object_t<node>; +using lambda = internal::lambda_t<node>; +using map = std::map<const std::string, node>; +using array = std::vector<node>; + +std::string render( + const std::string& tmplt, + const node& root, + const std::map<std::string,std::string>& partials = + std::map<std::string,std::string>()); + +} diff --git a/lib/mstch/src/CMakeLists.txt b/lib/mstch/src/CMakeLists.txt new file mode 100644 index 0000000..6517fc4 --- /dev/null +++ b/lib/mstch/src/CMakeLists.txt @@ -0,0 +1,64 @@ +find_package(Boost 1.54 REQUIRED) + +set(mstch_INCLUDE_DIR + ${PROJECT_SOURCE_DIR}/include CACHE STRING "mstch include directory") + +include_directories( + ${mstch_INCLUDE_DIR} + ${Boost_INCLUDE_DIR}) + +set(SRC + state/in_section.cpp + state/outside_section.cpp + state/render_state.hpp + visitor/get_token.hpp + visitor/has_token.hpp + visitor/is_node_empty.hpp + visitor/render_node.hpp + visitor/render_section.hpp + mstch.cpp + render_context.cpp + template_type.cpp + token.cpp + utils.cpp) + +add_library(mstch STATIC ${SRC}) + +set_property(TARGET mstch PROPERTY VERSION ${mstch_VERSION}) + +install( + TARGETS mstch EXPORT mstchTargets + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib) + +install( + FILES "${PROJECT_SOURCE_DIR}/include/mstch/mstch.hpp" + DESTINATION include/mstch + COMPONENT Devel) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config-version.cmake" + VERSION ${mstch_VERSION} + COMPATIBILITY AnyNewerVersion) + +export( + EXPORT mstchTargets + FILE "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-targets.cmake" + NAMESPACE mstch::) + +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/mstch-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config.cmake") + +install( + EXPORT mstchTargets + FILE mstch-targets.cmake + NAMESPACE mstch:: + DESTINATION lib/cmake/mstch) + +install(FILES + "${PROJECT_SOURCE_DIR}/cmake/mstch-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/mstch/mstch-config-version.cmake" + DESTINATION lib/cmake/mstch + COMPONENT Devel) diff --git a/lib/mstch/src/mstch.cpp b/lib/mstch/src/mstch.cpp new file mode 100644 index 0000000..4d84e97 --- /dev/null +++ b/lib/mstch/src/mstch.cpp @@ -0,0 +1,20 @@ +#include <iostream> + +#include "mstch/mstch.hpp" +#include "render_context.hpp" + +using namespace mstch; + +std::function<std::string(const std::string&)> mstch::config::escape; + +std::string mstch::render( + const std::string& tmplt, + const node& root, + const std::map<std::string,std::string>& partials) +{ + std::map<std::string, template_type> partial_templates; + for (auto& partial: partials) + partial_templates.insert({partial.first, {partial.second}}); + + return render_context(root, partial_templates).render(tmplt); +} diff --git a/lib/mstch/src/render_context.cpp b/lib/mstch/src/render_context.cpp new file mode 100644 index 0000000..90b2ffc --- /dev/null +++ b/lib/mstch/src/render_context.cpp @@ -0,0 +1,72 @@ +#include "render_context.hpp" +#include "state/outside_section.hpp" +#include "visitor/get_token.hpp" + +using namespace mstch; + +const mstch::node render_context::null_node; + +render_context::push::push(render_context& context, const mstch::node& node): + m_context(context) +{ + context.m_nodes.emplace_front(node); + context.m_node_ptrs.emplace_front(&node); + context.m_state.push(std::unique_ptr<render_state>(new outside_section)); +} + +render_context::push::~push() { + m_context.m_nodes.pop_front(); + m_context.m_node_ptrs.pop_front(); + m_context.m_state.pop(); +} + +std::string render_context::push::render(const template_type& templt) { + return m_context.render(templt); +} + +render_context::render_context( + const mstch::node& node, + const std::map<std::string, template_type>& partials): + m_partials(partials), m_nodes(1, node), m_node_ptrs(1, &node) +{ + m_state.push(std::unique_ptr<render_state>(new outside_section)); +} + +const mstch::node& render_context::find_node( + const std::string& token, + std::list<node const*> current_nodes) +{ + if (token != "." && token.find('.') != std::string::npos) + return find_node(token.substr(token.rfind('.') + 1), + {&find_node(token.substr(0, token.rfind('.')), current_nodes)}); + else + for (auto& node: current_nodes) + if (visit(has_token(token), *node)) + return visit(get_token(token, *node), *node); + return null_node; +} + +const mstch::node& render_context::get_node(const std::string& token) { + return find_node(token, m_node_ptrs); +} + +std::string render_context::render( + const template_type& templt, const std::string& prefix) +{ + std::string output; + bool prev_eol = true; + for (auto& token: templt) { + if (prev_eol && prefix.length() != 0) + output += m_state.top()->render(*this, {prefix}); + output += m_state.top()->render(*this, token); + prev_eol = token.eol(); + } + return output; +} + +std::string render_context::render_partial( + const std::string& partial_name, const std::string& prefix) +{ + return m_partials.count(partial_name) ? + render(m_partials.at(partial_name), prefix) : ""; +} diff --git a/lib/mstch/src/render_context.hpp b/lib/mstch/src/render_context.hpp new file mode 100644 index 0000000..b97d41b --- /dev/null +++ b/lib/mstch/src/render_context.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <deque> +#include <list> +#include <sstream> +#include <string> +#include <stack> + +#include "mstch/mstch.hpp" +#include "state/render_state.hpp" +#include "template_type.hpp" + +namespace mstch { + +class render_context { + public: + class push { + public: + push(render_context& context, const mstch::node& node = {}); + ~push(); + std::string render(const template_type& templt); + private: + render_context& m_context; + }; + + render_context( + const mstch::node& node, + const std::map<std::string, template_type>& partials); + const mstch::node& get_node(const std::string& token); + std::string render( + const template_type& templt, const std::string& prefix = ""); + std::string render_partial( + const std::string& partial_name, const std::string& prefix); + template<class T, class... Args> + void set_state(Args&& ... args) { + m_state.top() = std::unique_ptr<render_state>( + new T(std::forward<Args>(args)...)); + } + + private: + static const mstch::node null_node; + const mstch::node& find_node( + const std::string& token, + std::list<node const*> current_nodes); + std::map<std::string, template_type> m_partials; + std::deque<mstch::node> m_nodes; + std::list<const mstch::node*> m_node_ptrs; + std::stack<std::unique_ptr<render_state>> m_state; +}; + +} diff --git a/lib/mstch/src/state/in_section.cpp b/lib/mstch/src/state/in_section.cpp new file mode 100644 index 0000000..a139913 --- /dev/null +++ b/lib/mstch/src/state/in_section.cpp @@ -0,0 +1,34 @@ +#include "in_section.hpp" +#include "outside_section.hpp" +#include "visitor/is_node_empty.hpp" +#include "visitor/render_section.hpp" + +using namespace mstch; + +in_section::in_section(type type, const token& start_token): + m_type(type), m_start_token(start_token), m_skipped_openings(0) +{ +} + +std::string in_section::render(render_context& ctx, const token& token) { + if (token.token_type() == token::type::section_close) + if (token.name() == m_start_token.name() && m_skipped_openings == 0) { + auto& node = ctx.get_node(m_start_token.name()); + std::string out; + + if (m_type == type::normal && !visit(is_node_empty(), node)) + out = visit(render_section(ctx, m_section, m_start_token.delims()), node); + else if (m_type == type::inverted && visit(is_node_empty(), node)) + out = render_context::push(ctx).render(m_section); + + ctx.set_state<outside_section>(); + return out; + } else + m_skipped_openings--; + else if (token.token_type() == token::type::inverted_section_open || + token.token_type() == token::type::section_open) + m_skipped_openings++; + + m_section << token; + return ""; +} diff --git a/lib/mstch/src/state/in_section.hpp b/lib/mstch/src/state/in_section.hpp new file mode 100644 index 0000000..14ca2f7 --- /dev/null +++ b/lib/mstch/src/state/in_section.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <sstream> +#include <vector> + +#include "render_state.hpp" +#include "template_type.hpp" + +namespace mstch { + +class in_section: public render_state { + public: + enum class type { inverted, normal }; + in_section(type type, const token& start_token); + std::string render(render_context& context, const token& token) override; + + private: + const type m_type; + const token& m_start_token; + template_type m_section; + int m_skipped_openings; +}; + +} diff --git a/lib/mstch/src/state/outside_section.cpp b/lib/mstch/src/state/outside_section.cpp new file mode 100644 index 0000000..c9817b1 --- /dev/null +++ b/lib/mstch/src/state/outside_section.cpp @@ -0,0 +1,32 @@ +#include "outside_section.hpp" + +#include "visitor/render_node.hpp" +#include "in_section.hpp" +#include "render_context.hpp" + +using namespace mstch; + +std::string outside_section::render( + render_context& ctx, const token& token) +{ + using flag = render_node::flag; + switch (token.token_type()) { + case token::type::section_open: + ctx.set_state<in_section>(in_section::type::normal, token); + break; + case token::type::inverted_section_open: + ctx.set_state<in_section>(in_section::type::inverted, token); + break; + case token::type::variable: + return visit(render_node(ctx, flag::escape_html), ctx.get_node(token.name())); + case token::type::unescaped_variable: + return visit(render_node(ctx, flag::none), ctx.get_node(token.name())); + case token::type::text: + return token.raw(); + case token::type::partial: + return ctx.render_partial(token.name(), token.partial_prefix()); + default: + break; + } + return ""; +} diff --git a/lib/mstch/src/state/outside_section.hpp b/lib/mstch/src/state/outside_section.hpp new file mode 100644 index 0000000..8617dce --- /dev/null +++ b/lib/mstch/src/state/outside_section.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "render_state.hpp" + +namespace mstch { + +class outside_section: public render_state { + public: + std::string render(render_context& context, const token& token) override; +}; + +} diff --git a/lib/mstch/src/state/render_state.hpp b/lib/mstch/src/state/render_state.hpp new file mode 100644 index 0000000..e44a956 --- /dev/null +++ b/lib/mstch/src/state/render_state.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include <memory> + +#include "token.hpp" + +namespace mstch { + +class render_context; + +class render_state { + public: + virtual ~render_state() {} + virtual std::string render(render_context& context, const token& token) = 0; +}; + +} diff --git a/lib/mstch/src/template_type.cpp b/lib/mstch/src/template_type.cpp new file mode 100644 index 0000000..5b40e40 --- /dev/null +++ b/lib/mstch/src/template_type.cpp @@ -0,0 +1,104 @@ +#include "template_type.hpp" + +using namespace mstch; + +template_type::template_type(const std::string& str, const delim_type& delims): + m_open(delims.first), m_close(delims.second) +{ + tokenize(str); + strip_whitespace(); +} + +template_type::template_type(const std::string& str): + m_open("{{"), m_close("}}") +{ + tokenize(str); + strip_whitespace(); +} + +void template_type::process_text(citer begin, citer end) { + if (begin == end) + return; + auto start = begin; + for (auto it = begin; it != end; ++it) + if (*it == '\n' || it == end - 1) { + m_tokens.push_back({{start, it + 1}}); + start = it + 1; + } +} + +void template_type::tokenize(const std::string& tmp) { + citer beg = tmp.begin(); + auto npos = std::string::npos; + + for (std::size_t cur_pos = 0; cur_pos < tmp.size();) { + auto open_pos = tmp.find(m_open, cur_pos); + auto close_pos = tmp.find( + m_close, open_pos == npos ? open_pos : open_pos + 1); + + if (close_pos != npos && open_pos != npos) { + if (*(beg + open_pos + m_open.size()) == '{' && + *(beg + close_pos + m_close.size()) == '}') + ++close_pos; + + process_text(beg + cur_pos, beg + open_pos); + cur_pos = close_pos + m_close.size(); + m_tokens.push_back({{beg + open_pos, beg + close_pos + m_close.size()}, + m_open.size(), m_close.size()}); + + if (cur_pos == tmp.size()) { + m_tokens.push_back({{""}}); + m_tokens.back().eol(true); + } + + if (*(beg + open_pos + m_open.size()) == '=' && + *(beg + close_pos - 1) == '=') + { + auto tok_beg = beg + open_pos + m_open.size() + 1; + auto tok_end = beg + close_pos - 1; + auto front_skip = first_not_ws(tok_beg, tok_end); + auto back_skip = first_not_ws(reverse(tok_end), reverse(tok_beg)); + m_open = {front_skip, beg + tmp.find(' ', front_skip - beg)}; + m_close = {beg + tmp.rfind(' ', back_skip - beg) + 1, back_skip + 1}; + } + } else { + process_text(beg + cur_pos, tmp.end()); + cur_pos = close_pos; + } + } +} + +void template_type::strip_whitespace() { + auto line_begin = m_tokens.begin(); + bool has_tag = false, non_space = false; + + for (auto it = m_tokens.begin(); it != m_tokens.end(); ++it) { + auto type = (*it).token_type(); + if (type != token::type::text && type != token::type::variable && + type != token::type::unescaped_variable) + has_tag = true; + else if (!(*it).ws_only()) + non_space = true; + + if ((*it).eol()) { + if (has_tag && !non_space) { + store_prefixes(line_begin); + + auto c = line_begin; + for (bool end = false; !end; (*c).ws_only() ? c = m_tokens.erase(c) : ++c) + if ((end = (*c).eol())) + it = c - 1; + } + + non_space = has_tag = false; + line_begin = it + 1; + } + } +} + +void template_type::store_prefixes(std::vector<token>::iterator beg) { + for (auto cur = beg; !(*cur).eol(); ++cur) + if ((*cur).token_type() == token::type::partial && + cur != beg && (*(cur - 1)).ws_only()) + (*cur).partial_prefix((*(cur - 1)).raw()); +} diff --git a/lib/mstch/src/template_type.hpp b/lib/mstch/src/template_type.hpp new file mode 100644 index 0000000..3c5bf91 --- /dev/null +++ b/lib/mstch/src/template_type.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <string> +#include <vector> + +#include "token.hpp" +#include "utils.hpp" + +namespace mstch { + +class template_type { + public: + template_type() = default; + template_type(const std::string& str); + template_type(const std::string& str, const delim_type& delims); + std::vector<token>::const_iterator begin() const { return m_tokens.begin(); } + std::vector<token>::const_iterator end() const { return m_tokens.end(); } + void operator<<(const token& token) { m_tokens.push_back(token); } + + private: + std::vector<token> m_tokens; + std::string m_open; + std::string m_close; + void strip_whitespace(); + void process_text(citer beg, citer end); + void tokenize(const std::string& tmp); + void store_prefixes(std::vector<token>::iterator beg); +}; + +} diff --git a/lib/mstch/src/token.cpp b/lib/mstch/src/token.cpp new file mode 100644 index 0000000..9443a22 --- /dev/null +++ b/lib/mstch/src/token.cpp @@ -0,0 +1,42 @@ +#include "token.hpp" +#include "utils.hpp" + +using namespace mstch; + +token::type token::token_info(char c) { + switch (c) { + case '>': return type::partial; + case '^': return type::inverted_section_open; + case '/': return type::section_close; + case '&': return type::unescaped_variable; + case '#': return type::section_open; + case '!': return type::comment; + default: return type::variable; + } +} + +token::token(const std::string& str, std::size_t left, std::size_t right): + m_raw(str), m_eol(false), m_ws_only(false) +{ + if (left != 0 && right != 0) { + if (str[left] == '=' && str[str.size() - right - 1] == '=') { + m_type = type::delimiter_change; + } else if (str[left] == '{' && str[str.size() - right - 1] == '}') { + m_type = type::unescaped_variable; + m_name = {first_not_ws(str.begin() + left + 1, str.end() - right), + first_not_ws(str.rbegin() + 1 + right, str.rend() - left) + 1}; + } else { + auto c = first_not_ws(str.begin() + left, str.end() - right); + m_type = token_info(*c); + if (m_type != type::variable) + c = first_not_ws(c + 1, str.end() - right); + m_name = {c, first_not_ws(str.rbegin() + right, str.rend() - left) + 1}; + m_delims = {{str.begin(), str.begin() + left}, + {str.end() - right, str.end()}}; + } + } else { + m_type = type::text; + m_eol = (str.size() > 0 && str[str.size() - 1] == '\n'); + m_ws_only = (str.find_first_not_of(" \r\n\t") == std::string::npos); + } +} diff --git a/lib/mstch/src/token.hpp b/lib/mstch/src/token.hpp new file mode 100644 index 0000000..fde8017 --- /dev/null +++ b/lib/mstch/src/token.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <string> + +namespace mstch { + +using delim_type = std::pair<std::string, std::string>; + +class token { + public: + enum class type { + text, variable, section_open, section_close, inverted_section_open, + unescaped_variable, comment, partial, delimiter_change + }; + token(const std::string& str, std::size_t left = 0, std::size_t right = 0); + type token_type() const { return m_type; }; + const std::string& raw() const { return m_raw; }; + const std::string& name() const { return m_name; }; + const std::string& partial_prefix() const { return m_partial_prefix; }; + const delim_type& delims() const { return m_delims; }; + void partial_prefix(const std::string& p_partial_prefix) { + m_partial_prefix = p_partial_prefix; + }; + bool eol() const { return m_eol; } + void eol(bool eol) { m_eol = eol; } + bool ws_only() const { return m_ws_only; } + + private: + type m_type; + std::string m_name; + std::string m_raw; + std::string m_partial_prefix; + delim_type m_delims; + bool m_eol; + bool m_ws_only; + type token_info(char c); +}; + +} diff --git a/lib/mstch/src/utils.cpp b/lib/mstch/src/utils.cpp new file mode 100644 index 0000000..1646170 --- /dev/null +++ b/lib/mstch/src/utils.cpp @@ -0,0 +1,44 @@ +#include "utils.hpp" +#include "mstch/mstch.hpp" + +mstch::citer mstch::first_not_ws(mstch::citer begin, mstch::citer end) { + for (auto it = begin; it != end; ++it) + if (*it != ' ') return it; + return end; +} + +mstch::citer mstch::first_not_ws(mstch::criter begin, mstch::criter end) { + for (auto rit = begin; rit != end; ++rit) + if (*rit != ' ') return --(rit.base()); + return --(end.base()); +} + +mstch::criter mstch::reverse(mstch::citer it) { + return std::reverse_iterator<mstch::citer>(it); +} + +std::string mstch::html_escape(const std::string& str) { + if (mstch::config::escape) + return mstch::config::escape(str); + + std::string out; + citer start = str.begin(); + + auto add_escape = [&out, &start](const std::string& escaped, citer& it) { + out += std::string{start, it} + escaped; + start = it + 1; + }; + + for (auto it = str.begin(); it != str.end(); ++it) + switch (*it) { + case '&': add_escape("&", it); break; + case '\'': add_escape("'", it); break; + case '"': add_escape(""", it); break; + case '<': add_escape("<", it); break; + case '>': add_escape(">", it); break; + case '/': add_escape("/", it); break; + default: break; + } + + return out + std::string{start, str.end()}; +} diff --git a/lib/mstch/src/utils.hpp b/lib/mstch/src/utils.hpp new file mode 100644 index 0000000..9041a3e --- /dev/null +++ b/lib/mstch/src/utils.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <string> +#include <boost/variant/apply_visitor.hpp> + +namespace mstch { + +using citer = std::string::const_iterator; +using criter = std::string::const_reverse_iterator; + +citer first_not_ws(citer begin, citer end); +citer first_not_ws(criter begin, criter end); +std::string html_escape(const std::string& str); +criter reverse(citer it); + +template<class... Args> +auto visit(Args&&... args) -> decltype(boost::apply_visitor( + std::forward<Args>(args)...)) +{ + return boost::apply_visitor(std::forward<Args>(args)...); +} + +} diff --git a/lib/mstch/src/visitor/get_token.hpp b/lib/mstch/src/visitor/get_token.hpp new file mode 100644 index 0000000..d41ab6e --- /dev/null +++ b/lib/mstch/src/visitor/get_token.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include <boost/variant/static_visitor.hpp> + +#include "mstch/mstch.hpp" +#include "has_token.hpp" + +namespace mstch { + +class get_token: public boost::static_visitor<const mstch::node&> { + public: + get_token(const std::string& token, const mstch::node& node): + m_token(token), m_node(node) + { + } + + template<class T> + const mstch::node& operator()(const T&) const { + return m_node; + } + + const mstch::node& operator()(const map& map) const { + return map.at(m_token); + } + + const mstch::node& operator()(const std::shared_ptr<object>& object) const { + return object->at(m_token); + } + + private: + const std::string& m_token; + const mstch::node& m_node; +}; + +} diff --git a/lib/mstch/src/visitor/has_token.hpp b/lib/mstch/src/visitor/has_token.hpp new file mode 100644 index 0000000..5ab30d4 --- /dev/null +++ b/lib/mstch/src/visitor/has_token.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include <boost/variant/static_visitor.hpp> + +#include "mstch/mstch.hpp" + +namespace mstch { + +class has_token: public boost::static_visitor<bool> { + public: + has_token(const std::string& token): m_token(token) { + } + + template<class T> + bool operator()(const T&) const { + return m_token == "."; + } + + bool operator()(const map& map) const { + return map.count(m_token) == 1; + } + + bool operator()(const std::shared_ptr<object>& object) const { + return object->has(m_token); + } + + private: + const std::string& m_token; +}; + +} diff --git a/lib/mstch/src/visitor/is_node_empty.hpp b/lib/mstch/src/visitor/is_node_empty.hpp new file mode 100644 index 0000000..a0ae432 --- /dev/null +++ b/lib/mstch/src/visitor/is_node_empty.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <boost/variant/static_visitor.hpp> + +#include "mstch/mstch.hpp" + +namespace mstch { + +class is_node_empty: public boost::static_visitor<bool> { + public: + template<class T> + bool operator()(const T&) const { + return false; + } + + bool operator()(const std::nullptr_t&) const { + return true; + } + + bool operator()(const int& value) const { + return value == 0; + } + + bool operator()(const double& value) const { + return value == 0; + } + + bool operator()(const bool& value) const { + return !value; + } + + bool operator()(const std::string& value) const { + return value == ""; + } + + bool operator()(const array& array) const { + return array.size() == 0; + } +}; + +} diff --git a/lib/mstch/src/visitor/render_node.hpp b/lib/mstch/src/visitor/render_node.hpp new file mode 100644 index 0000000..633dd4d --- /dev/null +++ b/lib/mstch/src/visitor/render_node.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include <sstream> +#include <boost/variant/static_visitor.hpp> + +#include "render_context.hpp" +#include "mstch/mstch.hpp" +#include "utils.hpp" + +namespace mstch { + +class render_node: public boost::static_visitor<std::string> { + public: + enum class flag { none, escape_html }; + render_node(render_context& ctx, flag p_flag = flag::none): + m_ctx(ctx), m_flag(p_flag) + { + } + + template<class T> + std::string operator()(const T&) const { + return ""; + } + + std::string operator()(const int& value) const { + return std::to_string(value); + } + + std::string operator()(const double& value) const { + std::stringstream ss; + ss << value; + return ss.str(); + } + + std::string operator()(const bool& value) const { + return value ? "true" : "false"; + } + + std::string operator()(const lambda& value) const { + template_type interpreted{value([this](const mstch::node& n) { + return visit(render_node(m_ctx), n); + })}; + auto rendered = render_context::push(m_ctx).render(interpreted); + return (m_flag == flag::escape_html) ? html_escape(rendered) : rendered; + } + + std::string operator()(const std::string& value) const { + return (m_flag == flag::escape_html) ? html_escape(value) : value; + } + + private: + render_context& m_ctx; + flag m_flag; +}; + +} diff --git a/lib/mstch/src/visitor/render_section.hpp b/lib/mstch/src/visitor/render_section.hpp new file mode 100644 index 0000000..f2d5259 --- /dev/null +++ b/lib/mstch/src/visitor/render_section.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include <boost/variant/static_visitor.hpp> + +#include "render_context.hpp" +#include "mstch/mstch.hpp" +#include "utils.hpp" +#include "render_node.hpp" + +namespace mstch { + +class render_section: public boost::static_visitor<std::string> { + public: + enum class flag { none, keep_array }; + render_section( + render_context& ctx, + const template_type& section, + const delim_type& delims, + flag p_flag = flag::none): + m_ctx(ctx), m_section(section), m_delims(delims), m_flag(p_flag) + { + } + + template<class T> + std::string operator()(const T& t) const { + return render_context::push(m_ctx, t).render(m_section); + } + + std::string operator()(const lambda& fun) const { + std::string section_str; + for (auto& token: m_section) + section_str += token.raw(); + template_type interpreted{fun([this](const mstch::node& n) { + return visit(render_node(m_ctx), n); + }, section_str), m_delims}; + return render_context::push(m_ctx).render(interpreted); + } + + std::string operator()(const array& array) const { + std::string out; + if (m_flag == flag::keep_array) + return render_context::push(m_ctx, array).render(m_section); + else + for (auto& item: array) + out += visit(render_section( + m_ctx, m_section, m_delims, flag::keep_array), item); + return out; + } + + private: + render_context& m_ctx; + const template_type& m_section; + const delim_type& m_delims; + flag m_flag; +}; + +} diff --git a/lib/mstch/test/CMakeLists.txt b/lib/mstch/test/CMakeLists.txt new file mode 100644 index 0000000..061bc2e --- /dev/null +++ b/lib/mstch/test/CMakeLists.txt @@ -0,0 +1,59 @@ +find_package(Boost 1.54 REQUIRED) + +include_directories( + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/vendor/Catch/single_include + ${CMAKE_SOURCE_DIR}/vendor/rapidjson/include + ${Boost_INCLUDE_DIR}) + +file(GLOB data_files RELATIVE + "${CMAKE_SOURCE_DIR}/test/data" + "${CMAKE_SOURCE_DIR}/test/data/*.hpp") + +foreach(data_file ${data_files}) + string(REGEX REPLACE "\\.hpp" "" test_name "${data_file}") + list(APPEND tests "${test_name}") +endforeach(data_file) + +file(GLOB string_files RELATIVE + "${CMAKE_SOURCE_DIR}/test/data" + "${CMAKE_SOURCE_DIR}/test/data/*.mustache" + "${CMAKE_SOURCE_DIR}/test/data/*.txt" + "${CMAKE_SOURCE_DIR}/test/data/*.partial") + +foreach(string_file ${string_files}) + list(APPEND genargs "-i${string_file}") +endforeach(string_file) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp + COMMAND headerize --output ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp --namespace mstchtest ${genargs} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/test/data/) +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp PROPERTIES GENERATED TRUE) +add_custom_target(test_data_hpp DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/test_data.hpp) + +file(GLOB specs_files RELATIVE + "${CMAKE_SOURCE_DIR}/vendor/spec/specs" + "${CMAKE_SOURCE_DIR}/vendor/spec/specs/*.json") + +foreach(specs_file ${specs_files}) + list(APPEND specsargs "-i${specs_file}") + string(REGEX REPLACE "\\.json" "" test_name "${specs_file}") + string(REGEX REPLACE "~" "" test_name "${test_name}") + list(APPEND tests "specs_${test_name}") +endforeach(specs_file) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/specs_data.hpp + COMMAND headerize --output ${CMAKE_CURRENT_BINARY_DIR}/specs_data.hpp --namespace mstchtest ${specsargs} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/vendor/spec/specs/) +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/specs_data.hpp PROPERTIES GENERATED TRUE) +add_custom_target(specs_data_hpp DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/specs_data.hpp) + +add_executable(mstch_test test_main.cpp) +target_link_libraries(mstch_test mstch) +add_dependencies(mstch_test test_data_hpp specs_data_hpp) + +foreach(test ${tests}) + add_test(NAME ${test} COMMAND mstch_test ${test}) +endforeach(test) diff --git a/lib/mstch/test/data/ampersand_escape.hpp b/lib/mstch/test/data/ampersand_escape.hpp new file mode 100644 index 0000000..b307d50 --- /dev/null +++ b/lib/mstch/test/data/ampersand_escape.hpp @@ -0,0 +1,3 @@ +const auto ampersand_escape_data = mstch::map{ + {"message", std::string{"Some <code>"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/ampersand_escape.mustache b/lib/mstch/test/data/ampersand_escape.mustache new file mode 100644 index 0000000..6501a48 --- /dev/null +++ b/lib/mstch/test/data/ampersand_escape.mustache @@ -0,0 +1 @@ +{{&message}} diff --git a/lib/mstch/test/data/ampersand_escape.txt b/lib/mstch/test/data/ampersand_escape.txt new file mode 100644 index 0000000..2ed3fd3 --- /dev/null +++ b/lib/mstch/test/data/ampersand_escape.txt @@ -0,0 +1 @@ +Some <code> diff --git a/lib/mstch/test/data/apostrophe.hpp b/lib/mstch/test/data/apostrophe.hpp new file mode 100644 index 0000000..94738f4 --- /dev/null +++ b/lib/mstch/test/data/apostrophe.hpp @@ -0,0 +1,4 @@ +const auto apostrophe_data = mstch::map{ + {"apos", std::string{"'"}}, + {"control", std::string{"X"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/apostrophe.mustache b/lib/mstch/test/data/apostrophe.mustache new file mode 100644 index 0000000..e8687aa --- /dev/null +++ b/lib/mstch/test/data/apostrophe.mustache @@ -0,0 +1 @@ +{{apos}}{{control}} diff --git a/lib/mstch/test/data/apostrophe.txt b/lib/mstch/test/data/apostrophe.txt new file mode 100644 index 0000000..4427c30 --- /dev/null +++ b/lib/mstch/test/data/apostrophe.txt @@ -0,0 +1 @@ +'X diff --git a/lib/mstch/test/data/array_of_strings.hpp b/lib/mstch/test/data/array_of_strings.hpp new file mode 100644 index 0000000..5511c5d --- /dev/null +++ b/lib/mstch/test/data/array_of_strings.hpp @@ -0,0 +1,3 @@ +const auto array_of_strings_data = mstch::map{ + {"array_of_strings", mstch::array{std::string{"hello"}, std::string{"world"}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/array_of_strings.mustache b/lib/mstch/test/data/array_of_strings.mustache new file mode 100644 index 0000000..4d65738 --- /dev/null +++ b/lib/mstch/test/data/array_of_strings.mustache @@ -0,0 +1 @@ +{{#array_of_strings}}{{.}} {{/array_of_strings}} diff --git a/lib/mstch/test/data/array_of_strings.txt b/lib/mstch/test/data/array_of_strings.txt new file mode 100644 index 0000000..4a1f475 --- /dev/null +++ b/lib/mstch/test/data/array_of_strings.txt @@ -0,0 +1 @@ +hello world diff --git a/lib/mstch/test/data/backslashes.hpp b/lib/mstch/test/data/backslashes.hpp new file mode 100644 index 0000000..7969339 --- /dev/null +++ b/lib/mstch/test/data/backslashes.hpp @@ -0,0 +1,3 @@ +const auto backslashes_data = mstch::map{ + {"value", std::string{"\\abc"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/backslashes.mustache b/lib/mstch/test/data/backslashes.mustache new file mode 100644 index 0000000..fe7745b --- /dev/null +++ b/lib/mstch/test/data/backslashes.mustache @@ -0,0 +1,7 @@ +* {{value}} +* {{{value}}} +* {{&value}} +<script> +foo = { bar: 'abc\"xyz\"' }; +foo = { bar: 'x\'y' }; +</script> diff --git a/lib/mstch/test/data/backslashes.txt b/lib/mstch/test/data/backslashes.txt new file mode 100644 index 0000000..038dd37 --- /dev/null +++ b/lib/mstch/test/data/backslashes.txt @@ -0,0 +1,7 @@ +* \abc +* \abc +* \abc +<script> +foo = { bar: 'abc\"xyz\"' }; +foo = { bar: 'x\'y' }; +</script> diff --git a/lib/mstch/test/data/bug_11_eating_whitespace.hpp b/lib/mstch/test/data/bug_11_eating_whitespace.hpp new file mode 100644 index 0000000..57ce819 --- /dev/null +++ b/lib/mstch/test/data/bug_11_eating_whitespace.hpp @@ -0,0 +1,3 @@ +const auto bug_11_eating_whitespace_data = mstch::map{ + {"tag", std::string{"yo"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/bug_11_eating_whitespace.mustache b/lib/mstch/test/data/bug_11_eating_whitespace.mustache new file mode 100644 index 0000000..8d5cd92 --- /dev/null +++ b/lib/mstch/test/data/bug_11_eating_whitespace.mustache @@ -0,0 +1 @@ +{{tag}} foo diff --git a/lib/mstch/test/data/bug_11_eating_whitespace.txt b/lib/mstch/test/data/bug_11_eating_whitespace.txt new file mode 100644 index 0000000..f5bbc85 --- /dev/null +++ b/lib/mstch/test/data/bug_11_eating_whitespace.txt @@ -0,0 +1 @@ +yo foo diff --git a/lib/mstch/test/data/bug_length_property.hpp b/lib/mstch/test/data/bug_length_property.hpp new file mode 100644 index 0000000..533b596 --- /dev/null +++ b/lib/mstch/test/data/bug_length_property.hpp @@ -0,0 +1,3 @@ +const auto bug_length_property_data = mstch::map{ + {"length", std::string{"hello"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/bug_length_property.mustache b/lib/mstch/test/data/bug_length_property.mustache new file mode 100644 index 0000000..b000887 --- /dev/null +++ b/lib/mstch/test/data/bug_length_property.mustache @@ -0,0 +1 @@ +{{#length}}The length variable is: {{length}}{{/length}} diff --git a/lib/mstch/test/data/bug_length_property.txt b/lib/mstch/test/data/bug_length_property.txt new file mode 100644 index 0000000..f5355d3 --- /dev/null +++ b/lib/mstch/test/data/bug_length_property.txt @@ -0,0 +1 @@ +The length variable is: hello diff --git a/lib/mstch/test/data/changing_delimiters.hpp b/lib/mstch/test/data/changing_delimiters.hpp new file mode 100644 index 0000000..1322543 --- /dev/null +++ b/lib/mstch/test/data/changing_delimiters.hpp @@ -0,0 +1,4 @@ +const mstch::node changing_delimiters_data = mstch::map{ + {"foo", std::string{"foooooooooooooo"}}, + {"bar", std::string{"<b>bar!</b>"}} +}; diff --git a/lib/mstch/test/data/changing_delimiters.mustache b/lib/mstch/test/data/changing_delimiters.mustache new file mode 100644 index 0000000..0cd044c --- /dev/null +++ b/lib/mstch/test/data/changing_delimiters.mustache @@ -0,0 +1 @@ +{{=<% %>=}}<% foo %> {{foo}} <%{bar}%> {{{bar}}} diff --git a/lib/mstch/test/data/changing_delimiters.txt b/lib/mstch/test/data/changing_delimiters.txt new file mode 100644 index 0000000..1b1510d --- /dev/null +++ b/lib/mstch/test/data/changing_delimiters.txt @@ -0,0 +1 @@ +foooooooooooooo {{foo}} <b>bar!</b> {{{bar}}} diff --git a/lib/mstch/test/data/comments.hpp b/lib/mstch/test/data/comments.hpp new file mode 100644 index 0000000..96baccb --- /dev/null +++ b/lib/mstch/test/data/comments.hpp @@ -0,0 +1,3 @@ +const mstch::node comments_data = mstch::map{ + {"title", mstch::lambda{[]()->mstch::node{return std::string{"A Comedy of Errors"};}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/comments.mustache b/lib/mstch/test/data/comments.mustache new file mode 100644 index 0000000..5036801 --- /dev/null +++ b/lib/mstch/test/data/comments.mustache @@ -0,0 +1 @@ +<h1>{{title}}{{! just something interesting... or not... }}</h1> diff --git a/lib/mstch/test/data/comments.txt b/lib/mstch/test/data/comments.txt new file mode 100644 index 0000000..0133517 --- /dev/null +++ b/lib/mstch/test/data/comments.txt @@ -0,0 +1 @@ +<h1>A Comedy of Errors</h1> diff --git a/lib/mstch/test/data/complex.hpp b/lib/mstch/test/data/complex.hpp new file mode 100644 index 0000000..a438098 --- /dev/null +++ b/lib/mstch/test/data/complex.hpp @@ -0,0 +1,69 @@ +class complex_item: public mstch::object { +private: + std::string m_name; + bool m_current; + std::string m_url; +public: + complex_item(const std::string& name, bool current, const std::string& url): + m_name(name), m_current(current), m_url(url) + { + register_methods(this, std::map<std::string,mstch::node(complex_item::*)()>{ + {"name", &complex_item::name}, {"current", &complex_item::current}, + {"url", &complex_item::url}, {"link", &complex_item::link} + }); + } + + mstch::node current() { + return m_current; + } + + mstch::node url() { + return m_url; + } + + mstch::node name() { + return m_name; + } + + mstch::node link() { + return !m_current; + } +}; + +class complex: public mstch::object { +private: + std::string m_header; + mstch::array m_item; +public: + complex(): + m_header("Colors"), + m_item(mstch::array{ + std::make_shared<complex_item>("red", true, "#Red"), + std::make_shared<complex_item>("green", false, "#Green"), + std::make_shared<complex_item>("blue", false, "#Blue") + }) + { + register_methods(this, std::map<std::string,mstch::node(complex::*)()>{ + {"header", &complex::header}, {"item", &complex::item}, + {"list", &complex::list}, {"empty", &complex::empty} + }); + } + + mstch::node header() { + return m_header; + } + + mstch::node item() { + return m_item; + } + + mstch::node list() { + return m_item.size() != 0; + } + + mstch::node empty() { + return m_item.size() == 0; + } +}; + +const auto complex_data = std::make_shared<complex>();
\ No newline at end of file diff --git a/lib/mstch/test/data/complex.mustache b/lib/mstch/test/data/complex.mustache new file mode 100644 index 0000000..869a4f0 --- /dev/null +++ b/lib/mstch/test/data/complex.mustache @@ -0,0 +1,16 @@ +<h1>{{header}}</h1> +{{#list}} + <ul> + {{#item}} + {{#current}} + <li><strong>{{name}}</strong></li> + {{/current}} + {{#link}} + <li><a href="{{url}}">{{name}}</a></li> + {{/link}} + {{/item}} + </ul> +{{/list}} +{{#empty}} + <p>The list is empty.</p> +{{/empty}} diff --git a/lib/mstch/test/data/complex.txt b/lib/mstch/test/data/complex.txt new file mode 100644 index 0000000..596d3f6 --- /dev/null +++ b/lib/mstch/test/data/complex.txt @@ -0,0 +1,6 @@ +<h1>Colors</h1> + <ul> + <li><strong>red</strong></li> + <li><a href="#Green">green</a></li> + <li><a href="#Blue">blue</a></li> + </ul> diff --git a/lib/mstch/test/data/context_lookup.hpp b/lib/mstch/test/data/context_lookup.hpp new file mode 100644 index 0000000..c799080 --- /dev/null +++ b/lib/mstch/test/data/context_lookup.hpp @@ -0,0 +1,8 @@ +const auto context_lookup_data = mstch::map{ + {"outer", mstch::map{ + {"id", 1}, + {"second", mstch::map{ + {"nothing", 2} + }} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/context_lookup.mustache b/lib/mstch/test/data/context_lookup.mustache new file mode 100644 index 0000000..3c7b767 --- /dev/null +++ b/lib/mstch/test/data/context_lookup.mustache @@ -0,0 +1 @@ +{{#outer}}{{#second}}{{id}}{{/second}}{{/outer}} diff --git a/lib/mstch/test/data/context_lookup.txt b/lib/mstch/test/data/context_lookup.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/lib/mstch/test/data/context_lookup.txt @@ -0,0 +1 @@ +1 diff --git a/lib/mstch/test/data/delimiters.hpp b/lib/mstch/test/data/delimiters.hpp new file mode 100644 index 0000000..0e560ab --- /dev/null +++ b/lib/mstch/test/data/delimiters.hpp @@ -0,0 +1,6 @@ +const mstch::node delimiters_data = mstch::map{ + {"first", std::string{"It worked the first time."}}, + {"second", std::string{"And it worked the second time."}}, + {"third", std::string{"Then, surprisingly, it worked the third time."}}, + {"fourth", std::string{"Fourth time also fine!."}} +}; diff --git a/lib/mstch/test/data/delimiters.mustache b/lib/mstch/test/data/delimiters.mustache new file mode 100644 index 0000000..7fac846 --- /dev/null +++ b/lib/mstch/test/data/delimiters.mustache @@ -0,0 +1,7 @@ +{{=<% %>=}}* +<% first %> +* <% second %> +<%=| |=%> +* | third | +|={{ }}=| +* {{ fourth }} diff --git a/lib/mstch/test/data/delimiters.txt b/lib/mstch/test/data/delimiters.txt new file mode 100644 index 0000000..698a6bb --- /dev/null +++ b/lib/mstch/test/data/delimiters.txt @@ -0,0 +1,5 @@ +* +It worked the first time. +* And it worked the second time. +* Then, surprisingly, it worked the third time. +* Fourth time also fine!. diff --git a/lib/mstch/test/data/disappearing_whitespace.hpp b/lib/mstch/test/data/disappearing_whitespace.hpp new file mode 100644 index 0000000..5322fe4 --- /dev/null +++ b/lib/mstch/test/data/disappearing_whitespace.hpp @@ -0,0 +1,4 @@ +const auto disappearing_whitespace_data = mstch::map{ + {"bedrooms", true}, + {"total", 1} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/disappearing_whitespace.mustache b/lib/mstch/test/data/disappearing_whitespace.mustache new file mode 100644 index 0000000..16c16e0 --- /dev/null +++ b/lib/mstch/test/data/disappearing_whitespace.mustache @@ -0,0 +1 @@ +{{#bedrooms}}{{total}}{{/bedrooms}} BED diff --git a/lib/mstch/test/data/disappearing_whitespace.txt b/lib/mstch/test/data/disappearing_whitespace.txt new file mode 100644 index 0000000..66e98ef --- /dev/null +++ b/lib/mstch/test/data/disappearing_whitespace.txt @@ -0,0 +1 @@ +1 BED diff --git a/lib/mstch/test/data/dot_notation.hpp b/lib/mstch/test/data/dot_notation.hpp new file mode 100644 index 0000000..c287107 --- /dev/null +++ b/lib/mstch/test/data/dot_notation.hpp @@ -0,0 +1,34 @@ +class dot_notation_price: public mstch::object { +private: + int m_value; + mstch::map m_currency; +public: + dot_notation_price(): + m_value(200), m_currency(mstch::map{{"symbol", std::string{"$"}}, {"name", std::string{"USD"}}}) + { + register_methods(this, std::map<std::string,mstch::node(dot_notation_price::*)()>{ + {"value", &dot_notation_price::value}, + {"vat", &dot_notation_price::vat}, + {"currency", &dot_notation_price::currency}}); + } + + mstch::node value() { + return m_value; + } + + mstch::node vat() { + return m_value * 0.2; + } + + mstch::node currency() { + return m_currency; + } +}; + +const auto dot_notation_data = mstch::map{ + {"name", std::string{"A Book"}}, + {"authors", mstch::array{std::string{"John Power"}, std::string{"Jamie Walsh"}}}, + {"price", std::make_shared<dot_notation_price>()}, + {"availability", mstch::map{{"status", true}, {"text", std::string{"In Stock"}}}}, + {"truthy", mstch::map{{"zero", 0}, {"notTrue", false}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/dot_notation.mustache b/lib/mstch/test/data/dot_notation.mustache new file mode 100644 index 0000000..f89d70b --- /dev/null +++ b/lib/mstch/test/data/dot_notation.mustache @@ -0,0 +1,9 @@ +<!-- exciting part --> +<h1>{{name}}</h1> +<p>Authors: <ul>{{#authors}}<li>{{.}}</li>{{/authors}}</ul></p> +<p>Price: {{{price.currency.symbol}}}{{price.value}} {{#price.currency}}{{name}} <b>{{availability.text}}</b>{{/price.currency}}</p> +<p>VAT: {{{price.currency.symbol}}}{{#price}}{{vat}}{{/price}}</p> +<!-- boring part --> +<h2>Test truthy false values:</h2> +<p>Zero: {{truthy.zero}}</p> +<p>False: {{truthy.notTrue}}</p> diff --git a/lib/mstch/test/data/dot_notation.txt b/lib/mstch/test/data/dot_notation.txt new file mode 100644 index 0000000..08afa05 --- /dev/null +++ b/lib/mstch/test/data/dot_notation.txt @@ -0,0 +1,9 @@ +<!-- exciting part --> +<h1>A Book</h1> +<p>Authors: <ul><li>John Power</li><li>Jamie Walsh</li></ul></p> +<p>Price: $200 USD <b>In Stock</b></p> +<p>VAT: $40</p> +<!-- boring part --> +<h2>Test truthy false values:</h2> +<p>Zero: 0</p> +<p>False: false</p> diff --git a/lib/mstch/test/data/double_render.hpp b/lib/mstch/test/data/double_render.hpp new file mode 100644 index 0000000..eb24dc1 --- /dev/null +++ b/lib/mstch/test/data/double_render.hpp @@ -0,0 +1,5 @@ +const auto double_render_data = mstch::map{ + {"foo", true}, + {"bar", std::string{"{{win}}"}}, + {"win", std::string{"FAIL"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/double_render.mustache b/lib/mstch/test/data/double_render.mustache new file mode 100644 index 0000000..4500fd7 --- /dev/null +++ b/lib/mstch/test/data/double_render.mustache @@ -0,0 +1 @@ +{{#foo}}{{bar}}{{/foo}} diff --git a/lib/mstch/test/data/double_render.txt b/lib/mstch/test/data/double_render.txt new file mode 100644 index 0000000..b6e652d --- /dev/null +++ b/lib/mstch/test/data/double_render.txt @@ -0,0 +1 @@ +{{win}} diff --git a/lib/mstch/test/data/empty_list.hpp b/lib/mstch/test/data/empty_list.hpp new file mode 100644 index 0000000..9a4d893 --- /dev/null +++ b/lib/mstch/test/data/empty_list.hpp @@ -0,0 +1,3 @@ +const auto empty_list_data = mstch::map{ + {"jobs", mstch::array{}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/empty_list.mustache b/lib/mstch/test/data/empty_list.mustache new file mode 100644 index 0000000..4fdf13d --- /dev/null +++ b/lib/mstch/test/data/empty_list.mustache @@ -0,0 +1,4 @@ +These are the jobs: +{{#jobs}} +{{.}} +{{/jobs}} diff --git a/lib/mstch/test/data/empty_list.txt b/lib/mstch/test/data/empty_list.txt new file mode 100644 index 0000000..d9b4a67 --- /dev/null +++ b/lib/mstch/test/data/empty_list.txt @@ -0,0 +1 @@ +These are the jobs: diff --git a/lib/mstch/test/data/empty_sections.hpp b/lib/mstch/test/data/empty_sections.hpp new file mode 100644 index 0000000..c6caee0 --- /dev/null +++ b/lib/mstch/test/data/empty_sections.hpp @@ -0,0 +1 @@ +const auto empty_sections_data = mstch::map{};
\ No newline at end of file diff --git a/lib/mstch/test/data/empty_sections.mustache b/lib/mstch/test/data/empty_sections.mustache new file mode 100644 index 0000000..b6065db --- /dev/null +++ b/lib/mstch/test/data/empty_sections.mustache @@ -0,0 +1 @@ +{{#foo}}{{/foo}}foo{{#bar}}{{/bar}} diff --git a/lib/mstch/test/data/empty_sections.txt b/lib/mstch/test/data/empty_sections.txt new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/lib/mstch/test/data/empty_sections.txt @@ -0,0 +1 @@ +foo diff --git a/lib/mstch/test/data/empty_string.hpp b/lib/mstch/test/data/empty_string.hpp new file mode 100644 index 0000000..32e70bf --- /dev/null +++ b/lib/mstch/test/data/empty_string.hpp @@ -0,0 +1,6 @@ +const auto empty_string_data = mstch::map{ + {"description", std::string{"That is all!"}}, + {"child", mstch::map{ + {"description", std::string{""}} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/empty_string.mustache b/lib/mstch/test/data/empty_string.mustache new file mode 100644 index 0000000..f568441 --- /dev/null +++ b/lib/mstch/test/data/empty_string.mustache @@ -0,0 +1 @@ +{{description}}{{#child}}{{description}}{{/child}} diff --git a/lib/mstch/test/data/empty_string.txt b/lib/mstch/test/data/empty_string.txt new file mode 100644 index 0000000..22e2a6e --- /dev/null +++ b/lib/mstch/test/data/empty_string.txt @@ -0,0 +1 @@ +That is all! diff --git a/lib/mstch/test/data/empty_template.hpp b/lib/mstch/test/data/empty_template.hpp new file mode 100644 index 0000000..f0208af --- /dev/null +++ b/lib/mstch/test/data/empty_template.hpp @@ -0,0 +1 @@ +const auto empty_template_data = mstch::map{};
\ No newline at end of file diff --git a/lib/mstch/test/data/empty_template.mustache b/lib/mstch/test/data/empty_template.mustache new file mode 100644 index 0000000..bb2367a --- /dev/null +++ b/lib/mstch/test/data/empty_template.mustache @@ -0,0 +1 @@ +<html><head></head><body><h1>Test</h1></body></html>
\ No newline at end of file diff --git a/lib/mstch/test/data/empty_template.txt b/lib/mstch/test/data/empty_template.txt new file mode 100644 index 0000000..bb2367a --- /dev/null +++ b/lib/mstch/test/data/empty_template.txt @@ -0,0 +1 @@ +<html><head></head><body><h1>Test</h1></body></html>
\ No newline at end of file diff --git a/lib/mstch/test/data/error_eof_in_section.hpp b/lib/mstch/test/data/error_eof_in_section.hpp new file mode 100644 index 0000000..2e54aab --- /dev/null +++ b/lib/mstch/test/data/error_eof_in_section.hpp @@ -0,0 +1,3 @@ +const auto error_eof_in_section_data = mstch::map{ + {"hello", mstch::array{std::string{"a"}, std::string{"b"}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/error_eof_in_section.mustache b/lib/mstch/test/data/error_eof_in_section.mustache new file mode 100644 index 0000000..1035f91 --- /dev/null +++ b/lib/mstch/test/data/error_eof_in_section.mustache @@ -0,0 +1 @@ +yay{{#hello}}{{.}}
\ No newline at end of file diff --git a/lib/mstch/test/data/error_eof_in_section.txt b/lib/mstch/test/data/error_eof_in_section.txt new file mode 100644 index 0000000..fc0e7cb --- /dev/null +++ b/lib/mstch/test/data/error_eof_in_section.txt @@ -0,0 +1 @@ +yay
\ No newline at end of file diff --git a/lib/mstch/test/data/error_eof_in_tag.hpp b/lib/mstch/test/data/error_eof_in_tag.hpp new file mode 100644 index 0000000..6f3fb1d --- /dev/null +++ b/lib/mstch/test/data/error_eof_in_tag.hpp @@ -0,0 +1 @@ +const auto error_eof_in_tag_data = mstch::map{{"hello", std::string{"world"}}};
\ No newline at end of file diff --git a/lib/mstch/test/data/error_eof_in_tag.mustache b/lib/mstch/test/data/error_eof_in_tag.mustache new file mode 100644 index 0000000..ba4f670 --- /dev/null +++ b/lib/mstch/test/data/error_eof_in_tag.mustache @@ -0,0 +1 @@ +{{hello{{hello}}{{hello
\ No newline at end of file diff --git a/lib/mstch/test/data/error_eof_in_tag.txt b/lib/mstch/test/data/error_eof_in_tag.txt new file mode 100644 index 0000000..25e277c --- /dev/null +++ b/lib/mstch/test/data/error_eof_in_tag.txt @@ -0,0 +1 @@ +{{hello
\ No newline at end of file diff --git a/lib/mstch/test/data/error_not_found.hpp b/lib/mstch/test/data/error_not_found.hpp new file mode 100644 index 0000000..c8a5420 --- /dev/null +++ b/lib/mstch/test/data/error_not_found.hpp @@ -0,0 +1,3 @@ +const auto error_not_found_data = mstch::map{ + {"bar", 2} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/error_not_found.mustache b/lib/mstch/test/data/error_not_found.mustache new file mode 100644 index 0000000..24369f7 --- /dev/null +++ b/lib/mstch/test/data/error_not_found.mustache @@ -0,0 +1 @@ +{{foo}}
\ No newline at end of file diff --git a/lib/mstch/test/data/error_not_found.txt b/lib/mstch/test/data/error_not_found.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/mstch/test/data/error_not_found.txt diff --git a/lib/mstch/test/data/escaped.hpp b/lib/mstch/test/data/escaped.hpp new file mode 100644 index 0000000..bcb21bb --- /dev/null +++ b/lib/mstch/test/data/escaped.hpp @@ -0,0 +1,4 @@ +const mstch::node escaped_data = mstch::map{ + {"title", mstch::lambda{[]()->mstch::node{ return std::string{"Bear > Shark"}; }}}, + {"entities", mstch::lambda{[]()->mstch::node{ return std::string{"" \"'<>/"}; }}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/escaped.mustache b/lib/mstch/test/data/escaped.mustache new file mode 100644 index 0000000..93e800b --- /dev/null +++ b/lib/mstch/test/data/escaped.mustache @@ -0,0 +1,2 @@ +<h1>{{title}}</h1> +And even {{entities}}, but not {{{entities}}}. diff --git a/lib/mstch/test/data/escaped.txt b/lib/mstch/test/data/escaped.txt new file mode 100644 index 0000000..c1527d5 --- /dev/null +++ b/lib/mstch/test/data/escaped.txt @@ -0,0 +1,2 @@ +<h1>Bear > Shark</h1> +And even &quot; "'<>/, but not " "'<>/. diff --git a/lib/mstch/test/data/falsy.hpp b/lib/mstch/test/data/falsy.hpp new file mode 100644 index 0000000..736de44 --- /dev/null +++ b/lib/mstch/test/data/falsy.hpp @@ -0,0 +1,6 @@ +const auto falsy_data = mstch::map{ + {"emptyString", std::string{""}}, + {"emptyArray", mstch::array{}}, + {"zero", 0}, + {"null", mstch::node{}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/falsy.mustache b/lib/mstch/test/data/falsy.mustache new file mode 100644 index 0000000..4d992fd --- /dev/null +++ b/lib/mstch/test/data/falsy.mustache @@ -0,0 +1,8 @@ +{{#emptyString}}empty string{{/emptyString}} +{{^emptyString}}inverted empty string{{/emptyString}} +{{#emptyArray}}empty array{{/emptyArray}} +{{^emptyArray}}inverted empty array{{/emptyArray}} +{{#zero}}zero{{/zero}} +{{^zero}}inverted zero{{/zero}} +{{#null}}null{{/null}} +{{^null}}inverted null{{/null}} diff --git a/lib/mstch/test/data/falsy.txt b/lib/mstch/test/data/falsy.txt new file mode 100644 index 0000000..fde133c --- /dev/null +++ b/lib/mstch/test/data/falsy.txt @@ -0,0 +1,8 @@ + +inverted empty string + +inverted empty array + +inverted zero + +inverted null diff --git a/lib/mstch/test/data/falsy_array.hpp b/lib/mstch/test/data/falsy_array.hpp new file mode 100644 index 0000000..20a6394 --- /dev/null +++ b/lib/mstch/test/data/falsy_array.hpp @@ -0,0 +1,8 @@ +const auto falsy_array_data = mstch::map{ + {"list", mstch::array{ + mstch::array{std::string{""}, std::string{"emptyString"}}, + mstch::array{mstch::array{}, std::string{"emptyArray"}}, + mstch::array{0, std::string{"zero"}}, + mstch::array{mstch::node{}, std::string{"null"}}} + } +};
\ No newline at end of file diff --git a/lib/mstch/test/data/falsy_array.mustache b/lib/mstch/test/data/falsy_array.mustache new file mode 100644 index 0000000..2be7b37 --- /dev/null +++ b/lib/mstch/test/data/falsy_array.mustache @@ -0,0 +1,3 @@ +{{#list}} +{{#.}}{{#.}}{{.}}{{/.}}{{^.}}inverted {{/.}}{{/.}} +{{/list}}
\ No newline at end of file diff --git a/lib/mstch/test/data/falsy_array.txt b/lib/mstch/test/data/falsy_array.txt new file mode 100644 index 0000000..a001172 --- /dev/null +++ b/lib/mstch/test/data/falsy_array.txt @@ -0,0 +1,4 @@ +inverted emptyString +inverted emptyArray +inverted zero +inverted null diff --git a/lib/mstch/test/data/grandparent_context.hpp b/lib/mstch/test/data/grandparent_context.hpp new file mode 100644 index 0000000..a291143 --- /dev/null +++ b/lib/mstch/test/data/grandparent_context.hpp @@ -0,0 +1,19 @@ +const auto grandparent_context_data = mstch::map{ + {"grand_parent_id", std::string{"grand_parent1"}}, + {"parent_contexts", mstch::array{ + mstch::map{ + {"parent_id", std::string{"parent1"}}, + {"child_contexts", mstch::array{ + mstch::map{{"child_id", std::string{"parent1-child1"}}}, + mstch::map{{"child_id", std::string{"parent1-child2"}}} + }} + }, + mstch::map{ + {"parent_id", std::string{"parent2"}}, + {"child_contexts", mstch::array{ + mstch::map{{"child_id", std::string{"parent2-child1"}}}, + mstch::map{{"child_id", std::string{"parent2-child2"}}} + }} + } + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/grandparent_context.mustache b/lib/mstch/test/data/grandparent_context.mustache new file mode 100644 index 0000000..e6c07a2 --- /dev/null +++ b/lib/mstch/test/data/grandparent_context.mustache @@ -0,0 +1,10 @@ +{{grand_parent_id}} +{{#parent_contexts}} +{{grand_parent_id}} +{{parent_id}} +{{#child_contexts}} +{{grand_parent_id}} +{{parent_id}} +{{child_id}} +{{/child_contexts}} +{{/parent_contexts}} diff --git a/lib/mstch/test/data/grandparent_context.txt b/lib/mstch/test/data/grandparent_context.txt new file mode 100644 index 0000000..64996ad --- /dev/null +++ b/lib/mstch/test/data/grandparent_context.txt @@ -0,0 +1,17 @@ +grand_parent1 +grand_parent1 +parent1 +grand_parent1 +parent1 +parent1-child1 +grand_parent1 +parent1 +parent1-child2 +grand_parent1 +parent2 +grand_parent1 +parent2 +parent2-child1 +grand_parent1 +parent2 +parent2-child2 diff --git a/lib/mstch/test/data/higher_order_sections.hpp b/lib/mstch/test/data/higher_order_sections.hpp new file mode 100644 index 0000000..ef2582c --- /dev/null +++ b/lib/mstch/test/data/higher_order_sections.hpp @@ -0,0 +1,28 @@ +class higher_order_sections: public mstch::object { + private: + std::string m_helper; + public: + higher_order_sections(): m_helper("To tinker?") { + register_methods(this, std::map<std::string,mstch::node(higher_order_sections::*)()>{ + {"name", &higher_order_sections::name}, + {"helper", &higher_order_sections::helper}, + {"bolder", &higher_order_sections::bolder} + }); + } + + mstch::node name() { + return std::string{"Tater"}; + } + + mstch::node helper() { + return m_helper; + } + + mstch::node bolder() { + return mstch::lambda{[this](const std::string& text) -> mstch::node { + return "<b>" + text + "</b> " + m_helper; + }}; + } +}; + +const mstch::node higher_order_sections_data = std::make_shared<higher_order_sections>();
\ No newline at end of file diff --git a/lib/mstch/test/data/higher_order_sections.mustache b/lib/mstch/test/data/higher_order_sections.mustache new file mode 100644 index 0000000..04f5318 --- /dev/null +++ b/lib/mstch/test/data/higher_order_sections.mustache @@ -0,0 +1 @@ +{{#bolder}}Hi {{name}}.{{/bolder}} diff --git a/lib/mstch/test/data/higher_order_sections.txt b/lib/mstch/test/data/higher_order_sections.txt new file mode 100644 index 0000000..9db786a --- /dev/null +++ b/lib/mstch/test/data/higher_order_sections.txt @@ -0,0 +1 @@ +<b>Hi Tater.</b> To tinker? diff --git a/lib/mstch/test/data/implicit_iterator.hpp b/lib/mstch/test/data/implicit_iterator.hpp new file mode 100644 index 0000000..a610a95 --- /dev/null +++ b/lib/mstch/test/data/implicit_iterator.hpp @@ -0,0 +1,8 @@ +const auto implicit_iterator_data = mstch::map{ + {"data", mstch::map{ + {"author", mstch::map{ + {"twitter_id", 819606}, + {"name", std::string{"janl"}} + }} + }} +}; diff --git a/lib/mstch/test/data/implicit_iterator.mustache b/lib/mstch/test/data/implicit_iterator.mustache new file mode 100644 index 0000000..ae31f34 --- /dev/null +++ b/lib/mstch/test/data/implicit_iterator.mustache @@ -0,0 +1,7 @@ +{{# data.author.twitter_id }} +<meta name="twitter:site:id" content="{{.}}"> +{{/ data.author.twitter_id }} + +{{# data.author.name }} +<meta name="twitter:site" content="{{.}}"> +{{/ data.author.name }} diff --git a/lib/mstch/test/data/implicit_iterator.txt b/lib/mstch/test/data/implicit_iterator.txt new file mode 100644 index 0000000..0fccefd --- /dev/null +++ b/lib/mstch/test/data/implicit_iterator.txt @@ -0,0 +1,3 @@ +<meta name="twitter:site:id" content="819606"> + +<meta name="twitter:site" content="janl"> diff --git a/lib/mstch/test/data/included_tag.hpp b/lib/mstch/test/data/included_tag.hpp new file mode 100644 index 0000000..d094c3b --- /dev/null +++ b/lib/mstch/test/data/included_tag.hpp @@ -0,0 +1,3 @@ +const auto included_tag_data = mstch::map{ + {"html", std::string{"I like {{mustache}}"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/included_tag.mustache b/lib/mstch/test/data/included_tag.mustache new file mode 100644 index 0000000..70631c2 --- /dev/null +++ b/lib/mstch/test/data/included_tag.mustache @@ -0,0 +1 @@ +You said "{{{html}}}" today diff --git a/lib/mstch/test/data/included_tag.txt b/lib/mstch/test/data/included_tag.txt new file mode 100644 index 0000000..1af4556 --- /dev/null +++ b/lib/mstch/test/data/included_tag.txt @@ -0,0 +1 @@ +You said "I like {{mustache}}" today diff --git a/lib/mstch/test/data/inverted_section.hpp b/lib/mstch/test/data/inverted_section.hpp new file mode 100644 index 0000000..38c54c5 --- /dev/null +++ b/lib/mstch/test/data/inverted_section.hpp @@ -0,0 +1,3 @@ +const auto inverted_section_data = mstch::map{ + {"repos", mstch::array{}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/inverted_section.mustache b/lib/mstch/test/data/inverted_section.mustache new file mode 100644 index 0000000..b0a183b --- /dev/null +++ b/lib/mstch/test/data/inverted_section.mustache @@ -0,0 +1,3 @@ +{{#repos}}<b>{{name}}</b>{{/repos}} +{{^repos}}No repos :({{/repos}} +{{^nothin}}Hello!{{/nothin}} diff --git a/lib/mstch/test/data/inverted_section.txt b/lib/mstch/test/data/inverted_section.txt new file mode 100644 index 0000000..b421582 --- /dev/null +++ b/lib/mstch/test/data/inverted_section.txt @@ -0,0 +1,3 @@ + +No repos :( +Hello! diff --git a/lib/mstch/test/data/keys_with_questionmarks.hpp b/lib/mstch/test/data/keys_with_questionmarks.hpp new file mode 100644 index 0000000..49661ca --- /dev/null +++ b/lib/mstch/test/data/keys_with_questionmarks.hpp @@ -0,0 +1,5 @@ +const auto keys_with_questionmarks_data = mstch::map{ + {"person?", mstch::map{ + {"name", std::string{"Jon"}} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/keys_with_questionmarks.mustache b/lib/mstch/test/data/keys_with_questionmarks.mustache new file mode 100644 index 0000000..417f17f --- /dev/null +++ b/lib/mstch/test/data/keys_with_questionmarks.mustache @@ -0,0 +1,3 @@ +{{#person?}} + Hi {{name}}! +{{/person?}} diff --git a/lib/mstch/test/data/keys_with_questionmarks.txt b/lib/mstch/test/data/keys_with_questionmarks.txt new file mode 100644 index 0000000..0f69b94 --- /dev/null +++ b/lib/mstch/test/data/keys_with_questionmarks.txt @@ -0,0 +1 @@ + Hi Jon! diff --git a/lib/mstch/test/data/multiline_comment.hpp b/lib/mstch/test/data/multiline_comment.hpp new file mode 100644 index 0000000..8a655f6 --- /dev/null +++ b/lib/mstch/test/data/multiline_comment.hpp @@ -0,0 +1 @@ +const auto multiline_comment_data = mstch::map{};
\ No newline at end of file diff --git a/lib/mstch/test/data/multiline_comment.mustache b/lib/mstch/test/data/multiline_comment.mustache new file mode 100644 index 0000000..dff0893 --- /dev/null +++ b/lib/mstch/test/data/multiline_comment.mustache @@ -0,0 +1,6 @@ +{{! + +This is a multi-line comment. + +}} +Hello world! diff --git a/lib/mstch/test/data/multiline_comment.txt b/lib/mstch/test/data/multiline_comment.txt new file mode 100644 index 0000000..cd08755 --- /dev/null +++ b/lib/mstch/test/data/multiline_comment.txt @@ -0,0 +1 @@ +Hello world! diff --git a/lib/mstch/test/data/nested_dot.hpp b/lib/mstch/test/data/nested_dot.hpp new file mode 100644 index 0000000..313202f --- /dev/null +++ b/lib/mstch/test/data/nested_dot.hpp @@ -0,0 +1 @@ +const auto nested_dot_data = mstch::map{{"name", std::string{"Bruno"}}};
\ No newline at end of file diff --git a/lib/mstch/test/data/nested_dot.mustache b/lib/mstch/test/data/nested_dot.mustache new file mode 100644 index 0000000..12b0728 --- /dev/null +++ b/lib/mstch/test/data/nested_dot.mustache @@ -0,0 +1 @@ +{{#name}}Hello {{.}}{{/name}}
\ No newline at end of file diff --git a/lib/mstch/test/data/nested_dot.txt b/lib/mstch/test/data/nested_dot.txt new file mode 100644 index 0000000..58df047 --- /dev/null +++ b/lib/mstch/test/data/nested_dot.txt @@ -0,0 +1 @@ +Hello Bruno
\ No newline at end of file diff --git a/lib/mstch/test/data/nested_higher_order_sections.hpp b/lib/mstch/test/data/nested_higher_order_sections.hpp new file mode 100644 index 0000000..26ed72a --- /dev/null +++ b/lib/mstch/test/data/nested_higher_order_sections.hpp @@ -0,0 +1,6 @@ +const mstch::node nested_higher_order_sections_data = mstch::map{ + {"bold", mstch::lambda{[](const std::string& text) -> mstch::node { + return std::string{"<b>"} + text + std::string{"</b>"}; + }}}, + {"person", mstch::map{{"name", std::string{"Jonas"}}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/nested_higher_order_sections.mustache b/lib/mstch/test/data/nested_higher_order_sections.mustache new file mode 100644 index 0000000..e312fe7 --- /dev/null +++ b/lib/mstch/test/data/nested_higher_order_sections.mustache @@ -0,0 +1 @@ +{{#bold}}{{#person}}My name is {{name}}!{{/person}}{{/bold}} diff --git a/lib/mstch/test/data/nested_higher_order_sections.txt b/lib/mstch/test/data/nested_higher_order_sections.txt new file mode 100644 index 0000000..0ee6a40 --- /dev/null +++ b/lib/mstch/test/data/nested_higher_order_sections.txt @@ -0,0 +1 @@ +<b>My name is Jonas!</b> diff --git a/lib/mstch/test/data/nested_iterating.hpp b/lib/mstch/test/data/nested_iterating.hpp new file mode 100644 index 0000000..ac2f478 --- /dev/null +++ b/lib/mstch/test/data/nested_iterating.hpp @@ -0,0 +1,8 @@ +const auto nested_iterating_data = mstch::map{ + {"inner", mstch::array{mstch::map{ + {"foo", std::string{"foo"}}, + {"inner", mstch::array{mstch::map{ + {"bar", std::string{"bar"}} + }}} + }}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/nested_iterating.mustache b/lib/mstch/test/data/nested_iterating.mustache new file mode 100644 index 0000000..1a3bb1a --- /dev/null +++ b/lib/mstch/test/data/nested_iterating.mustache @@ -0,0 +1 @@ +{{#inner}}{{foo}}{{#inner}}{{bar}}{{/inner}}{{/inner}} diff --git a/lib/mstch/test/data/nested_iterating.txt b/lib/mstch/test/data/nested_iterating.txt new file mode 100644 index 0000000..323fae0 --- /dev/null +++ b/lib/mstch/test/data/nested_iterating.txt @@ -0,0 +1 @@ +foobar diff --git a/lib/mstch/test/data/nesting.hpp b/lib/mstch/test/data/nesting.hpp new file mode 100644 index 0000000..7008030 --- /dev/null +++ b/lib/mstch/test/data/nesting.hpp @@ -0,0 +1,7 @@ +const auto nesting_data = mstch::map{ + {"foo", mstch::array{ + mstch::map{{"a", mstch::map{{"b", 1}}}}, + mstch::map{{"a", mstch::map{{"b", 2}}}}, + mstch::map{{"a", mstch::map{{"b", 3}}}} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/nesting.mustache b/lib/mstch/test/data/nesting.mustache new file mode 100644 index 0000000..551366d --- /dev/null +++ b/lib/mstch/test/data/nesting.mustache @@ -0,0 +1,5 @@ +{{#foo}} + {{#a}} + {{b}} + {{/a}} +{{/foo}} diff --git a/lib/mstch/test/data/nesting.txt b/lib/mstch/test/data/nesting.txt new file mode 100644 index 0000000..7db34b1 --- /dev/null +++ b/lib/mstch/test/data/nesting.txt @@ -0,0 +1,3 @@ + 1 + 2 + 3 diff --git a/lib/mstch/test/data/nesting_same_name.hpp b/lib/mstch/test/data/nesting_same_name.hpp new file mode 100644 index 0000000..c696cc9 --- /dev/null +++ b/lib/mstch/test/data/nesting_same_name.hpp @@ -0,0 +1,8 @@ +const auto nesting_same_name_data = mstch::map{ + {"items", mstch::array{ + mstch::map{ + {"name", std::string{"name"}}, + {"items", mstch::array{1, 2, 3, 4}} + } + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/nesting_same_name.mustache b/lib/mstch/test/data/nesting_same_name.mustache new file mode 100644 index 0000000..777dbd6 --- /dev/null +++ b/lib/mstch/test/data/nesting_same_name.mustache @@ -0,0 +1 @@ +{{#items}}{{name}}{{#items}}{{.}}{{/items}}{{/items}} diff --git a/lib/mstch/test/data/nesting_same_name.txt b/lib/mstch/test/data/nesting_same_name.txt new file mode 100644 index 0000000..34fcfd3 --- /dev/null +++ b/lib/mstch/test/data/nesting_same_name.txt @@ -0,0 +1 @@ +name1234 diff --git a/lib/mstch/test/data/null_lookup_array.hpp b/lib/mstch/test/data/null_lookup_array.hpp new file mode 100644 index 0000000..ea6c961 --- /dev/null +++ b/lib/mstch/test/data/null_lookup_array.hpp @@ -0,0 +1,8 @@ +const auto null_lookup_array_data = mstch::map{ + {"name", std::string{"David"}}, + {"twitter", std::string{"@dasilvacontin"}}, + {"farray", mstch::array{ + mstch::array{std::string{"Flor"}, std::string{"@florrts"}}, + mstch::array{std::string{"Miquel"}, mstch::node{}}, + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/null_lookup_array.mustache b/lib/mstch/test/data/null_lookup_array.mustache new file mode 100644 index 0000000..0543895 --- /dev/null +++ b/lib/mstch/test/data/null_lookup_array.mustache @@ -0,0 +1,3 @@ +{{#farray}} +{{#.}}{{#.}}{{.}} {{/.}}{{^.}}no twitter{{/.}}{{/.}} +{{/farray}} diff --git a/lib/mstch/test/data/null_lookup_array.txt b/lib/mstch/test/data/null_lookup_array.txt new file mode 100644 index 0000000..d4f4dc5 --- /dev/null +++ b/lib/mstch/test/data/null_lookup_array.txt @@ -0,0 +1,2 @@ +Flor @florrts +Miquel no twitter diff --git a/lib/mstch/test/data/null_lookup_object.hpp b/lib/mstch/test/data/null_lookup_object.hpp new file mode 100644 index 0000000..cbee5f9 --- /dev/null +++ b/lib/mstch/test/data/null_lookup_object.hpp @@ -0,0 +1,14 @@ +const auto null_lookup_object_data = mstch::map{ + {"name", std::string{"David"}}, + {"twitter", std::string{"@dasilvacontin"}}, + {"fobject", mstch::array{ + mstch::map{ + {"name", std::string{"Flor"}}, + {"twitter", std::string{"@florrts"}} + }, + mstch::map{ + {"name", std::string{"Miquel"}}, + {"twitter", mstch::node{}} + } + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/null_lookup_object.mustache b/lib/mstch/test/data/null_lookup_object.mustache new file mode 100644 index 0000000..e709ae4 --- /dev/null +++ b/lib/mstch/test/data/null_lookup_object.mustache @@ -0,0 +1,3 @@ +{{#fobject}} +{{name}}'s twitter: {{#twitter}}{{.}}{{/twitter}}{{^twitter}}unknown{{/twitter}}. +{{/fobject}} diff --git a/lib/mstch/test/data/null_lookup_object.txt b/lib/mstch/test/data/null_lookup_object.txt new file mode 100644 index 0000000..d1291ee --- /dev/null +++ b/lib/mstch/test/data/null_lookup_object.txt @@ -0,0 +1,2 @@ +Flor's twitter: @florrts. +Miquel's twitter: unknown. diff --git a/lib/mstch/test/data/null_string.hpp b/lib/mstch/test/data/null_string.hpp new file mode 100644 index 0000000..619a6f9 --- /dev/null +++ b/lib/mstch/test/data/null_string.hpp @@ -0,0 +1,6 @@ +const auto null_string_data = mstch::map{ + {"name", std::string{"Elise"}}, + {"glytch", true}, + {"binary", false}, + {"value", mstch::node{}} +}; diff --git a/lib/mstch/test/data/null_string.mustache b/lib/mstch/test/data/null_string.mustache new file mode 100644 index 0000000..d087a39 --- /dev/null +++ b/lib/mstch/test/data/null_string.mustache @@ -0,0 +1,4 @@ +Hello {{name}} +glytch {{glytch}} +binary {{binary}} +value {{value}} diff --git a/lib/mstch/test/data/null_string.txt b/lib/mstch/test/data/null_string.txt new file mode 100644 index 0000000..8a0428b --- /dev/null +++ b/lib/mstch/test/data/null_string.txt @@ -0,0 +1,4 @@ +Hello Elise +glytch true +binary false +value diff --git a/lib/mstch/test/data/null_view.hpp b/lib/mstch/test/data/null_view.hpp new file mode 100644 index 0000000..a3ad5ea --- /dev/null +++ b/lib/mstch/test/data/null_view.hpp @@ -0,0 +1,4 @@ +const auto null_view_data = mstch::map{ + {"name", std::string{"Joe"}}, + {"friends", mstch::node{}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/null_view.mustache b/lib/mstch/test/data/null_view.mustache new file mode 100644 index 0000000..115b376 --- /dev/null +++ b/lib/mstch/test/data/null_view.mustache @@ -0,0 +1 @@ +{{name}}'s friends: {{#friends}}{{name}}, {{/friends}}
\ No newline at end of file diff --git a/lib/mstch/test/data/null_view.txt b/lib/mstch/test/data/null_view.txt new file mode 100644 index 0000000..15ed2ab --- /dev/null +++ b/lib/mstch/test/data/null_view.txt @@ -0,0 +1 @@ +Joe's friends:
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_array.hpp b/lib/mstch/test/data/partial_array.hpp new file mode 100644 index 0000000..a03b0e9 --- /dev/null +++ b/lib/mstch/test/data/partial_array.hpp @@ -0,0 +1,3 @@ +const auto partial_array_data = mstch::map{ + {"array", mstch::array{std::string{"1"}, std::string{"2"}, std::string{"3"}, std::string{"4"}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_array.mustache b/lib/mstch/test/data/partial_array.mustache new file mode 100644 index 0000000..7a336fe --- /dev/null +++ b/lib/mstch/test/data/partial_array.mustache @@ -0,0 +1 @@ +{{>partial}}
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_array.partial b/lib/mstch/test/data/partial_array.partial new file mode 100644 index 0000000..0ba652c --- /dev/null +++ b/lib/mstch/test/data/partial_array.partial @@ -0,0 +1,4 @@ +Here's a non-sense array of values +{{#array}} + {{.}} +{{/array}} diff --git a/lib/mstch/test/data/partial_array.txt b/lib/mstch/test/data/partial_array.txt new file mode 100644 index 0000000..892837c --- /dev/null +++ b/lib/mstch/test/data/partial_array.txt @@ -0,0 +1,5 @@ +Here's a non-sense array of values + 1 + 2 + 3 + 4 diff --git a/lib/mstch/test/data/partial_array_of_partials.hpp b/lib/mstch/test/data/partial_array_of_partials.hpp new file mode 100644 index 0000000..2b00c5a --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials.hpp @@ -0,0 +1,8 @@ +const auto partial_array_of_partials_data = mstch::map{ + {"numbers", mstch::array{ + mstch::map{{"i", std::string{"1"}}}, + mstch::map{{"i", std::string{"2"}}}, + mstch::map{{"i", std::string{"3"}}}, + mstch::map{{"i", std::string{"4"}}} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_array_of_partials.mustache b/lib/mstch/test/data/partial_array_of_partials.mustache new file mode 100644 index 0000000..1af6d68 --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials.mustache @@ -0,0 +1,4 @@ +Here is some stuff! +{{#numbers}} +{{>partial}} +{{/numbers}} diff --git a/lib/mstch/test/data/partial_array_of_partials.partial b/lib/mstch/test/data/partial_array_of_partials.partial new file mode 100644 index 0000000..bdde77d --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials.partial @@ -0,0 +1 @@ +{{i}} diff --git a/lib/mstch/test/data/partial_array_of_partials.txt b/lib/mstch/test/data/partial_array_of_partials.txt new file mode 100644 index 0000000..f622375 --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials.txt @@ -0,0 +1,5 @@ +Here is some stuff! +1 +2 +3 +4 diff --git a/lib/mstch/test/data/partial_array_of_partials_implicit.hpp b/lib/mstch/test/data/partial_array_of_partials_implicit.hpp new file mode 100644 index 0000000..28e3a3c --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials_implicit.hpp @@ -0,0 +1,3 @@ +const auto partial_array_of_partials_implicit_data = mstch::map{ + {"numbers", mstch::array{std::string{"1"}, std::string{"2"}, std::string{"3"}, std::string{"4"}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_array_of_partials_implicit.mustache b/lib/mstch/test/data/partial_array_of_partials_implicit.mustache new file mode 100644 index 0000000..1af6d68 --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials_implicit.mustache @@ -0,0 +1,4 @@ +Here is some stuff! +{{#numbers}} +{{>partial}} +{{/numbers}} diff --git a/lib/mstch/test/data/partial_array_of_partials_implicit.partial b/lib/mstch/test/data/partial_array_of_partials_implicit.partial new file mode 100644 index 0000000..12f7159 --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials_implicit.partial @@ -0,0 +1 @@ +{{.}} diff --git a/lib/mstch/test/data/partial_array_of_partials_implicit.txt b/lib/mstch/test/data/partial_array_of_partials_implicit.txt new file mode 100644 index 0000000..f622375 --- /dev/null +++ b/lib/mstch/test/data/partial_array_of_partials_implicit.txt @@ -0,0 +1,5 @@ +Here is some stuff! +1 +2 +3 +4 diff --git a/lib/mstch/test/data/partial_empty.hpp b/lib/mstch/test/data/partial_empty.hpp new file mode 100644 index 0000000..276c972 --- /dev/null +++ b/lib/mstch/test/data/partial_empty.hpp @@ -0,0 +1,3 @@ +const auto partial_empty_data = mstch::map{ + {"foo", 1} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_empty.mustache b/lib/mstch/test/data/partial_empty.mustache new file mode 100644 index 0000000..a710047 --- /dev/null +++ b/lib/mstch/test/data/partial_empty.mustache @@ -0,0 +1,2 @@ +hey {{foo}} +{{>partial}} diff --git a/lib/mstch/test/data/partial_empty.partial b/lib/mstch/test/data/partial_empty.partial new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/mstch/test/data/partial_empty.partial diff --git a/lib/mstch/test/data/partial_empty.txt b/lib/mstch/test/data/partial_empty.txt new file mode 100644 index 0000000..1a67907 --- /dev/null +++ b/lib/mstch/test/data/partial_empty.txt @@ -0,0 +1 @@ +hey 1 diff --git a/lib/mstch/test/data/partial_template.hpp b/lib/mstch/test/data/partial_template.hpp new file mode 100644 index 0000000..88c659b --- /dev/null +++ b/lib/mstch/test/data/partial_template.hpp @@ -0,0 +1,4 @@ +const mstch::node partial_template_data = mstch::map{ + {"title", mstch::lambda{[]()->mstch::node{ return std::string{"Welcome"}; }}}, + {"again", mstch::lambda{[]()->mstch::node{ return std::string{"Goodbye"}; }}}, +};
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_template.mustache b/lib/mstch/test/data/partial_template.mustache new file mode 100644 index 0000000..6a7492e --- /dev/null +++ b/lib/mstch/test/data/partial_template.mustache @@ -0,0 +1,2 @@ +<h1>{{title}}</h1> +{{>partial}} diff --git a/lib/mstch/test/data/partial_template.partial b/lib/mstch/test/data/partial_template.partial new file mode 100644 index 0000000..a404529 --- /dev/null +++ b/lib/mstch/test/data/partial_template.partial @@ -0,0 +1 @@ +Again, {{again}}! diff --git a/lib/mstch/test/data/partial_template.txt b/lib/mstch/test/data/partial_template.txt new file mode 100644 index 0000000..692698f --- /dev/null +++ b/lib/mstch/test/data/partial_template.txt @@ -0,0 +1,2 @@ +<h1>Welcome</h1> +Again, Goodbye! diff --git a/lib/mstch/test/data/partial_view.hpp b/lib/mstch/test/data/partial_view.hpp new file mode 100644 index 0000000..5c2c3eb --- /dev/null +++ b/lib/mstch/test/data/partial_view.hpp @@ -0,0 +1,42 @@ +class partial_view: public mstch::object { +private: + int m_value; + +public: + partial_view(): m_value(10000) { + register_methods(this, std::map<std::string,mstch::node(partial_view::*)()>{ + {"greeting", &partial_view::greeting}, + {"farewell", &partial_view::farewell}, + {"name", &partial_view::name}, + {"value", &partial_view::value}, + {"taxed_value", &partial_view::taxed_value}, + {"in_ca", &partial_view::in_ca} + }); + } + + mstch::node greeting() { + return std::string{"Welcome"}; + } + + mstch::node farewell() { + return std::string{"Fair enough, right?"}; + } + + mstch::node name() { + return std::string{"Chris"}; + } + + mstch::node value() { + return m_value; + } + + mstch::node taxed_value() { + return m_value - (m_value * 0.4); + } + + mstch::node in_ca() { + return true; + } +}; + +const auto partial_view_data = std::make_shared<partial_view>();
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_view.mustache b/lib/mstch/test/data/partial_view.mustache new file mode 100644 index 0000000..f8f6a5b --- /dev/null +++ b/lib/mstch/test/data/partial_view.mustache @@ -0,0 +1,3 @@ +<h1>{{greeting}}</h1> +{{>partial}} +<h3>{{farewell}}</h3> diff --git a/lib/mstch/test/data/partial_view.partial b/lib/mstch/test/data/partial_view.partial new file mode 100644 index 0000000..03df206 --- /dev/null +++ b/lib/mstch/test/data/partial_view.partial @@ -0,0 +1,5 @@ +Hello {{name}} +You have just won ${{value}}! +{{#in_ca}} +Well, ${{ taxed_value }}, after taxes. +{{/in_ca}}
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_view.txt b/lib/mstch/test/data/partial_view.txt new file mode 100644 index 0000000..c09147c --- /dev/null +++ b/lib/mstch/test/data/partial_view.txt @@ -0,0 +1,5 @@ +<h1>Welcome</h1> +Hello Chris +You have just won $10000! +Well, $6000, after taxes. +<h3>Fair enough, right?</h3> diff --git a/lib/mstch/test/data/partial_whitespace.hpp b/lib/mstch/test/data/partial_whitespace.hpp new file mode 100644 index 0000000..85fa5dc --- /dev/null +++ b/lib/mstch/test/data/partial_whitespace.hpp @@ -0,0 +1,41 @@ +class partial_whitespace: public mstch::object { +private: + int m_value; +public: + partial_whitespace(): m_value(10000) { + register_methods(this, std::map<std::string,mstch::node(partial_whitespace::*)()>{ + {"greeting", &partial_whitespace::greeting}, + {"farewell", &partial_whitespace::farewell}, + {"name", &partial_whitespace::name}, + {"value", &partial_whitespace::value}, + {"taxed_value", &partial_whitespace::taxed_value}, + {"in_ca", &partial_whitespace::in_ca} + }); + } + + mstch::node greeting() { + return std::string{"Welcome"}; + } + + mstch::node farewell() { + return std::string{"Fair enough, right?"}; + } + + mstch::node name() { + return std::string{"Chris"}; + } + + mstch::node value() { + return m_value; + } + + mstch::node taxed_value() { + return static_cast<int>(m_value - (m_value * 0.4)); + } + + mstch::node in_ca() { + return true; + } +}; + +const auto partial_whitespace_data = std::make_shared<partial_whitespace>();
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_whitespace.mustache b/lib/mstch/test/data/partial_whitespace.mustache new file mode 100644 index 0000000..48bd1ff --- /dev/null +++ b/lib/mstch/test/data/partial_whitespace.mustache @@ -0,0 +1,3 @@ +<h1>{{ greeting }}</h1> +{{> partial }} +<h3>{{ farewell }}</h3> diff --git a/lib/mstch/test/data/partial_whitespace.partial b/lib/mstch/test/data/partial_whitespace.partial new file mode 100644 index 0000000..30de8f6 --- /dev/null +++ b/lib/mstch/test/data/partial_whitespace.partial @@ -0,0 +1,5 @@ +Hello {{ name}} +You have just won ${{value }}! +{{# in_ca }} +Well, ${{ taxed_value }}, after taxes. +{{/ in_ca }}
\ No newline at end of file diff --git a/lib/mstch/test/data/partial_whitespace.txt b/lib/mstch/test/data/partial_whitespace.txt new file mode 100644 index 0000000..c09147c --- /dev/null +++ b/lib/mstch/test/data/partial_whitespace.txt @@ -0,0 +1,5 @@ +<h1>Welcome</h1> +Hello Chris +You have just won $10000! +Well, $6000, after taxes. +<h3>Fair enough, right?</h3> diff --git a/lib/mstch/test/data/recursion_with_same_names.hpp b/lib/mstch/test/data/recursion_with_same_names.hpp new file mode 100644 index 0000000..a9170db --- /dev/null +++ b/lib/mstch/test/data/recursion_with_same_names.hpp @@ -0,0 +1,8 @@ +const auto recursion_with_same_names_data = mstch::map{ + {"name", std::string{"name"}}, + {"description", std::string{"desc"}}, + {"terms", mstch::array{ + mstch::map{{"name", std::string{"t1"}}, {"index", 0}}, + mstch::map{{"name", std::string{"t2"}}, {"index", 1}} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/recursion_with_same_names.mustache b/lib/mstch/test/data/recursion_with_same_names.mustache new file mode 100644 index 0000000..c331d04 --- /dev/null +++ b/lib/mstch/test/data/recursion_with_same_names.mustache @@ -0,0 +1,7 @@ +{{ name }} +{{ description }} + +{{#terms}} + {{name}} + {{index}} +{{/terms}} diff --git a/lib/mstch/test/data/recursion_with_same_names.txt b/lib/mstch/test/data/recursion_with_same_names.txt new file mode 100644 index 0000000..cb15d75 --- /dev/null +++ b/lib/mstch/test/data/recursion_with_same_names.txt @@ -0,0 +1,7 @@ +name +desc + + t1 + 0 + t2 + 1 diff --git a/lib/mstch/test/data/reuse_of_enumerables.hpp b/lib/mstch/test/data/reuse_of_enumerables.hpp new file mode 100644 index 0000000..88c1c8e --- /dev/null +++ b/lib/mstch/test/data/reuse_of_enumerables.hpp @@ -0,0 +1,6 @@ +const auto reuse_of_enumerables_data = mstch::map{ + {"terms", mstch::array{ + mstch::map{{"name", std::string{"t1"}}, {"index", 0}}, + mstch::map{{"name", std::string{"t2"}}, {"index", 1}} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/reuse_of_enumerables.mustache b/lib/mstch/test/data/reuse_of_enumerables.mustache new file mode 100644 index 0000000..cc0cb7a --- /dev/null +++ b/lib/mstch/test/data/reuse_of_enumerables.mustache @@ -0,0 +1,8 @@ +{{#terms}} + {{name}} + {{index}} +{{/terms}} +{{#terms}} + {{name}} + {{index}} +{{/terms}} diff --git a/lib/mstch/test/data/reuse_of_enumerables.txt b/lib/mstch/test/data/reuse_of_enumerables.txt new file mode 100644 index 0000000..6d05d96 --- /dev/null +++ b/lib/mstch/test/data/reuse_of_enumerables.txt @@ -0,0 +1,8 @@ + t1 + 0 + t2 + 1 + t1 + 0 + t2 + 1 diff --git a/lib/mstch/test/data/section_as_context.hpp b/lib/mstch/test/data/section_as_context.hpp new file mode 100644 index 0000000..fc5333a --- /dev/null +++ b/lib/mstch/test/data/section_as_context.hpp @@ -0,0 +1,10 @@ +const auto section_as_context_data = mstch::map{ + {"a_object", mstch::map{ + {"title", std::string{"this is an object"}}, + {"description", std::string{"one of its attributes is a list"}}, + {"a_list", mstch::array{ + mstch::map{{"label", std::string{"listitem1"}}}, + mstch::map{{"label", std::string{"listitem2"}}} + }} + }} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/section_as_context.mustache b/lib/mstch/test/data/section_as_context.mustache new file mode 100644 index 0000000..59990f6 --- /dev/null +++ b/lib/mstch/test/data/section_as_context.mustache @@ -0,0 +1,9 @@ +{{#a_object}} + <h1>{{title}}</h1> + <p>{{description}}</p> + <ul> + {{#a_list}} + <li>{{label}}</li> + {{/a_list}} + </ul> +{{/a_object}} diff --git a/lib/mstch/test/data/section_as_context.txt b/lib/mstch/test/data/section_as_context.txt new file mode 100644 index 0000000..d834e80 --- /dev/null +++ b/lib/mstch/test/data/section_as_context.txt @@ -0,0 +1,6 @@ + <h1>this is an object</h1> + <p>one of its attributes is a list</p> + <ul> + <li>listitem1</li> + <li>listitem2</li> + </ul> diff --git a/lib/mstch/test/data/section_functions_in_partials.hpp b/lib/mstch/test/data/section_functions_in_partials.hpp new file mode 100644 index 0000000..1fc4434 --- /dev/null +++ b/lib/mstch/test/data/section_functions_in_partials.hpp @@ -0,0 +1,5 @@ +const mstch::node section_functions_in_partials_data = mstch::map{ + {"bold", mstch::lambda{[](const std::string& text) -> mstch::node { + return std::string{"<b>"} + text + std::string{"</b>"}; + }}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/section_functions_in_partials.mustache b/lib/mstch/test/data/section_functions_in_partials.mustache new file mode 100644 index 0000000..8164932 --- /dev/null +++ b/lib/mstch/test/data/section_functions_in_partials.mustache @@ -0,0 +1,3 @@ +{{> partial}} + +<p>some more text</p> diff --git a/lib/mstch/test/data/section_functions_in_partials.partial b/lib/mstch/test/data/section_functions_in_partials.partial new file mode 100644 index 0000000..3e90b00 --- /dev/null +++ b/lib/mstch/test/data/section_functions_in_partials.partial @@ -0,0 +1 @@ +{{#bold}}Hello There{{/bold}} diff --git a/lib/mstch/test/data/section_functions_in_partials.txt b/lib/mstch/test/data/section_functions_in_partials.txt new file mode 100644 index 0000000..2f5955c --- /dev/null +++ b/lib/mstch/test/data/section_functions_in_partials.txt @@ -0,0 +1,3 @@ +<b>Hello There</b> + +<p>some more text</p> diff --git a/lib/mstch/test/data/simple.hpp b/lib/mstch/test/data/simple.hpp new file mode 100644 index 0000000..254a6d3 --- /dev/null +++ b/lib/mstch/test/data/simple.hpp @@ -0,0 +1,32 @@ +class simple: public mstch::object { +private: + int m_value; +public: + simple(): + m_value(10000) + { + register_methods(this, std::map<std::string,mstch::node(simple::*)()>{ + {"name", &simple::name}, + {"value", &simple::value}, + {"taxed_value", &simple::taxed_value}, + {"in_ca", &simple::in_ca}}); + } + + mstch::node name() { + return std::string{"Chris"}; + } + + mstch::node value() { + return m_value; + } + + mstch::node taxed_value() { + return m_value - (m_value * 0.4); + } + + mstch::node in_ca() { + return true; + } +}; + +const auto simple_data = std::make_shared<simple>();
\ No newline at end of file diff --git a/lib/mstch/test/data/simple.mustache b/lib/mstch/test/data/simple.mustache new file mode 100644 index 0000000..2fea632 --- /dev/null +++ b/lib/mstch/test/data/simple.mustache @@ -0,0 +1,5 @@ +Hello {{name}} +You have just won ${{value}}! +{{#in_ca}} +Well, ${{ taxed_value }}, after taxes. +{{/in_ca}} diff --git a/lib/mstch/test/data/simple.txt b/lib/mstch/test/data/simple.txt new file mode 100644 index 0000000..5d75d65 --- /dev/null +++ b/lib/mstch/test/data/simple.txt @@ -0,0 +1,3 @@ +Hello Chris +You have just won $10000! +Well, $6000, after taxes. diff --git a/lib/mstch/test/data/string_as_context.hpp b/lib/mstch/test/data/string_as_context.hpp new file mode 100644 index 0000000..56ee3a1 --- /dev/null +++ b/lib/mstch/test/data/string_as_context.hpp @@ -0,0 +1,4 @@ +const auto string_as_context_data = mstch::map{ + {"a_string", std::string{"aa"}}, + {"a_list", mstch::array{std::string{"a"},std::string{"b"},std::string{"c"}}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/string_as_context.mustache b/lib/mstch/test/data/string_as_context.mustache new file mode 100644 index 0000000..00f7181 --- /dev/null +++ b/lib/mstch/test/data/string_as_context.mustache @@ -0,0 +1,5 @@ +<ul> +{{#a_list}} + <li>{{a_string}}/{{.}}</li> +{{/a_list}} +</ul>
\ No newline at end of file diff --git a/lib/mstch/test/data/string_as_context.txt b/lib/mstch/test/data/string_as_context.txt new file mode 100644 index 0000000..8bd87ff --- /dev/null +++ b/lib/mstch/test/data/string_as_context.txt @@ -0,0 +1,5 @@ +<ul> + <li>aa/a</li> + <li>aa/b</li> + <li>aa/c</li> +</ul>
\ No newline at end of file diff --git a/lib/mstch/test/data/two_in_a_row.hpp b/lib/mstch/test/data/two_in_a_row.hpp new file mode 100644 index 0000000..496ef8f --- /dev/null +++ b/lib/mstch/test/data/two_in_a_row.hpp @@ -0,0 +1,4 @@ +const auto two_in_a_row_data = mstch::map{ + {"name", std::string{"Joe"}}, + {"greeting", std::string{"Welcome"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/two_in_a_row.mustache b/lib/mstch/test/data/two_in_a_row.mustache new file mode 100644 index 0000000..b23f29e --- /dev/null +++ b/lib/mstch/test/data/two_in_a_row.mustache @@ -0,0 +1 @@ +{{greeting}}, {{name}}! diff --git a/lib/mstch/test/data/two_in_a_row.txt b/lib/mstch/test/data/two_in_a_row.txt new file mode 100644 index 0000000..c6d6a9b --- /dev/null +++ b/lib/mstch/test/data/two_in_a_row.txt @@ -0,0 +1 @@ +Welcome, Joe! diff --git a/lib/mstch/test/data/two_sections.hpp b/lib/mstch/test/data/two_sections.hpp new file mode 100644 index 0000000..16f0cc0 --- /dev/null +++ b/lib/mstch/test/data/two_sections.hpp @@ -0,0 +1 @@ +const auto two_sections_data = mstch::map{};
\ No newline at end of file diff --git a/lib/mstch/test/data/two_sections.mustache b/lib/mstch/test/data/two_sections.mustache new file mode 100644 index 0000000..a4b9f2a --- /dev/null +++ b/lib/mstch/test/data/two_sections.mustache @@ -0,0 +1,4 @@ +{{#foo}} +{{/foo}} +{{#bar}} +{{/bar}} diff --git a/lib/mstch/test/data/two_sections.txt b/lib/mstch/test/data/two_sections.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/mstch/test/data/two_sections.txt diff --git a/lib/mstch/test/data/unescaped.hpp b/lib/mstch/test/data/unescaped.hpp new file mode 100644 index 0000000..66914a9 --- /dev/null +++ b/lib/mstch/test/data/unescaped.hpp @@ -0,0 +1,3 @@ +const mstch::node unescaped_data = mstch::map{ + {"title", mstch::lambda{[]()->mstch::node{ return std::string{"Bear > Shark"}; }}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/unescaped.mustache b/lib/mstch/test/data/unescaped.mustache new file mode 100644 index 0000000..6b07d7b --- /dev/null +++ b/lib/mstch/test/data/unescaped.mustache @@ -0,0 +1 @@ +<h1>{{{title}}}</h1> diff --git a/lib/mstch/test/data/unescaped.txt b/lib/mstch/test/data/unescaped.txt new file mode 100644 index 0000000..089ad79 --- /dev/null +++ b/lib/mstch/test/data/unescaped.txt @@ -0,0 +1 @@ +<h1>Bear > Shark</h1> diff --git a/lib/mstch/test/data/whitespace.hpp b/lib/mstch/test/data/whitespace.hpp new file mode 100644 index 0000000..9fa087f --- /dev/null +++ b/lib/mstch/test/data/whitespace.hpp @@ -0,0 +1,4 @@ +const auto whitespace_data = mstch::map{ + {"tag1", std::string{"Hello"}}, + {"tag2", std::string{"World"}} +};
\ No newline at end of file diff --git a/lib/mstch/test/data/whitespace.mustache b/lib/mstch/test/data/whitespace.mustache new file mode 100644 index 0000000..aa76e08 --- /dev/null +++ b/lib/mstch/test/data/whitespace.mustache @@ -0,0 +1,4 @@ +{{tag1}} + + +{{tag2}}. diff --git a/lib/mstch/test/data/whitespace.txt b/lib/mstch/test/data/whitespace.txt new file mode 100644 index 0000000..851fa74 --- /dev/null +++ b/lib/mstch/test/data/whitespace.txt @@ -0,0 +1,4 @@ +Hello + + +World. diff --git a/lib/mstch/test/data/zero_view.hpp b/lib/mstch/test/data/zero_view.hpp new file mode 100644 index 0000000..f6006b3 --- /dev/null +++ b/lib/mstch/test/data/zero_view.hpp @@ -0,0 +1 @@ +const auto zero_view_data = mstch::map{{"nums", mstch::array{0, 1, 2}}};
\ No newline at end of file diff --git a/lib/mstch/test/data/zero_view.mustache b/lib/mstch/test/data/zero_view.mustache new file mode 100644 index 0000000..1cdc190 --- /dev/null +++ b/lib/mstch/test/data/zero_view.mustache @@ -0,0 +1 @@ +{{#nums}}{{.}},{{/nums}}
\ No newline at end of file diff --git a/lib/mstch/test/data/zero_view.txt b/lib/mstch/test/data/zero_view.txt new file mode 100644 index 0000000..2aee585 --- /dev/null +++ b/lib/mstch/test/data/zero_view.txt @@ -0,0 +1 @@ +0,1,2,
\ No newline at end of file diff --git a/lib/mstch/test/specs_lambdas.hpp b/lib/mstch/test/specs_lambdas.hpp new file mode 100644 index 0000000..603c9b6 --- /dev/null +++ b/lib/mstch/test/specs_lambdas.hpp @@ -0,0 +1,32 @@ +std::map<std::string,mstch::node> specs_lambdas { + {"Interpolation", mstch::lambda{[](const std::string&) -> mstch::node { + return std::string{"world"}; + }}}, + {"Interpolation - Expansion", mstch::lambda{[](const std::string&) -> mstch::node { + return std::string{"{{planet}}"}; + }}}, + {"Interpolation - Alternate Delimiters", mstch::lambda{[](const std::string&) -> mstch::node { + return std::string{"|planet| => {{planet}}"}; + }}}, + {"Interpolation - Multiple Calls", mstch::lambda{[](const std::string&) -> mstch::node { + static int calls = 0; return ++calls; + }}}, + {"Escaping", mstch::lambda{[](const std::string&) -> mstch::node { + return std::string{">"}; + }}}, + {"Section", mstch::lambda{[](const std::string& txt) -> mstch::node { + return std::string{(txt == "{{x}}") ? "yes" : "no"}; + }}}, + {"Section - Expansion", mstch::lambda{[](const std::string& txt) -> mstch::node { + return txt + std::string{"{{planet}}"} + txt; + }}}, + {"Section - Alternate Delimiters", mstch::lambda{[](const std::string& txt) -> mstch::node { + return txt + std::string{"{{planet}} => |planet|"} + txt; + }}}, + {"Section - Multiple Calls", mstch::lambda{[](const std::string& txt) -> mstch::node { + return "__" + txt + "__"; + }}}, + {"Inverted Section", mstch::lambda{[](const std::string&) -> mstch::node { + return false; + }}} +};
\ No newline at end of file diff --git a/lib/mstch/test/test_context.hpp b/lib/mstch/test/test_context.hpp new file mode 100644 index 0000000..edef5e3 --- /dev/null +++ b/lib/mstch/test/test_context.hpp @@ -0,0 +1,58 @@ +#include "data/simple.hpp" +#include "data/empty_string.hpp" +#include "data/multiline_comment.hpp" +#include "data/included_tag.hpp" +#include "data/string_as_context.hpp" +#include "data/falsy_array.hpp" +#include "data/nested_dot.hpp" +#include "data/escaped.hpp" +#include "data/partial_view.hpp" +#include "data/nested_higher_order_sections.hpp" +#include "data/zero_view.hpp" +#include "data/disappearing_whitespace.hpp" +#include "data/ampersand_escape.hpp" +#include "data/falsy.hpp" +#include "data/reuse_of_enumerables.hpp" +#include "data/apostrophe.hpp" +#include "data/grandparent_context.hpp" +#include "data/higher_order_sections.hpp" +#include "data/empty_list.hpp" +#include "data/two_in_a_row.hpp" +#include "data/partial_array_of_partials.hpp" +#include "data/keys_with_questionmarks.hpp" +#include "data/error_not_found.hpp" +#include "data/complex.hpp" +#include "data/double_render.hpp" +#include "data/null_lookup_object.hpp" +#include "data/error_eof_in_tag.hpp" +#include "data/delimiters.hpp" +#include "data/null_view.hpp" +#include "data/null_string.hpp" +#include "data/comments.hpp" +#include "data/null_lookup_array.hpp" +#include "data/section_as_context.hpp" +#include "data/unescaped.hpp" +#include "data/dot_notation.hpp" +#include "data/recursion_with_same_names.hpp" +#include "data/two_sections.hpp" +#include "data/partial_array_of_partials_implicit.hpp" +#include "data/changing_delimiters.hpp" +#include "data/nesting_same_name.hpp" +#include "data/partial_empty.hpp" +#include "data/inverted_section.hpp" +#include "data/nested_iterating.hpp" +#include "data/partial_template.hpp" +#include "data/nesting.hpp" +#include "data/bug_11_eating_whitespace.hpp" +#include "data/implicit_iterator.hpp" +#include "data/whitespace.hpp" +#include "data/array_of_strings.hpp" +#include "data/empty_sections.hpp" +#include "data/context_lookup.hpp" +#include "data/section_functions_in_partials.hpp" +#include "data/partial_whitespace.hpp" +#include "data/backslashes.hpp" +#include "data/error_eof_in_section.hpp" +#include "data/empty_template.hpp" +#include "data/partial_array.hpp" +#include "data/bug_length_property.hpp" diff --git a/lib/mstch/test/test_main.cpp b/lib/mstch/test/test_main.cpp new file mode 100644 index 0000000..a52fe3c --- /dev/null +++ b/lib/mstch/test/test_main.cpp @@ -0,0 +1,155 @@ +#define CATCH_CONFIG_MAIN + +#include "catch.hpp" +#include "rapidjson/document.h" +#include "mstch/mstch.hpp" +#include "test_context.hpp" +#include "test_data.hpp" +#include "specs_data.hpp" +#include "specs_lambdas.hpp" + +using namespace mstchtest; + +mstch::node to_value(const rapidjson::Value& val) { + if (val.IsString()) + return std::string{val.GetString()}; + if (val.IsBool()) + return val.GetBool(); + if (val.IsDouble()) + return val.GetDouble(); + if (val.IsInt()) + return val.GetInt(); + return mstch::node{}; +} + +mstch::array to_array(const rapidjson::Value& val); + +mstch::map to_object(const rapidjson::Value& val) { + mstch::map ret; + for (auto i = val.MemberBegin(); i != val.MemberEnd(); ++i) { + if (i->value.IsArray()) + ret.insert(std::make_pair(i->name.GetString(), to_array(i->value))); + else if (i->value.IsObject()) + ret.insert(std::make_pair(i->name.GetString(), to_object(i->value))); + else + ret.insert(std::make_pair(i->name.GetString(), to_value(i->value))); + } + return ret; +} + +mstch::array to_array(const rapidjson::Value& val) { + mstch::array ret; + for (auto i = val.Begin(); i != val.End(); ++i) { + if (i->IsArray()) + ret.push_back(to_array(*i)); + else if (i->IsObject()) + ret.push_back(to_object(*i)); + else + ret.push_back(to_value(*i)); + } + return ret; +} + +mstch::node parse_with_rapidjson(const std::string& str) { + rapidjson::Document doc; + doc.Parse(str.c_str()); + return to_object(doc); +} + +#define MSTCH_PARTIAL_TEST(x) TEST_CASE(#x) { \ + REQUIRE(x ## _txt == mstch::render(x ## _mustache, x ## _data, {{"partial", x ## _partial}})); \ +} + +#define MSTCH_TEST(x) TEST_CASE(#x) { \ + REQUIRE(x ## _txt == mstch::render(x ## _mustache, x ## _data)); \ +} + +#define SPECS_TEST(x) TEST_CASE("specs_" #x) { \ + using boost::get; \ + auto data = parse_with_rapidjson(x ## _json); \ + for (auto& test_item: get<mstch::array>(get<mstch::map>(data)["tests"])) {\ + auto test = get<mstch::map>(test_item); \ + std::map<std::string,std::string> partials; \ + if (test.count("partials")) \ + for (auto& partial_item: get<mstch::map>(test["partials"])) \ + partials.insert(std::make_pair(partial_item.first, get<std::string>(partial_item.second))); \ + mstch::map context; \ + for (auto& data_item: get<mstch::map>(test["data"])) \ + if (data_item.first == "lambda") \ + context.insert(std::make_pair(data_item.first, specs_lambdas[get<std::string>(test["name"])])); \ + else \ + context.insert(data_item); \ + SECTION(get<std::string>(test["name"])) \ + REQUIRE(mstch::render( \ + get<std::string>(test["template"]), \ + context, partials) == \ + get<std::string>(test["expected"])); \ + } \ +} + +MSTCH_TEST(ampersand_escape) +MSTCH_TEST(apostrophe) +MSTCH_TEST(array_of_strings) +MSTCH_TEST(backslashes) +MSTCH_TEST(bug_11_eating_whitespace) +MSTCH_TEST(bug_length_property) +MSTCH_TEST(changing_delimiters) +MSTCH_TEST(comments) +MSTCH_TEST(complex) +MSTCH_TEST(context_lookup) +MSTCH_TEST(delimiters) +MSTCH_TEST(disappearing_whitespace) +MSTCH_TEST(dot_notation) +MSTCH_TEST(double_render) +MSTCH_TEST(empty_list) +MSTCH_TEST(empty_sections) +MSTCH_TEST(empty_string) +MSTCH_TEST(empty_template) +MSTCH_TEST(error_eof_in_section) +MSTCH_TEST(error_eof_in_tag) +MSTCH_TEST(error_not_found) +MSTCH_TEST(escaped) +MSTCH_TEST(falsy) +MSTCH_TEST(falsy_array) +MSTCH_TEST(grandparent_context) +MSTCH_TEST(higher_order_sections) +MSTCH_TEST(implicit_iterator) +MSTCH_TEST(included_tag) +MSTCH_TEST(inverted_section) +MSTCH_TEST(keys_with_questionmarks) +MSTCH_TEST(multiline_comment) +MSTCH_TEST(nested_dot) +MSTCH_TEST(nested_higher_order_sections) +MSTCH_TEST(nested_iterating) +MSTCH_TEST(nesting) +MSTCH_TEST(nesting_same_name) +MSTCH_TEST(null_lookup_array) +MSTCH_TEST(null_lookup_object) +MSTCH_TEST(null_string) +MSTCH_TEST(null_view) +MSTCH_PARTIAL_TEST(partial_array) +MSTCH_PARTIAL_TEST(partial_array_of_partials) +MSTCH_PARTIAL_TEST(partial_array_of_partials_implicit) +MSTCH_PARTIAL_TEST(partial_empty) +MSTCH_PARTIAL_TEST(partial_template) +MSTCH_PARTIAL_TEST(partial_view) +MSTCH_PARTIAL_TEST(partial_whitespace) +MSTCH_TEST(recursion_with_same_names) +MSTCH_TEST(reuse_of_enumerables) +MSTCH_TEST(section_as_context) +MSTCH_PARTIAL_TEST(section_functions_in_partials) +MSTCH_TEST(simple) +MSTCH_TEST(string_as_context) +MSTCH_TEST(two_in_a_row) +MSTCH_TEST(two_sections) +MSTCH_TEST(unescaped) +MSTCH_TEST(whitespace) +MSTCH_TEST(zero_view) + +SPECS_TEST(comments) +SPECS_TEST(delimiters) +SPECS_TEST(interpolation) +SPECS_TEST(inverted) +SPECS_TEST(partials) +SPECS_TEST(sections) +SPECS_TEST(lambdas) |
