Repository: nifi-minifi-cpp Updated Branches: refs/heads/master 342cee09a -> d4f0bcfd3
http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/include/bustache/generate/ostream.hpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/include/bustache/generate/ostream.hpp b/thirdparty/bustache/include/bustache/generate/ostream.hpp new file mode 100644 index 0000000..8f1a83e --- /dev/null +++ b/thirdparty/bustache/include/bustache/generate/ostream.hpp @@ -0,0 +1,60 @@ +/*////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2016 Jamboree + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +//////////////////////////////////////////////////////////////////////////////*/ +#ifndef BUSTACHE_GENERATE_OSTREAM_HPP_INCLUDED +#define BUSTACHE_GENERATE_OSTREAM_HPP_INCLUDED + +#include <iostream> +#include <bustache/generate.hpp> + +namespace bustache { namespace detail +{ + template<class CharT, class Traits> + struct ostream_sink + { + std::basic_ostream<CharT, Traits>& out; + + void operator()(char const* it, char const* end) const + { + out.write(it, end - it); + } + + template<class T> + void operator()(T data) const + { + out << data; + } + + void operator()(bool data) const + { + out << (data ? "true" : "false"); + } + }; +}} + +namespace bustache +{ + template<class CharT, class Traits, class Context> + void generate_ostream + ( + std::basic_ostream<CharT, Traits>& out, format const& fmt, + value::view const& data, Context const& context, option_type flag + ) + { + detail::ostream_sink<CharT, Traits> sink{out}; + generate(sink, fmt, data, context, flag); + } + + // This is instantiated in src/generate.cpp. + extern template + void generate_ostream + ( + std::ostream& out, format const& fmt, + value::view const& data, detail::any_context const& context, option_type flag + ); +} + +#endif \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/include/bustache/generate/string.hpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/include/bustache/generate/string.hpp b/thirdparty/bustache/include/bustache/generate/string.hpp new file mode 100644 index 0000000..cd1ddb4 --- /dev/null +++ b/thirdparty/bustache/include/bustache/generate/string.hpp @@ -0,0 +1,108 @@ +/*////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2016 Jamboree + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +//////////////////////////////////////////////////////////////////////////////*/ +#ifndef BUSTACHE_GENERATE_STRING_HPP_INCLUDED +#define BUSTACHE_GENERATE_STRING_HPP_INCLUDED + +#include <cstdio> // for snprintf +#include <string> +#include <bustache/generate.hpp> + +namespace bustache { namespace detail +{ + template<class String> + struct string_sink + { + String& out; + + void operator()(char const* it, char const* end) const + { + out.insert(out.end(), it, end); + } + + void operator()(int data) const + { + append_num("%d", data); + } + + void operator()(double data) const + { + append_num("%g", data); + } + + void operator()(bool data) const + { + data ? append("true") : append("false"); + } + + template<std::size_t N> + void append(char const (&str)[N]) const + { + out.insert(out.end(), str, str + (N - 1)); + } + + template<class T> + void append_num(char const* fmt, T data) const + { + char buf[64]; + char* p; + auto old_size = out.size(); + auto capacity = out.capacity(); + auto bufsize = capacity - old_size; + if (bufsize) + { + out.resize(capacity); + p = &out.front() + old_size; + } + else + { + bufsize = sizeof(buf); + p = buf; + } + auto n = std::snprintf(p, bufsize, fmt, data); + if (n < 0) // error + return; + if (unsigned(n + 1) <= bufsize) + { + if (p == buf) + { + out.insert(out.end(), p, p + n); + return; + } + } + else + { + out.resize(old_size + n + 1); // '\0' will be written + std::snprintf(&out.front() + old_size, n + 1, fmt, data); + } + out.resize(old_size + n); + } + }; +}} + +namespace bustache +{ + template<class String, class Context> + void generate_string + ( + String& out, format const& fmt, + value::view const& data, Context const& context, option_type flag + ) + { + detail::string_sink<String> sink{out}; + generate(sink, fmt, data, context, flag); + } + + // This is instantiated in src/generate.cpp. + extern template + void generate_string + ( + std::string& out, format const& fmt, + value::view const& data, detail::any_context const& context, option_type flag + ); +} + +#endif \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/include/bustache/model.hpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/include/bustache/model.hpp b/thirdparty/bustache/include/bustache/model.hpp new file mode 100644 index 0000000..87b7a28 --- /dev/null +++ b/thirdparty/bustache/include/bustache/model.hpp @@ -0,0 +1,165 @@ +/*////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2014-2017 Jamboree + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +//////////////////////////////////////////////////////////////////////////////*/ +#ifndef BUSTACHE_MODEL_HPP_INCLUDED +#define BUSTACHE_MODEL_HPP_INCLUDED + +#include <bustache/format.hpp> +#include <bustache/detail/variant.hpp> +#include <bustache/detail/any_context.hpp> +#include <vector> +#include <functional> +#include <boost/unordered_map.hpp> + +namespace bustache +{ + class value; + + using array = std::vector<value>; + + // We use boost::unordered_map because it allows incomplete type. + using object = boost::unordered_map<std::string, value>; + + using lambda0v = std::function<value()>; + + using lambda0f = std::function<format()>; + + using lambda1v = std::function<value(ast::content_list const&)>; + + using lambda1f = std::function<format(ast::content_list const&)>; + + namespace detail + { + struct bool_ + { + bool_(bool); + }; + } + +#define BUSTACHE_VALUE(X, D) \ + X(0, std::nullptr_t, D) \ + X(1, bool, D) \ + X(2, int, D) \ + X(3, double, D) \ + X(4, std::string, D) \ + X(5, array, D) \ + X(6, lambda0v, D) \ + X(7, lambda0f, D) \ + X(8, lambda1v, D) \ + X(9, lambda1f, D) \ + X(10, object, D) \ +/***/ + + class value : public variant_base<value> + { + static std::nullptr_t match_type(std::nullptr_t); + static int match_type(int); + // Use a fake bool_ to prevent unintended bool conversion. + static bool match_type(detail::bool_); + static double match_type(double); + static std::string match_type(std::string); + static array match_type(array); + static lambda0v match_type(lambda0v); + static lambda0f match_type(lambda0f); + static lambda1v match_type(lambda1v); + static lambda1f match_type(lambda1f); + static object match_type(object); + // Need to override for `char const*`, otherwise `bool` will be chosen + static std::string match_type(char const*); + + public: + + struct view; + using pointer = variant_ptr<view>; + + Zz_BUSTACHE_VARIANT_DECL(value, BUSTACHE_VALUE, false) + + value() noexcept : _which(0), _0() {} + + pointer get_pointer() const + { + return {_which, _storage}; + } + }; + + struct value::view : variant_base<view> + { + using switcher = value::switcher; + +#define BUSTACHE_VALUE_VIEW_CTOR(N, U, D) \ + view(U const& data) noexcept : _which(N), _data(&data) {} + BUSTACHE_VALUE(BUSTACHE_VALUE_VIEW_CTOR,) +#undef BUSTACHE_VALUE_VIEW_CTOR + + view(value const& data) noexcept + : _which(data._which), _data(data._storage) + {} + + view(unsigned which, void const* data) noexcept + : _which(which), _data(data) + {} + + unsigned which() const + { + return _which; + } + + void const* data() const + { + return _data; + } + + pointer get_pointer() const + { + return {_which, _data}; + } + + private: + + unsigned _which; + void const* _data; + }; +#undef BUSTACHE_VALUE +} + +namespace bustache +{ + // Forward decl only. + template<class CharT, class Traits, class Context> + void generate_ostream + ( + std::basic_ostream<CharT, Traits>& out, format const& fmt, + value::view const& data, Context const& context, option_type flag + ); + + // Forward decl only. + template<class String, class Context> + void generate_string + ( + String& out, format const& fmt, + value::view const& data, Context const& context, option_type flag + ); + + template<class CharT, class Traits, class T, class Context, + typename std::enable_if<std::is_constructible<value::view, T>::value, bool>::type = true> + inline std::basic_ostream<CharT, Traits>& + operator<<(std::basic_ostream<CharT, Traits>& out, manipulator<T, Context> const& manip) + { + generate_ostream(out, manip.fmt, manip.data, detail::any_context(manip.context), manip.flag); + return out; + } + + template<class T, class Context, + typename std::enable_if<std::is_constructible<value::view, T>::value, bool>::type = true> + inline std::string to_string(manipulator<T, Context> const& manip) + { + std::string ret; + generate_string(ret, manip.fmt, manip.data, detail::any_context(manip.context), manip.flag); + return ret; + } +} + +#endif \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/src/format.cpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/src/format.cpp b/thirdparty/bustache/src/format.cpp new file mode 100644 index 0000000..7b56084 --- /dev/null +++ b/thirdparty/bustache/src/format.cpp @@ -0,0 +1,484 @@ +/*////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2014-2016 Jamboree + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +//////////////////////////////////////////////////////////////////////////////*/ +#include <cctype> +#include <utility> +#include <cstring> +#include <bustache/format.hpp> + +namespace bustache { namespace parser { namespace +{ + using delim = std::pair<std::string, std::string>; + + template<class I> + inline void skip(I& i, I e) + { + while (i != e && std::isspace(*i)) + ++i; + } + + template<class I> + inline bool parse_char(I& i, I e, char c) + { + if (i != e && *i == c) + { + ++i; + return true; + } + return false; + } + + template<class I> + inline bool parse_lit(I& i, I e, boost::string_ref const& str) + { + I i0 = i; + for (char c : str) + { + if (!parse_char(i, e, c)) + { + i = i0; + return false; + } + } + return true; + } + + template<class I> + void expect_key(I& i, I e, delim& d, std::string& attr, bool suffix) + { + skip(i, e); + I i0 = i; + while (i != e) + { + I i1 = i; + skip(i, e); + if (!suffix || parse_char(i, e, '}')) + { + skip(i, e); + if (parse_lit(i, e, d.second)) + { + attr.assign(i0, i1); + if (i0 == i1) + throw format_error(error_badkey); + return; + } + } + if (i != e) + ++i; + } + throw format_error(error_badkey); + } + + template<class I> + bool parse_content + ( + I& i0, I& i, I e, delim& d, bool& pure, + boost::string_ref& text, ast::content& attr, + boost::string_ref const& section + ); + + template<class I> + void parse_contents + ( + I i0, I& i, I e, delim& d, bool& pure, + ast::content_list& attr, boost::string_ref const& section + ); + + template<class I> + I process_pure(I& i, I e, bool& pure) + { + I i0 = i; + if (pure) + { + while (i != e) + { + if (*i == '\n') + { + i0 = ++i; + break; + } + else if (std::isspace(*i)) + ++i; + else + { + pure = false; + break; + } + } + } + return i0; + } + + template<class I> + inline bool expect_block(I& i, I e, delim& d, bool& pure, ast::block& attr) + { + expect_key(i, e, d, attr.key, false); + I i0 = process_pure(i, e, pure); + bool standalone = pure; + parse_contents(i0, i, e, d, pure, attr.contents, attr.key); + return standalone; + } + + template<class I> + bool expect_inheritance(I& i, I e, delim& d, bool& pure, ast::partial& attr) + { + expect_key(i, e, d, attr.key, false); + I i0 = process_pure(i, e, pure); + bool standalone = pure; + for (boost::string_ref text;;) + { + ast::content a; + auto end = parse_content(i0, i, e, d, pure, text, a, attr.key); + if (auto p = get<ast::block>(&a)) + attr.overriders.emplace(std::move(p->key), std::move(p->contents)); + if (end) + break; + } + return standalone; + } + + template<class I> + void expect_comment(I& i, I e, delim& d) + { + while (!parse_lit(i, e, d.second)) + { + if (i == e) + throw format_error(error_delim); + ++i; + } + } + + template<class I> + void expect_set_delim(I& i, I e, delim& d) + { + skip(i, e); + I i0 = i; + while (i != e) + { + if (std::isspace(*i)) + break; + ++i; + } + if (i == e) + throw format_error(error_baddelim); + d.first.assign(i0, i); + skip(i, e); + i0 = i; + I i1 = i; + for (;; ++i) + { + if (i == e) + throw format_error(error_set_delim); + if (*i == '=') + { + i1 = i; + break; + } + if (std::isspace(*i)) + { + i1 = i; + skip(++i, e); + if (i == e || *i != '=') + throw format_error(error_set_delim); + break; + } + } + if (i0 == i1) + throw format_error(error_baddelim); + std::string new_close(i0, i1); + skip(++i, e); + if (!parse_lit(i, e, d.second)) + throw format_error(error_delim); + d.second = std::move(new_close); + } + + struct tag_result + { + bool is_end_section; + bool check_standalone; + bool is_standalone; + }; + + template<class I> + tag_result expect_tag + ( + I& i, I e, delim& d, bool& pure, + ast::content& attr, boost::string_ref const& section + ) + { + skip(i, e); + if (i == e) + throw format_error(error_badkey); + tag_result ret{}; + switch (*i) + { + case '#': + case '^': + { + ast::section a; + a.tag = *i; + ret.is_standalone = expect_block(++i, e, d, pure, a); + attr = std::move(a); + return ret; + } + case '/': + skip(++i, e); + if (section.empty() || !parse_lit(i, e, section)) + throw format_error(error_section); + skip(i, e); + if (!parse_lit(i, e, d.second)) + throw format_error(error_delim); + ret.check_standalone = pure; + ret.is_end_section = true; + break; + case '!': + { + expect_comment(++i, e, d); + ret.check_standalone = pure; + break; + } + case '=': + { + expect_set_delim(++i, e, d); + ret.check_standalone = pure; + break; + } + case '>': + { + ast::partial a; + expect_key(++i, e, d, a.key, false); + attr = std::move(a); + ret.check_standalone = pure; + break; + } + case '&': + case '{': + { + ast::variable a; + a.tag = *i; + expect_key(++i, e, d, a.key, a.tag == '{'); + attr = std::move(a); + pure = false; + break; + } + // Extensions + case '<': + { + ast::partial a; + ret.is_standalone = expect_inheritance(++i, e, d, pure, a); + attr = std::move(a); + return ret; + } + case '$': + { + ast::block a; + ret.is_standalone = expect_block(++i, e, d, pure, a); + attr = std::move(a); + return ret; + } + default: + ast::variable a; + expect_key(i, e, d, a.key, false); + attr = std::move(a); + pure = false; + break; + } + return ret; + } + + // return true if it ends + template<class I> + bool parse_content + ( + I& i0, I& i, I e, delim& d, bool& pure, + boost::string_ref& text, ast::content& attr, + boost::string_ref const& section + ) + { + for (I i1 = i; i != e;) + { + if (*i == '\n') + { + pure = true; + i1 = ++i; + } + else if (std::isspace(*i)) + ++i; + else + { + I i2 = i; + if (parse_lit(i, e, d.first)) + { + tag_result tag(expect_tag(i, e, d, pure, attr, section)); + text = boost::string_ref(i0, i1 - i0); + if (tag.check_standalone) + { + I i3 = i; + while (i != e) + { + if (*i == '\n') + { + ++i; + break; + } + else if (std::isspace(*i)) + ++i; + else + { + pure = false; + text = boost::string_ref(i0, i2 - i0); + // For end-section, we move the current pos (i) + // since i0 is local to the section and is not + // propagated upwards. + (tag.is_end_section ? i : i0) = i3; + return tag.is_end_section; + } + } + tag.is_standalone = true; + } + if (!tag.is_standalone) + text = boost::string_ref(i0, i2 - i0); + else if (auto partial = get<ast::partial>(&attr)) + partial->indent.assign(i1, i2 - i1); + i0 = i; + return i == e || tag.is_end_section; + } + else + { + pure = false; + ++i; + } + } + } + text = boost::string_ref(i0, i - i0); + return true; + } + + template<class I> + void parse_contents + ( + I i0, I& i, I e, delim& d, bool& pure, + ast::content_list& attr, boost::string_ref const& section + ) + { + for (;;) + { + boost::string_ref text; + ast::content a; + auto end = parse_content(i0, i, e, d, pure, text, a, section); + if (!text.empty()) + attr.push_back(text); + if (!is_null(a)) + attr.push_back(std::move(a)); + if (end) + return; + } + } + + template<class I> + inline void parse_start(I& i, I e, ast::content_list& attr) + { + delim d("{{", "}}"); + bool pure = true; + parse_contents(i, i, e, d, pure, attr, {}); + } +}}} + +namespace bustache +{ + static char const* get_error_string(error_type err) + { + switch (err) + { + case error_set_delim: + return "format_error(error_set_delim): mismatched '='"; + case error_baddelim: + return "format_error(error_baddelim): invalid delimiter"; + case error_delim: + return "format_error(error_delim): mismatched delimiter"; + case error_section: + return "format_error(error_section): mismatched end section tag"; + case error_badkey: + return "format_error(error_badkey): invalid key"; + default: + return "format_error"; + } + } + + format_error::format_error(error_type err) + : runtime_error(get_error_string(err)), _err(err) + {} + + void format::init(char const* begin, char const* end) + { + parser::parse_start(begin, end, _contents); + } + + struct accum_size + { + using result_type = std::size_t; + + std::size_t operator()(ast::text const& text) const + { + return text.size(); + } + + std::size_t operator()(ast::section const& section) const + { + std::size_t n = 0; + for (auto const& content : section.contents) + n += visit(*this, content); + return n; + } + + template <typename T> + std::size_t operator()(T const&) const + { + return 0; + } + }; + + std::size_t format::text_size() const + { + accum_size accum; + std::size_t n = 0; + for (auto const& content : _contents) + n += visit(accum, content); + return n; + } + + struct copy_text_visitor + { + using result_type = void; + + char* data; + + void operator()(ast::text& text) + { + auto n = text.size(); + std::memcpy(data, text.data(), n); + text = {data, n}; + data += n; + } + + void operator()(ast::section& section) + { + for (auto& content : section.contents) + visit(*this, content); + } + + template <typename T> + void operator()(T const&) const {} + }; + + void format::copy_text(std::size_t n) + { + _text.reset(new char[n]); + copy_text_visitor visitor{_text.get()}; + for (auto& content : _contents) + visit(visitor, content); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/src/generate.cpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/src/generate.cpp b/thirdparty/bustache/src/generate.cpp new file mode 100644 index 0000000..1e41455 --- /dev/null +++ b/thirdparty/bustache/src/generate.cpp @@ -0,0 +1,77 @@ +/*////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2016 Jamboree + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +//////////////////////////////////////////////////////////////////////////////*/ + +#include <bustache/generate.hpp> +#include <bustache/generate/ostream.hpp> +#include <bustache/generate/string.hpp> + +namespace bustache { namespace detail +{ + value::pointer content_visitor_base::resolve(std::string const& key) const + { + auto ki = key.begin(); + auto ke = key.end(); + if (ki == ke) + return{}; + value::pointer pv = nullptr; + if (*ki == '.') + { + if (++ki == ke) + return cursor; + auto k0 = ki; + while (*ki != '.' && ++ki != ke); + key_cache.assign(k0, ki); + pv = find(scope->data, key_cache); + } + else + { + auto k0 = ki; + while (ki != ke && *ki != '.') ++ki; + key_cache.assign(k0, ki); + pv = scope->lookup(key_cache); + } + if (ki == ke) + return pv; + if (auto obj = get<object>(pv)) + { + auto k0 = ++ki; + while (ki != ke) + { + if (*ki == '.') + { + key_cache.assign(k0, ki); + obj = get<object>(find(*obj, key_cache)); + if (!obj) + return nullptr; + k0 = ++ki; + } + else + ++ki; + } + key_cache.assign(k0, ki); + return find(*obj, key_cache); + } + return nullptr; + } +}} + +namespace bustache +{ + template + void generate_ostream + ( + std::ostream& out, format const& fmt, + value::view const& data, detail::any_context const& context, option_type flag + ); + + template + void generate_string + ( + std::string& out, format const& fmt, + value::view const& data, detail::any_context const& context, option_type flag + ); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/test/CMakeLists.txt b/thirdparty/bustache/test/CMakeLists.txt new file mode 100644 index 0000000..74ecaee --- /dev/null +++ b/thirdparty/bustache/test/CMakeLists.txt @@ -0,0 +1,22 @@ +find_package(Catch REQUIRED) + +add_library(Catch INTERFACE) +target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR}) + +function(add_catch_test name) + set(TEST_TARGET test_${name}) + add_executable(${TEST_TARGET} + ${name}.cpp + ) + target_link_libraries(${TEST_TARGET} + ${PROJECT_NAME} Catch + ) + set_target_properties(${TEST_TARGET} PROPERTIES + CXX_STANDARD 14 + ) + add_test(${TEST_TARGET} ${TEST_TARGET}) +endfunction() + +add_catch_test(specs) + +add_catch_test(variant) http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/benchmark.cpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/test/benchmark.cpp b/thirdparty/bustache/test/benchmark.cpp new file mode 100644 index 0000000..d9b434c --- /dev/null +++ b/thirdparty/bustache/test/benchmark.cpp @@ -0,0 +1,238 @@ +#include <benchmark/benchmark.h> +#include <bustache/model.hpp> +#include <mstch/mstch.hpp> +#include <mustache.hpp> + +static char tmp[] = +R"(<h1>{{header}}</h1> +{{#bug}} +{{/bug}} + +{{# items}} + {{#first}} + <li><strong>{{name}}</strong></li> + {{/first}} + {{#link}} + <li><a {{>href}}>{{name}}</a></li> + {{/link}} +{{ /items}} + +{{#empty}} + <p>The list is empty.</p> +{{/ empty }} + +{{=[ ]=}} + +[#array]([.])[/array] + +[#items] +[count]->[count]->[count] +[/items] + +[a.b.c] == [#a][#b][c][/b][/a] + +<div class="comments"> + <h3>[header]</h3> + <ul> + [#comments] + <li class="comment"> + <h5>[name]</h5> + <p>[body]</p> + </li> + <!--[count]--> + [/comments] + </ul> +</div>)"; + +static void bustache_usage(benchmark::State& state) +{ + using namespace bustache; + + boost::unordered_map<std::string, bustache::format> context + { + {"href", "href=\"{{url}}\""_fmt} + }; + + int n = 0; + object data + { + {"header", "Colors"}, + {"items", + array + { + object + { + {"name", "red"}, + {"first", true}, + {"url", "#Red"} + }, + object + { + {"name", "green"}, + {"link", true}, + {"url", "#Green"} + }, + object + { + {"name", "blue"}, + {"link", true}, + {"url", "#Blue"} + } + } + }, + {"empty", false}, + {"count", [&n] { return ++n; }}, + {"array", array{1, 2, 3}}, + {"a", object{{"b", object{{"c", true}}}}}, + {"comments", + array + { + object + { + {"name", "Joe"}, + {"body", "<html> should be escaped"} + }, + object + { + {"name", "Sam"}, + {"body", "{{mustache}} can be seen"} + }, + object + { + {"name", "New"}, + {"body", "break\nup"} + } + } + } + }; + + format fmt(tmp); + + while (state.KeepRunning()) + { + n = 0; + to_string(fmt(data, context, escape_html)); + } +} + +static void mstch_usage(benchmark::State& state) +{ + using namespace mstch; + using namespace std::string_literals; + + std::map<std::string, std::string> context + { + {"href", "href=\"{{url}}\""} + }; + + int n = 0; + map data + { + {"header", "Colors"s}, + {"items", + array + { + map + { + {"name", "red"s}, + {"first", true}, + {"url", "#Red"s} + }, + map + { + {"name", "green"s}, + {"link", true}, + {"url", "#Green"s} + }, + map + { + {"name", "blue"s}, + {"link", true}, + {"url", "#Blue"s} + } + } + }, + {"empty", false}, + {"count", lambda{[&n]() -> node { return ++n; }}}, + {"array", array{1, 2, 3}}, + {"a", map{{"b", map{{"c", true}}}}}, + {"comments", + array + { + map + { + {"name", "Joe"s}, + {"body", "<html> should be escaped"s} + }, + map + { + {"name", "Sam"s}, + {"body", "{{mustache}} can be seen"s} + }, + map + { + {"name", "New"s}, + {"body", "break\nup"s} + } + } + } + }; + + while (state.KeepRunning()) + { + n = 0; + render(tmp, data, context); + } +} + +static void kainjow_usage(benchmark::State& state) +{ + using namespace Kainjow; + using Data = Mustache::Data; + + int n = 0; + Data data; + data.set("header", "Colors"); + { + Data d1, d2, d3; + d1.set("name", "red"); + d1.set("first", Data::Type::True); + d1.set("url", "#Red"); + d2.set("name", "green"); + d2.set("link", Data::Type::True); + d2.set("url", "#Green"); + d3.set("name", "blue"); + d3.set("link", Data::Type::True); + d3.set("url", "#Blue"); + data.set("items", Data::ListType{d1, d2, d3}); + } + data.set("empty", Data::Type::False); + data.set("count", Data::LambdaType{[&n](const std::string&) { return std::to_string(++n); }}); + data.set("array", Data::ListType{"1", "2", "3"}); + data.set("a", {"b",{"c", "true"}}); + { + Data d1, d2, d3; + d1.set("name", "Joe"); + d1.set("body", "<html> should be escaped"); + d2.set("name", "Sam"); + d2.set("body", "{{mustache}} can be seen"); + d3.set("name", "New"); + d3.set("body", "break\nup"); + data.set("comments", Data::ListType{d1, d2, d3}); + } + data.set("href", Data::PartialType{[]() { return "href=\"{{url}}\""; }}); + + Mustache fmt(tmp); + + while (state.KeepRunning()) + { + n = 0; + fmt.render(data); + } +} + +BENCHMARK(bustache_usage); +BENCHMARK(mstch_usage); +BENCHMARK(kainjow_usage); + +BENCHMARK_MAIN(); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/specs.cpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/test/specs.cpp b/thirdparty/bustache/test/specs.cpp new file mode 100644 index 0000000..617ed1d --- /dev/null +++ b/thirdparty/bustache/test/specs.cpp @@ -0,0 +1,714 @@ +/*////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2016 Jamboree + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +//////////////////////////////////////////////////////////////////////////////*/ +#define CATCH_CONFIG_MAIN +#include <catch.hpp> +#include <bustache/model.hpp> +#include <boost/unordered_map.hpp> + +using namespace bustache; +using context = boost::unordered_map<std::string, format>; + +TEST_CASE("interpolation") +{ + object const empty; + + // No Interpolation + CHECK(to_string("Hello from {Mustache}!"_fmt(empty)) == "Hello from {Mustache}!"); + + // Basic Interpolation + CHECK(to_string("Hello, {{subject}}!"_fmt(object{{"subject", "world"}})) == "Hello, world!"); + + // HTML Escaping + CHECK(to_string("These characters should be HTML escaped: {{forbidden}}"_fmt(object{{"forbidden", "& \" < >"}}, escape_html)) + == "These characters should be HTML escaped: & " < >"); + + // Triple Mustache + CHECK(to_string("These characters should not be HTML escaped: {{{forbidden}}}"_fmt(object{{"forbidden", "& \" < >"}}, escape_html)) + == "These characters should not be HTML escaped: & \" < >"); + + // Ampersand + CHECK(to_string("These characters should not be HTML escaped: {{&forbidden}}"_fmt(object{{"forbidden", "& \" < >"}}, escape_html)) + == "These characters should not be HTML escaped: & \" < >"); + + // Basic Integer Interpolation + CHECK(to_string(R"("{{mph}} miles an hour!")"_fmt(object{{"mph", 85}})) == R"("85 miles an hour!")"); + + // Triple Mustache Integer Interpolation + CHECK(to_string(R"("{{{mph}}} miles an hour!")"_fmt(object{{"mph", 85}})) == R"("85 miles an hour!")"); + + // Ampersand Integer Interpolation + CHECK(to_string(R"("{{&mph}} miles an hour!")"_fmt(object{{"mph", 85}})) == R"("85 miles an hour!")"); + + // Basic Decimal Interpolation + CHECK(to_string(R"("{{power}} jiggawatts!")"_fmt(object{{"power", 1.21}})) == R"("1.21 jiggawatts!")"); + + // Triple Decimal Interpolation + CHECK(to_string(R"("{{{power}}} jiggawatts!")"_fmt(object{{"power", 1.21}})) == R"("1.21 jiggawatts!")"); + + // Ampersand Decimal Interpolation + CHECK(to_string(R"("{{&power}} jiggawatts!")"_fmt(object{{"power", 1.21}})) == R"("1.21 jiggawatts!")"); + + // Context Misses + { + // Basic Context Miss Interpolation + CHECK(to_string("I ({{cannot}}) be seen!"_fmt(empty)) == "I () be seen!"); + + // Triple Mustache Context Miss Interpolation + CHECK(to_string("I ({{{cannot}}}) be seen!"_fmt(empty)) == "I () be seen!"); + + // Ampersand Context Miss Interpolation + CHECK(to_string("I ({{&cannot}}) be seen!"_fmt(empty)) == "I () be seen!"); + } + + // Dotted Names + { + // Dotted Names - Basic Interpolation + CHECK(to_string(R"("{{person.name}}" == "{{#person}}{{name}}{{/person}}")"_fmt(object{{"person", object{{"name", "Joe"}}}})) == R"("Joe" == "Joe")"); + + // Dotted Names - Triple Mustache Interpolation + CHECK(to_string(R"("{{{person.name}}}" == "{{#person}}{{name}}{{/person}}")"_fmt(object{{"person", object{{"name", "Joe"}}}})) == R"("Joe" == "Joe")"); + + // Dotted Names - Ampersand Interpolation + CHECK(to_string(R"("{{&person.name}}" == "{{#person}}{{name}}{{/person}}")"_fmt(object{{"person", object{{"name", "Joe"}}}})) == R"("Joe" == "Joe")"); + + // Dotted Names - Arbitrary Depth + CHECK(to_string(R"("{{a.b.c.d.e.name}}" == "Phil")"_fmt( + object{{"a", object{{"b", object{{"c", object{{"d", object{{"e", object{{"name", "Phil"}}}}}}}}}}}})) + == R"("Phil" == "Phil")"); + + // Dotted Names - Broken Chains + CHECK(to_string(R"("{{a.b.c}}" == "")"_fmt(empty)) == R"("" == "")"); + + // Dotted Names - Broken Chain Resolution + CHECK(to_string(R"("{{a.b.c.name}}" == "")"_fmt( + object{ + {"a", object{{"b", empty}}}, + {"c", object{{"name", "Jim"}}} + })) == R"("" == "")"); + + // Dotted Names - Initial Resolution + CHECK(to_string(R"("{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil")"_fmt( + object{ + {"a", object{{"b", object{{"c", object{{"d", object{{"e", object{{"name", "Phil"}}}}}}}}}}}, + {"c", object{{"c", object{{"d", object{{"e", object{{"name", "Wrong"}}}}}}}}} + })) == R"("Phil" == "Phil")"); + + // Dotted Names - Context Precedence + CHECK(to_string("{{#a}}{{b.c}}{{/a}}"_fmt(object{{"b", empty}, {"c", "ERROR"}})) == ""); + } + + object s{{"string", "---"}}; + + // Whitespace Sensitivity + { + // Interpolation - Surrounding Whitespace + CHECK(to_string("| {{string}} |"_fmt(s)) == "| --- |"); + + // Triple Mustache - Surrounding Whitespace + CHECK(to_string("| {{{string}}} |"_fmt(s)) == "| --- |"); + + // Ampersand - Surrounding Whitespace + CHECK(to_string("| {{&string}} |"_fmt(s)) == "| --- |"); + + // Interpolation - Standalone + CHECK(to_string(" {{string}}\n"_fmt(s)) == " ---\n"); + + // Triple Mustache - Standalone + CHECK(to_string(" {{{string}}}\n"_fmt(s)) == " ---\n"); + + // Ampersand - Standalone + CHECK(to_string(" {{&string}}\n"_fmt(s)) == " ---\n"); + } + + // Whitespace Insensitivity + { + // Interpolation With Padding + CHECK(to_string("|{{ string }}|"_fmt(s)) == "|---|"); + + // Triple Mustache With Padding + CHECK(to_string("|{{{ string }}}|"_fmt(s)) == "|---|"); + + // Ampersand With Padding + CHECK(to_string("|{{& string }}|"_fmt(s)) == "|---|"); + } +} + +TEST_CASE("sections") +{ + object const empty; + + // Truthy + CHECK(to_string(R"("{{#boolean}}This should be rendered.{{/boolean}}")"_fmt(object{{"boolean", true}})) + == R"("This should be rendered.")"); + + // Falsey + CHECK(to_string(R"("{{#boolean}}This should not be rendered.{{/boolean}}")"_fmt(object{{"boolean", false}})) + == R"("")"); + + // Context + CHECK(to_string(R"("{{#context}}Hi {{name}}.{{/context}}")"_fmt(object{{"context", object{{"name", "Joe"}}}})) + == R"("Hi Joe.")"); + + // Deeply Nested Contexts + CHECK(to_string( + "{{#a}}\n" + "{{one}}\n" + "{{#b}}\n" + "{{one}}{{two}}{{one}}\n" + "{{#c}}\n" + "{{one}}{{two}}{{three}}{{two}}{{one}}\n" + "{{#d}}\n" + "{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n" + "{{#e}}\n" + "{{one}}{{two}}{{three}}{{four}}{{five}}{{four}}{{three}}{{two}}{{one}}\n" + "{{/e}}\n" + "{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n" + "{{/d}}\n" + "{{one}}{{two}}{{three}}{{two}}{{one}}\n" + "{{/c}}\n" + "{{one}}{{two}}{{one}}\n" + "{{/b}}\n" + "{{one}}\n" + "{{/a}}"_fmt(object{ + {"a", object{{"one", 1}}}, + {"b", object{{"two", 2}}}, + {"c", object{{"three", 3}}}, + {"d", object{{"four", 4}}}, + {"e", object{{"five", 5}}}})) + == + "1\n" + "121\n" + "12321\n" + "1234321\n" + "123454321\n" + "1234321\n" + "12321\n" + "121\n" + "1\n"); + + // List + CHECK(to_string(R"("{{#list}}{{item}}{{/list}}")"_fmt(object{{"list", array{object{{"item", 1}}, object{{"item", 2}}, object{{"item", 3}}}}})) + == R"("123")"); + + // Empty List + CHECK(to_string(R"("{{#list}}Yay lists!{{/list}}")"_fmt(object{{"list", array{}}})) == R"("")"); + + // Doubled + CHECK(to_string( + "{{#bool}}\n" + "* first\n" + "{{/bool}}\n" + "* {{two}}\n" + "{{#bool}}\n" + "* third\n" + "{{/bool}}"_fmt(object{{"bool", true}, {"two", "second"}})) + == + "* first\n" + "* second\n" + "* third\n"); + + // Nested (Truthy) + CHECK(to_string("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"_fmt(object{{"bool", true}})) == "| A B C D E |"); + + // Nested (Falsey) + CHECK(to_string("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |"_fmt(object{{"bool", false}})) == "| A E |"); + + // Context Misses + CHECK(to_string("[{{#missing}}Found key 'missing'!{{/missing}}]"_fmt(empty)) == "[]"); + + // Implicit Iterators + { + // Implicit Iterator - String + CHECK(to_string(R"#("{{#list}}({{.}}){{/list}}")#"_fmt(object{{"list", array{1, 2, 3, 4, 5}}})) == R"#("(1)(2)(3)(4)(5)")#"); + + // Implicit Iterator - Decimal + CHECK(to_string(R"#("{{#list}}({{.}}){{/list}}")#"_fmt(object{{"list", array{1.1, 2.2, 3.3, 4.4, 5.5}}})) == R"#("(1.1)(2.2)(3.3)(4.4)(5.5)")#"); + + // Implicit Iterator - Array + CHECK(to_string(R"#("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}")#"_fmt(object{{"list", array{array{1, 2, 3}, array{"a", "b", "c"}}}})) == R"#("(123)(abc)")#"); + } + + // Dotted Names + { + // Dotted Names - Truthy + CHECK(to_string(R"("{{#a.b.c}}Here{{/a.b.c}}" == "Here")"_fmt(object{{"a", object{{"b", object{{"c", true}}}}}})) == R"("Here" == "Here")"); + + // Dotted Names - Falsey + CHECK(to_string(R"("{{#a.b.c}}Here{{/a.b.c}}" == "")"_fmt(object{{"a", object{{"b", object{{"c", false}}}}}})) == R"("" == "")"); + + // Dotted Names - Broken Chains + CHECK(to_string(R"("{{#a.b.c}}Here{{/a.b.c}}" == "")"_fmt(object{{"a", empty}})) == R"("" == "")"); + } + + object const o{{"boolean", true}}; + + // Whitespace Sensitivity + { + // Surrounding Whitespace + CHECK(to_string(" | {{#boolean}}\t|\t{{/boolean}} | \n"_fmt(o)) == " | \t|\t | \n"); + + // Internal Whitespace + CHECK(to_string(" | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n"_fmt(o)) == " | \n | \n"); + + // Indented Inline Sections + CHECK(to_string(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n"_fmt(o)) == " YES\n GOOD\n"); + + // Standalone Lines + CHECK(to_string( + "| This Is\n" + "{{#boolean}}\n" + "|\n" + "{{/boolean}}\n" + "| A Line"_fmt(o)) + == + "| This Is\n" + "|\n" + "| A Line"); + + // Indented Standalone Lines + CHECK(to_string( + "| This Is\n" + " {{#boolean}}\n" + "|\n" + " {{/boolean}}\n" + "| A Line"_fmt(o)) + == + "| This Is\n" + "|\n" + "| A Line"); + + // Standalone Line Endings + CHECK(to_string("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|"_fmt(o)) == "|\r\n|"); + + // Standalone Without Previous Line + CHECK(to_string(" {{#boolean}}\n#{{/boolean}}\n/"_fmt(o)) == "#\n/"); + + // Standalone Without Newline + CHECK(to_string("#{{#boolean}}\n/\n {{/boolean}}"_fmt(o)) == "#\n/\n"); + } + + // Whitespace Insensitivity + { + CHECK(to_string("|{{# boolean }}={{/ boolean }}|"_fmt(o)) == "|=|"); + } +} + +TEST_CASE("inverted") +{ + object const empty; + + // Falsey + CHECK(to_string(R"("{{^boolean}}This should be rendered.{{/boolean}}")"_fmt(object{{"boolean", false}})) == R"("This should be rendered.")"); + + // Truthy + CHECK(to_string(R"("{{^boolean}}This should not be rendered.{{/boolean}}")"_fmt(object{{"boolean", true}})) == R"("")"); + + // Context + CHECK(to_string(R"("{{^context}}Hi {{name}}.{{/context}}")"_fmt(object{{"context", object{{"name", "Joe"}}}})) == R"("")"); + + // List + CHECK(to_string(R"("{{^list}}{{n}}{{/list}}")"_fmt(object{{"list", array{object{{"n", 1}}, object{{"n", 2}}, object{{"n", 3}}}}})) == R"("")"); + + // Empty List + CHECK(to_string(R"("{{^list}}Yay lists!{{/list}}")"_fmt(object{{"list", array{}}})) == R"("Yay lists!")"); + + // Doubled + CHECK(to_string( + "{{^bool}}\n" + "* first\n" + "{{/bool}}\n" + "* {{two}}\n" + "{{^bool}}\n" + "* third\n" + "{{/bool}}"_fmt(object{{"bool", false}, {"two", "second"}})) + == + "* first\n" + "* second\n" + "* third\n"); + + // Nested (Falsey) + CHECK(to_string("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |"_fmt(object{{"bool", false}})) == "| A B C D E |"); + + // Nested (Truthy) + CHECK(to_string("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |"_fmt(object{{"bool", true}})) == "| A E |"); + + // Context Misses + CHECK(to_string("[{{^missing}}Cannot find key 'missing'!{{/missing}}]"_fmt(empty)) == "[Cannot find key 'missing'!]"); + + // Dotted Names + { + // Dotted Names - Truthy + CHECK(to_string(R"("{{^a.b.c}}Not Here{{/a.b.c}}" == "")"_fmt(object{{"a", object{{"b", object{{"c", true}}}}}})) == R"("" == "")"); + + // Dotted Names - Falsey + CHECK(to_string(R"("{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here")"_fmt(object{{"a", object{{"b", object{{"c", false}}}}}})) == R"("Not Here" == "Not Here")"); + + // Dotted Names - Broken Chains + CHECK(to_string(R"("{{^a.b.c}}Not Here{{/a.b.c}}" == "Not Here")"_fmt(object{{"a", empty}})) == R"("Not Here" == "Not Here")"); + } + + object const o{{"boolean", false}}; + + // Whitespace Sensitivity + { + // Surrounding Whitespace + CHECK(to_string(" | {{^boolean}}\t|\t{{/boolean}} | \n"_fmt(o)) == " | \t|\t | \n"); + + // Internal Whitespace + CHECK(to_string(" | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n"_fmt(o)) == " | \n | \n"); + + // Indented Inline Sections + CHECK(to_string(" {{^boolean}}YES{{/boolean}}\n {{^boolean}}GOOD{{/boolean}}\n"_fmt(o)) == " YES\n GOOD\n"); + + // Standalone Lines + CHECK(to_string( + "| This Is\n" + "{{^boolean}}\n" + "|\n" + "{{/boolean}}\n" + "| A Line"_fmt(o)) + == + "| This Is\n" + "|\n" + "| A Line"); + + // Indented Standalone Lines + CHECK(to_string( + "| This Is\n" + " {{^boolean}}\n" + "|\n" + " {{/boolean}}\n" + "| A Line"_fmt(o)) + == + "| This Is\n" + "|\n" + "| A Line"); + + // Standalone Line Endings + CHECK(to_string("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|"_fmt(o)) == "|\r\n|"); + + // Standalone Without Previous Line + CHECK(to_string(" {{^boolean}}\n#{{/boolean}}\n/"_fmt(o)) == "#\n/"); + + // Standalone Without Newline + CHECK(to_string("#{{^boolean}}\n/\n {{/boolean}}"_fmt(o)) == "#\n/\n"); + } + + // Whitespace Insensitivity + { + CHECK(to_string("|{{^ boolean }}={{/ boolean }}|"_fmt(o)) == "|=|"); + } +} + +TEST_CASE("delimiters") +{ + // Pair Behavior + CHECK(to_string("{{=<% %>=}}(<%text%>)"_fmt(object{{"text", "Hey!"}})) == "(Hey!)"); + + // Special Characters + CHECK(to_string("({{=[ ]=}}[text])"_fmt(object{{"text", "It worked!"}})) == "(It worked!)"); + + // Sections + CHECK(to_string( + "[\n" + "{{#section}}\n" + " {{data}}\n" + " |data|\n" + "{{/section}}\n" + "{{= | | =}}\n" + "|#section|\n" + " {{data}}\n" + " |data|\n" + "|/section|\n" + "]"_fmt(object{{"section", true}, {"data", "I got interpolated."}})) + == + "[\n" + " I got interpolated.\n" + " |data|\n" + " {{data}}\n" + " I got interpolated.\n" + "]"); + + // Inverted Sections + CHECK(to_string( + "[\n" + "{{^section}}\n" + " {{data}}\n" + " |data|\n" + "{{/section}}\n" + "{{= | | =}}\n" + "|^section|\n" + " {{data}}\n" + " |data|\n" + "|/section|\n" + "]"_fmt(object{{"section", false},{"data", "I got interpolated."}})) + == + "[\n" + " I got interpolated.\n" + " |data|\n" + " {{data}}\n" + " I got interpolated.\n" + "]"); + + // Partial Inheritence + CHECK(to_string( + "[ {{>include}} ]\n" + "{{= | | =}}\n" + "[ |>include| ]"_fmt(object{{"value", "yes"}}, context{{"include", ".{{value}}."_fmt}})) + == + "[ .yes. ]\n" + "[ .yes. ]"); + + // Post-Partial Behavior + CHECK(to_string( + "[ {{>include}} ]\n" + "[ .{{value}}. .|value|. ]"_fmt(object{{"value", "yes"}}, context{{"include", ".{{value}}. {{= | | =}} .|value|."_fmt}})) + == + "[ .yes. .yes. ]\n" + "[ .yes. .|value|. ]"); + + object const empty; + + // Whitespace Sensitivity + { + // Surrounding Whitespace + CHECK(to_string("| {{=@ @=}} |"_fmt(empty)) == "| |"); + + // Outlying Whitespace (Inline) + CHECK(to_string(" | {{=@ @=}}\n"_fmt(empty)) == " | \n"); + + // Standalone Tag + CHECK(to_string( + "Begin.\n" + "{{=@ @=}}\n" + "End."_fmt(empty)) + == + "Begin.\n" + "End."); + + // Indented Standalone Tag + CHECK(to_string( + "Begin.\n" + " {{=@ @=}}\n" + "End."_fmt(empty)) + == + "Begin.\n" + "End."); + + // Standalone Line Endings + CHECK(to_string("|\r\n{{= @ @ =}}\r\n|"_fmt(empty)) == "|\r\n|"); + + // Standalone Without Previous Line + CHECK(to_string(" {{=@ @=}}\n="_fmt(empty)) == "="); + + // Standalone Without Newline + CHECK(to_string("=\n {{=@ @=}}"_fmt(empty)) == "=\n"); + } + + // Whitespace Insensitivity + { + // Pair with Padding + CHECK(to_string("|{{= @ @ =}}|"_fmt(empty)) == "||"); + } +} + +TEST_CASE("comments") +{ + object const empty; + + // Inline + CHECK(to_string("12345{{! Comment Block! }}67890"_fmt(empty)) == "1234567890"); + + // Multiline + CHECK(to_string( + "12345{{!\n" + " This is a\n" + " multi-line comment...\n" + "}}67890"_fmt(empty)) + == + "1234567890"); + + // Standalone + CHECK(to_string( + "Begin.\n" + "{{! Comment Block! }}\n" + "End."_fmt(empty)) + == + "Begin.\n" + "End."); + + // Indented Standalone + CHECK(to_string( + "Begin.\n" + " {{! Comment Block! }}\n" + "End."_fmt(empty)) + == + "Begin.\n" + "End."); + + // Standalone Line Endings + CHECK(to_string("|\r\n{{! Standalone Comment }}\r\n|"_fmt(empty)) == "|\r\n|"); + + // Standalone Without Previous Line + CHECK(to_string(" {{! I'm Still Standalone }}\n!"_fmt(empty)) == "!"); + + // Standalone Without Newline + CHECK(to_string("!\n {{! I'm Still Standalone }}"_fmt(empty)) == "!\n"); + + // Multiline Standalone + CHECK(to_string( + "Begin.\n" + "{{!\n" + "Something's going on here...\n" + "}}\n" + "End."_fmt(empty)) + == + "Begin.\n" + "End."); + + // Indented Multiline Standalone + CHECK(to_string( + "Begin.\n" + " {{!\n" + " Something's going on here...\n" + " }}\n" + "End."_fmt(empty)) + == + "Begin.\n" + "End."); + + // Indented Inline + CHECK(to_string(" 12 {{! 34 }}\n"_fmt(empty)) == " 12 \n"); + + // Surrounding Whitespace + CHECK(to_string("12345 {{! Comment Block! }} 67890"_fmt(empty)) == "12345 67890"); +} + +TEST_CASE("partials") +{ + object const empty; + + // Basic Behavior + CHECK(to_string(R"("{{>text}}")"_fmt(empty, context{{"text", "from partial"_fmt}})) == R"("from partial")"); + + // Failed Lookup + CHECK(to_string(R"("{{>text}}")"_fmt(empty)) == R"("")"); + + // Context + CHECK(to_string(R"("{{>partial}}")"_fmt(object{{"text", "content"}}, context{{"partial", "*{{text}}*"_fmt}})) == R"("*content*")"); + + // Recursion + CHECK(to_string("{{>node}}"_fmt(object{ + {"content", "X"}, + {"nodes", array{object{{"content", "Y"}, {"nodes", array{}}}}} + }, context{{"node", "{{content}}<{{#nodes}}{{>node}}{{/nodes}}>"_fmt}})) == "X<Y<>>"); + + // Whitespace Sensitivity + { + // Surrounding Whitespace + CHECK(to_string("| {{>partial}} |"_fmt(empty, context{{"partial", "\t|\t"_fmt}})) == "| \t|\t |"); + + // Inline Indentation + CHECK(to_string(" {{data}} {{> partial}}\n"_fmt(object{{"data", "|"}}, context{{"partial", ">\n>"_fmt}})) == " | >\n>\n"); + + // Standalone Line Endings + CHECK(to_string("|\r\n{{>partial}}\r\n|"_fmt(empty, context{{"partial", ">"_fmt}})) == "|\r\n>|"); + + // Standalone Without Previous Line + CHECK(to_string(" {{>partial}}\n>"_fmt(empty, context{{"partial", ">\n>"_fmt}})) == " >\n >>"); + + // Standalone Without Newline + CHECK(to_string(">\n {{>partial}}"_fmt(empty, context{{"partial", ">\n>"_fmt}})) == ">\n >\n >"); + + // Standalone Indentation + CHECK(to_string( + "\\\n" + " {{>partial}}\n" + "/"_fmt(object{{"content", "<\n->"}}, + context{{"partial", + "|\n" + "{{{content}}}\n" + "|\n"_fmt}})) + == + "\\\n" + " |\n" + " <\n" + "->\n" + " |\n" + "/"); + } + + // Whitespace Insensitivity + { + // Padding Whitespace + CHECK(to_string("|{{> partial }}|"_fmt(empty, context{{"partial", "[]"_fmt}})) == "|[]|"); + } +} + +TEST_CASE("lambdas") +{ + // Interpolation + CHECK(to_string("Hello, {{lambda}}!"_fmt(object{{"lambda", [] { return "world"; }}})) == "Hello, world!"); + + // Interpolation - Expansion + CHECK(to_string( + "Hello, {{lambda}}!"_fmt(object{ + {"lambda", [] { return "{{planet}}"_fmt; }}, + {"planet", "world"}})) + == + "Hello, world!"); + + // Interpolation - Alternate Delimiters + CHECK(to_string( + "{{= | | =}}\nHello, (|&lambda|)!"_fmt(object{ + {"lambda", [] { return "|planet| => {{planet}}"_fmt; }}, + {"planet", "world"}})) + == + "Hello, (|planet| => world)!"); + + // Interpolation - Multiple Calls + CHECK(to_string( + "{{lambda}} == {{{lambda}}} == {{lambda}}"_fmt(object{ + {"lambda", [n = 0]() mutable { return ++n; }}})) + == + "1 == 2 == 3"); + + // Escaping + CHECK(to_string("<{{lambda}}{{{lambda}}}"_fmt(object{{"lambda", [] { return ">"; }}}, escape_html)) == "<>>"); + + // Section - Expansion + CHECK(to_string("<{{#lambda}}-{{/lambda}}>"_fmt(object{ + {"lambda", [](ast::content_list const& contents) { + ast::content_list list; + list.insert(list.end(), contents.begin(), contents.end()); + list.push_back(ast::variable{"planet"}); + list.insert(list.end(), contents.begin(), contents.end()); + return format(std::move(list), false); + }}, + {"planet", "Earth"}})) + == + "<-Earth->"); + + // Section - Multiple Calls + CHECK(to_string("{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}"_fmt(object{ + {"lambda", [](ast::content_list const& contents) { + ast::content_list list; + list.push_back(ast::text("__")); + list.insert(list.end(), contents.begin(), contents.end()); + list.push_back(ast::text("__")); + return format(std::move(list), false); + }}})) + == + "__FILE__ != __LINE__"); + + // Inverted Section + CHECK(to_string("<{{^lambda}}{{static}}{{/lambda}}>"_fmt(object{ + {"lambda", [](ast::content_list const&) { return false; }}, + {"static", "static"}})) + == + "<>"); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/d4f0bcfd/thirdparty/bustache/test/variant.cpp ---------------------------------------------------------------------- diff --git a/thirdparty/bustache/test/variant.cpp b/thirdparty/bustache/test/variant.cpp new file mode 100644 index 0000000..9e04858 --- /dev/null +++ b/thirdparty/bustache/test/variant.cpp @@ -0,0 +1,233 @@ +/*////////////////////////////////////////////////////////////////////////////// + Copyright (c) 2016-2017 Jamboree + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +//////////////////////////////////////////////////////////////////////////////*/ +#define CATCH_CONFIG_MAIN +#include <catch.hpp> +#include <bustache/detail/variant.hpp> + +struct BadCopyError {}; + +struct BadCopy +{ + BadCopy() = default; + + BadCopy(BadCopy&&) = default; + + BadCopy(BadCopy const&) + { + throw BadCopyError(); + } + + BadCopy& operator=(BadCopy const&) + { + throw BadCopyError(); + } + + BadCopy& operator=(BadCopy&&) = default; +}; + +struct A +{ + A() = default; + + A(A&& other) noexcept + { + other.moved = true; + } + + A(A const&) = default; + + A& operator=(A&& other) noexcept + { + assigned = true; + other.moved = true; + return *this; + } + + A& operator=(A const& other) noexcept + { + assigned = true; + return *this; + } + + bool assigned = false; + bool moved = false; +}; + +struct GoodInt +{ + operator int() const noexcept + { + return 0; + } +}; + +struct BadIntError {}; + +struct BadInt +{ + operator int() const + { + throw BadIntError(); + } +}; + +struct Visitor +{ + unsigned operator()(bool const&) const + { + return 0; + } + + unsigned operator()(int const&) const + { + return 1; + } + + unsigned operator()(A const&) const + { + return 2; + } + + unsigned operator()(BadCopy const&) const + { + return 3; + } +}; + +namespace bustache +{ +#define VAR(X, D) \ + X(0, bool, D) \ + X(1, int, D) \ + X(2, A, D) \ + X(3, BadCopy, D) \ +/***/ + class Var : public variant_base<Var> + { + VAR(Zz_BUSTACHE_VARIANT_MATCH,) + public: + Zz_BUSTACHE_VARIANT_DECL(Var, VAR, false) + + Var() noexcept : _which(0), _0() {} + }; +#undef VAR +} + +using namespace bustache; + +TEST_CASE("variant-ctor") +{ + { + Var v; + CHECK(v.valid()); + CHECK(v.which() == 0); + Var v2(v); + CHECK(v.which() == 0); + } + { + Var v(true); + CHECK(v.valid()); + CHECK(v.which() == 0); + Var v2(v); + CHECK(v.which() == 0); + } + { + Var v(0); + CHECK(v.valid()); + CHECK(v.which() == 1); + Var v2(v); + CHECK(v.which() == 1); + } + { + Var v(A{}); + CHECK(v.valid()); + CHECK(v.which() == 2); + Var v2(v); + CHECK(v.which() == 2); + } + { + Var v(BadCopy{}); + CHECK(v.valid()); + CHECK(v.which() == 3); + CHECK_THROWS_AS(Var{v}, BadCopyError); + } + { // Test convertible. + Var v(GoodInt{}); + CHECK(v.valid()); + CHECK(v.which() == 1); + } + { + Var v1(A{}); + CHECK(v1.which() == 2); + Var v2(std::move(v1)); + CHECK(v1.which() == 2); + CHECK(v2.which() == 2); + CHECK(get<A>(v1).moved == true); + } +} + +TEST_CASE("variant-access") +{ + Var v; + CHECK(v.which() == 0); + CHECK(get<bool>(&v) != nullptr); + CHECK(get<bool>(v) == false); + CHECK(get<int>(&v) == nullptr); + CHECK_THROWS_AS(get<int>(v), bad_variant_access); + v = 1024; + CHECK(v.which() == 1); + CHECK(get<int>(&v) != nullptr); + CHECK(get<int>(v) == 1024); + get<int>(v) = true; + CHECK(v.which() == 1); + CHECK(get<int>(v) == 1); + v = true; + CHECK(v.which() == 0); + CHECK(get<bool>(v) == true); + CHECK_THROWS_AS(get<A>(v), bad_variant_access); + { + REQUIRE(v.which() != 2); + auto& a = v = A(); + CHECK(v.which() == 2); + CHECK(get<A>(&v) != nullptr); + CHECK(get<A>(&v) == &a); + CHECK(!a.assigned); + } + { + REQUIRE(v.which() == 2); + auto& b = v = A(); + CHECK(v.which() == 2); + CHECK(get<A>(&v) == &b); + CHECK(b.assigned); + } +} + +TEST_CASE("variant-valuess-by-exception") +{ + Var v; + CHECK(v.valid()); + CHECK_THROWS_AS(v = BadInt(), BadIntError); + CHECK(v.which() != 0); + CHECK(!v.valid()); + v = 42; + CHECK(v.valid()); + + Var v2(BadCopy{}); + CHECK_THROWS_AS(v = v2, BadCopyError); + CHECK(!v.valid()); + CHECK(v2.which() == 3); +} + +TEST_CASE("variant-visit") +{ + Visitor v; + CHECK(visit(v, Var{}) == 0); + CHECK(visit(v, Var{true}) == 0); + CHECK(visit(v, Var{0}) == 1); + CHECK(visit(v, Var{A{}}) == 2); + CHECK(visit(v, Var{BadCopy{}}) == 3); +} \ No newline at end of file