diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aabcff5..dab2e825 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,9 +19,10 @@ set_property(CACHE JINJA2CPP_WITH_SANITIZERS PROPERTY STRINGS ${JINJA2CPP_SANITI set(JINJA2CPP_SUPPORTED_REGEX std boost) set(JINJA2CPP_USE_REGEX boost CACHE STRING "Use regex parser in lexer, boost works faster on most platforms") set_property(CACHE JINJA2CPP_USE_REGEX PROPERTY STRINGS ${JINJA2CPP_SUPPORTED_REGEX}) -set(JINJA2CPP_WITH_JSON_BINDINGS boost nlohmann rapid all none) +message("aa ${JINJA2CPP_USE_REGEX}") +set(JINJA2CPP_WITH_JSON_SUPPORTED_BINDINGS boost nlohmann rapid all none) set(JINJA2CPP_WITH_JSON_BINDINGS boost CACHE STRING "Build with json support(boost|rapid)") -set_property(CACHE JINJA2CPP_WITH_JSON_BINDINGS PROPERTY STRINGS ${JINJA2CPP_WITH_JSON_BINDINGS}) +set_property(CACHE JINJA2CPP_WITH_JSON_BINDINGS PROPERTY STRINGS ${JINJA2CPP_WITH_JSON_SUPPORTED_BINDINGS}) set (JINJA2CPP_DEPS_MODE "internal" CACHE STRING "Jinja2Cpp dependency management mode (internal | external | external-boost | conan-build). See documentation for details. 'interal' is default.") option(JINJA2CPP_BUILD_TESTS "Build Jinja2Cpp unit tests" ${JINJA2CPP_IS_MAIN_PROJECT}) option(JINJA2CPP_STRICT_WARNINGS "Enable additional warnings and treat them as errors" ON) @@ -144,15 +145,38 @@ include(collect_sources) set (LIB_TARGET_NAME jinja2cpp) -CollectSources(Sources Headers ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(JINJA2CPP_SOURCES + src/error_info.cpp + src/expression_evaluator.cpp + src/expression_parser.cpp + src/filesystem_handler.cpp + src/filters.cpp + src/generic_list.cpp + src/internal_value.cpp + src/lexer.cpp + src/serialize_filters.cpp + src/statements.cpp + src/string_converter_filter.cpp + src/template.cpp + src/template_env.cpp + src/template_parser.cpp + src/testers.cpp + src/value.cpp +) + +#CollectSources(Sources Headers ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) CollectSources(PublicSources PublicHeaders ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include) add_library(${LIB_TARGET_NAME} ${LIB_LINK_TYPE} - ${Sources} - ${Headers} - ${PublicHeaders} + ${JINJA2CPP_SOURCES} ) +# add_library(${LIB_TARGET_NAME} ${LIB_LINK_TYPE} +# ${Sources} +# ${Headers} +# ${PublicHeaders} +# ) + target_sources(${LIB_TARGET_NAME} PUBLIC FILE_SET HEADERS FILES ${PublicHeaders} @@ -218,10 +242,27 @@ endif () if ("${JINJA2CPP_USE_REGEX}" STREQUAL "boost") set(_regex_define "-DJINJA2CPP_USE_REGEX_BOOST") endif() + +message(STATUS "!!! ${JINJA2CPP_WITH_JSON_BINDINGS}") if ("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "boost") set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_BOOST") + target_sources(${LIB_TARGET_NAME} + PRIVATE + src/binding/boost_json_serializer.cpp + ) elseif("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "rapid") set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_RAPID") + target_sources(${LIB_TARGET_NAME} + PRIVATE + src/binding/rapid_json_serializer.cpp + ) +else() + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_NLOHMANN") + target_sources(${LIB_TARGET_NAME} + PRIVATE + src/binding/nlohmann_json_serializer.cpp + ) + #message(FATAL_ERROR "bbb") endif() target_compile_definitions(${LIB_TARGET_NAME} PUBLIC @@ -251,8 +292,35 @@ if (JINJA2CPP_BUILD_TESTS) enable_testing() CollectSources(TestSources TestHeaders ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/test) - add_executable(jinja2cpp_tests ${TestSources} ${TestHeaders}) - target_link_libraries(jinja2cpp_tests gtest gtest_main + set(JINJA2CPP_TEST_SOURCES + test/basic_tests.cpp + test/binding + test/errors_test.cpp + test/expressions_test.cpp + test/extends_test.cpp + test/filesystem_handler_test.cpp + test/filters_test.cpp + test/forloop_test.cpp + test/helpers_tests.cpp + test/if_test.cpp + test/import_test.cpp + test/includes_test.cpp + test/macro_test.cpp + test/metadata_test.cpp + test/perf_test.cpp + test/statements_tets.cpp + test/test_data + test/test_tools.h + test/testers_test.cpp + test/tojson_filter_test.cpp + test/user_callable_test.cpp + ) +#test/binding/boost_json_binding_test.cpp +#test/binding/nlohmann_json_binding_test.cpp +#test/binding/rapid_json_binding_test.cpp +#test/binding/rapid_json_serializer_test.cpp + add_executable(jinja2cpp_tests ${JINJA2CPP_TEST_SOURCES}) + target_link_libraries(jinja2cpp_tests gtest gmock gtest_main nlohmann_json::nlohmann_json ${LIB_TARGET_NAME} ${EXTRA_TEST_LIBS} ${JINJA2CPP_PRIVATE_LIBS}) set_target_properties(jinja2cpp_tests PROPERTIES @@ -277,6 +345,39 @@ if (JINJA2CPP_BUILD_TESTS) add_dependencies(jinja2cpp_tests CopyTestData) add_test(NAME jinja2cpp_tests COMMAND jinja2cpp_tests) + + if ("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "boost") + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_BOOST") + set(_bindings_test_source test/binding/boost_json_binding_test.cpp) + + target_sources(jinja2cpp_tests + PRIVATE + src/binding/boost_json_serializer.cpp + ) + endif() + if("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "rapid") + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_RAPID") + target_sources(jinja2cpp_tests + PRIVATE + test/binding/rapid_json_binding_test.cpp + test/binding/rapid_json_serializer_test.cpp + ) + endif() + if("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "nlohmann") + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_RAPID") + target_sources(jinja2cpp_tests + PRIVATE + test/binding/nlohmann_json_binding_test.cpp + ) + endif() + target_compile_definitions(jinja2cpp_tests + PUBLIC + -DBOOST_SYSTEM_NO_DEPRECATED + -DBOOST_ERROR_CODE_HEADER_ONLY + ${_regex_define} + ${_bindings_define} + ) + endif () set (JINJA2CPP_INSTALL_CONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/${LIB_TARGET_NAME}") @@ -353,4 +454,4 @@ if(JINJA2CPP_INSTALL) DESTINATION ${JINJA2CPP_INSTALL_CONFIG_DIR} ) -endif() \ No newline at end of file +endif() diff --git a/src/binding/boost_json_parser.h b/src/binding/boost_json_parser.h new file mode 100644 index 00000000..ab10d3a9 --- /dev/null +++ b/src/binding/boost_json_parser.h @@ -0,0 +1,27 @@ +#ifndef JINJA2CPP_SRC_BOOST_JSON_PARSER_H +#define JINJA2CPP_SRC_BOOST_JSON_PARSER_H + +#include + +#include +#include + +namespace jinja2 +{ + +template +nonstd::expected Parse(nonstd::basic_string_view json, boost::anys::unique_any& metadataJson) +{ + (void)json; + boost::system::error_code ec; + auto value = boost::json::parse({json.data(), json.size()}, ec); + if (ec) + { + return nonstd::make_unexpected(ec.what()); + } + return Reflect(value); +} + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_BOOST_JSON_PARSER_H diff --git a/src/binding/boost_json_serializer.cpp b/src/binding/boost_json_serializer.cpp index 7dbabbbe..85ec41ca 100644 --- a/src/binding/boost_json_serializer.cpp +++ b/src/binding/boost_json_serializer.cpp @@ -1,9 +1,14 @@ #include "boost_json_serializer.h" #include "../value_visitors.h" -#include + #include +#include +#include +#include + + template <> struct fmt::formatter : ostream_formatter {}; namespace jinja2 @@ -140,18 +145,29 @@ void PrettyPrint(fmt::basic_memory_buffer& os, const boost::json::value& j case boost::json::kind::array: { fmt::format_to(std::back_inserter(os), "["); + bool singleLineArray = false; auto const& arr = jv.get_array(); if (!arr.empty()) { - auto it = arr.begin(); + if (!singleLineArray && indent != 0) + fmt::format_to(std::back_inserter(os), "\n"); + auto it = arr.begin(); for (;;) { + fmt::format_to(std::back_inserter(os), "{: >{}}", "", (indent * (level + 1))); PrettyPrint(os, *it, indent, level + 1); if (++it == arr.end()) break; - fmt::format_to(std::back_inserter(os), "{: <{}}", ",", (indent == 0) ? 0 : 2); + fmt::format_to(std::back_inserter(os), "{: <{}}", ",", (indent == 0) ? 0 : 1); + if (!singleLineArray && indent != 0) + fmt::format_to(std::back_inserter(os), "\n"); } } + if (!singleLineArray && indent != 0) + { + fmt::format_to(std::back_inserter(os), "\n"); + fmt::format_to(std::back_inserter(os), "{: >{}}", "", (indent * level) + 1); + } fmt::format_to(std::back_inserter(os), "]"); break; } @@ -191,4 +207,35 @@ std::string ValueWrapper::AsString(const uint8_t indent) const } } // namespace boost_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent) +{ + using namespace std::literals; + boost_json_serializer::DocumentWrapper jsonDoc; + const auto jsonValue = jsonDoc.CreateValue(value); + const auto jsonString = jsonValue.AsString(static_cast(indent)); + const auto result = std::accumulate(jsonString.begin(), jsonString.end(), ""s, [](const auto &str, const auto &c) + { + switch (c) + { + case '<': + return str + "\\u003c"; + break; + case '>': + return str +"\\u003e"; + break; + case '&': + return str +"\\u0026"; + break; + case '\'': + return str +"\\u0027"; + break; + default: + return str + c; + break; + } + }); + return result; +} + } // namespace jinja2 diff --git a/src/binding/boost_json_serializer.h b/src/binding/boost_json_serializer.h index 162333e4..934a2f84 100644 --- a/src/binding/boost_json_serializer.h +++ b/src/binding/boost_json_serializer.h @@ -41,7 +41,12 @@ class DocumentWrapper private: }; +using DocumentWrapper = jinja2::boost_json_serializer::DocumentWrapper; + } // namespace boost_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent); + } // namespace jinja2 #endif // JINJA2CPP_SRC_BOOST_JSON_SERIALIZER_H diff --git a/src/binding/nlohmann_json_parser.h b/src/binding/nlohmann_json_parser.h new file mode 100644 index 00000000..e9d849fc --- /dev/null +++ b/src/binding/nlohmann_json_parser.h @@ -0,0 +1,26 @@ +#ifndef JINJA2CPP_SRC_NLOHMANN_JSON_PARSER_H +#define JINJA2CPP_SRC_NLOHMANN_JSON_PARSER_H + +#include + +#include +#include + +namespace jinja2 +{ + +template +nonstd::expected Parse(nonstd::basic_string_view json, boost::anys::unique_any& metadataJson) +{ + (void)json; + auto value = nlohmann::json::parse({json.data(), json.size()}); + if (ec) + { + return nonstd::make_unexpected(ec.what()); + } + return Reflect(value); +} + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_NLOHMANN_JSON_PARSER_H diff --git a/src/binding/nlohmann_json_serializer.cpp b/src/binding/nlohmann_json_serializer.cpp new file mode 100644 index 00000000..db2b1000 --- /dev/null +++ b/src/binding/nlohmann_json_serializer.cpp @@ -0,0 +1,232 @@ +#include "nlohmann_json_serializer.h" + +#include "../value_visitors.h" + +#include + +#include +#include +#include + + +//template <> struct fmt::formatter : ostream_formatter {}; + +namespace jinja2 +{ +namespace nlohmann_json_serializer +{ +namespace +{ +struct JsonInserter : visitors::BaseVisitor +{ + using BaseVisitor::operator(); + + explicit JsonInserter() {} + + nlohmann::json operator()(const ListAdapter& list) const + { + nlohmann::json listValue; //(nlohmann::json::kind::array); + + for (auto& v : list) + { + listValue.push_back(Apply(v)); + } + return listValue; + } + + nlohmann::json operator()(const MapAdapter& map) const + { + nlohmann::json mapNode; //(nlohmann::json::kind::object); + + const auto& keys = map.GetKeys(); + for (auto& k : keys) + { + mapNode.emplace(k.c_str(), Apply(map.GetValueByName(k))); + } + + return mapNode; + } + + nlohmann::json operator()(const KeyValuePair& kwPair) const + { + nlohmann::json pairNode; //(nlohmann::json::kind::object); + pairNode.emplace(kwPair.key.c_str(), Apply(kwPair.value)); + + return pairNode; + } + + nlohmann::json operator()(const std::string& str) const { return nlohmann::json(str.c_str()); } + + nlohmann::json operator()(const nonstd::string_view& str) const + { + return nlohmann::json(std::string{str.data(), str.size()}); + } + + nlohmann::json operator()(const std::wstring& str) const + { + auto s = ConvertString(str); + return nlohmann::json({s.c_str()}); + } + + nlohmann::json operator()(const nonstd::wstring_view& str) const + { + auto s = ConvertString(str); + return nlohmann::json(s.c_str()); + } + + nlohmann::json operator()(bool val) const { return nlohmann::json(val); } + + nlohmann::json operator()(EmptyValue) const { return nlohmann::json(); } + + nlohmann::json operator()(const Callable&) const { return nlohmann::json(""); } + + nlohmann::json operator()(double val) const { return nlohmann::json(val); } + + nlohmann::json operator()(int64_t val) const { return nlohmann::json(val); } + +}; +} // namespace +/* +DocumentWrapper::DocumentWrapper() +{ +} + +ValueWrapper DocumentWrapper::CreateValue(const InternalValue& value) const +{ + auto v = Apply(value); + return ValueWrapper(std::move(v)); +} + +ValueWrapper::ValueWrapper(nlohmann::json::value&& value) + : m_value(std::move(value)) +{ +} + +void PrettyPrint(fmt::basic_memory_buffer& os, const nlohmann::json::value& jv, uint8_t indent = 4, int level = 0) +{ + switch (jv.kind()) + { + case nlohmann::json::kind::object: + { + fmt::format_to(std::back_inserter(os), "{}", '{'); + if (indent != 0) + { + fmt::format_to(std::back_inserter(os), "{}", "\n"); + } + const auto& obj = jv.get_object(); + if (!obj.empty()) + { + auto it = obj.begin(); + for (;;) + { + auto key = nlohmann::json::serialize(it->key()); + fmt::format_to( + std::back_inserter(os), + "{: >{}}{: <{}}", + key, + key.size() + indent * (level + 1), + ":", + (indent == 0) ? 0 : 2 + ); + PrettyPrint(os, it->value(), indent, level + 1); + if (++it == obj.end()) + break; + fmt::format_to(std::back_inserter(os), "{: <{}}", ",", (indent == 0) ? 0 : 2); + } + } + if (indent != 0) + { + fmt::format_to(std::back_inserter(os), "{}", "\n"); + } + fmt::format_to(std::back_inserter(os), "{: >{}}", "}", (indent * level) + 1); + break; + } + + case nlohmann::json::kind::array: + { + fmt::format_to(std::back_inserter(os), "["); + auto const& arr = jv.get_array(); + if (!arr.empty()) + { + auto it = arr.begin(); + for (;;) + { + PrettyPrint(os, *it, indent, level + 1); + if (++it == arr.end()) + break; + fmt::format_to(std::back_inserter(os), "{: <{}}", ",", (indent == 0) ? 0 : 2); + } + } + fmt::format_to(std::back_inserter(os), "]"); + break; + } + + case nlohmann::json::kind::string: + { + fmt::format_to(std::back_inserter(os), "{}", nlohmann::json::serialize(jv.get_string())); + break; + } + + case nlohmann::json::kind::uint64: + case nlohmann::json::kind::int64: + case nlohmann::json::kind::double_: + { + fmt::format_to(std::back_inserter(os), "{}", jv); + break; + } + case nlohmann::json::kind::bool_: + { + fmt::format_to(std::back_inserter(os), "{}", jv.get_bool()); + break; + } + + case nlohmann::json::kind::null: + { + fmt::format_to(std::back_inserter(os), "null"); + break; + } + } +} + +std::string ValueWrapper::AsString(const uint8_t indent) const +{ + fmt::memory_buffer out; + PrettyPrint(out, m_value, indent); + return fmt::to_string(out); +} +*/ +} // namespace nlohmann_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent) +{ + using namespace std::literals; + //boost_json_serializer::DocumentWrapper jsonDoc; + auto jsonValue = Apply(value); + auto jsonString = jsonValue.dump(indent == 0 ? -1 : indent); + //const auto jsonValue = jsonDoc.CreateValue(value); + //const auto jsonString = jsonValue.AsString(static_cast(indent)); + const auto result = std::accumulate(jsonString.begin(), jsonString.end(), ""s, [](const auto &str, const auto &c) + { + switch (c) + { + case '<': + return str + "\\u003c"; + break; + case '>': + return str +"\\u003e"; + break; + case '&': + return str +"\\u0026"; + break; + case '\'': + return str +"\\u0027"; + break; + default: + return str + c; + break; + } + }); + return result; +} + +} // namespace jinja2 diff --git a/src/binding/nlohmann_json_serializer.h b/src/binding/nlohmann_json_serializer.h new file mode 100644 index 00000000..77af0d8c --- /dev/null +++ b/src/binding/nlohmann_json_serializer.h @@ -0,0 +1,52 @@ +#ifndef JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H +#define JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H + +#include "../internal_value.h" + +#include + +#include + +namespace jinja2 +{ +namespace boost_json_serializer +{ +/* +class ValueWrapper +{ + friend class DocumentWrapper; + +public: + ValueWrapper(ValueWrapper&&) = default; + ValueWrapper& operator=(ValueWrapper&&) = default; + + std::string AsString(uint8_t indent = 0) const; + +private: + ValueWrapper(boost::json::value&& value); + + boost::json::value m_value; +}; + +class DocumentWrapper +{ +public: + DocumentWrapper(); + + DocumentWrapper(DocumentWrapper&&) = default; + DocumentWrapper& operator=(DocumentWrapper&&) = default; + + ValueWrapper CreateValue(const InternalValue& value) const; + +private: +}; + +using DocumentWrapper = jinja2::boost_json_serializer::DocumentWrapper; + */ +} // namespace boost_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent); + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H diff --git a/src/binding/rapid_json_parser.h b/src/binding/rapid_json_parser.h new file mode 100644 index 00000000..d28e7512 --- /dev/null +++ b/src/binding/rapid_json_parser.h @@ -0,0 +1,80 @@ +#ifndef JINJA2CPP_SRC_RAPID_JSON_PARSER_H +#define JINJA2CPP_SRC_RAPID_JSON_PARSER_H + +#include +#include +#include + +#include + +#include +#include + + +namespace jinja2 +{ + +namespace detail +{ +template +struct RapidJsonEncodingType; + +template<> +struct RapidJsonEncodingType<1> +{ + using type = rapidjson::UTF8; +}; + +#ifdef BOOST_ENDIAN_BIG_BYTE +template<> +struct RapidJsonEncodingType<2> +{ + using type = rapidjson::UTF16BE; +}; + +template<> +struct RapidJsonEncodingType<4> +{ + using type = rapidjson::UTF32BE; +}; +#else +template<> +struct RapidJsonEncodingType<2> +{ + using type = rapidjson::UTF16LE; +}; + +template<> +struct RapidJsonEncodingType<4> +{ + using type = rapidjson::UTF32LE; +}; +#endif +} // namespace detail + +template +nonstd::expected Parse(nonstd::basic_string_view json, boost::anys::unique_any& metadataJson) +{ + using JsonDocumentType = rapidjson::GenericDocument::type>; + metadataJson.emplace(); +// metadataJson = boost::anys::unique_any(boost::anys::in_place_type_t); +// metadataJson = {std::make_unique()}; + // metadataJson = boost::any(std::move(rapidJson)); + auto& jsonDoc = boost::any_cast(metadataJson); + rapidjson::ParseResult res = jsonDoc.Parse(json.data(), json.size()); +//__assert("a", __FILE__, __LINE__); +// if (res.IsError()) +// __assert("a", __FILE__, __LINE__); + if (!res) + { + std::string jsonError = rapidjson::GetParseError_En(res.Code()); + return nonstd::make_unexpected(jsonError); + } +// metadataJson = json; + std::cout<< "!!!!" << jsonDoc.MemberCount() << std::endl; + return Reflect(jsonDoc); +} + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_RAPID_JSON_PARSER_H diff --git a/src/binding/rapid_json_serializer.cpp b/src/binding/rapid_json_serializer.cpp index bd7e5a32..09ad993f 100644 --- a/src/binding/rapid_json_serializer.cpp +++ b/src/binding/rapid_json_serializer.cpp @@ -4,6 +4,8 @@ #include +#include + namespace jinja2 { namespace rapidjson_serializer @@ -116,12 +118,46 @@ std::string ValueWrapper::AsString(const uint8_t indent) const { PrettyWriter writer(buffer); writer.SetIndent(' ', indent); - writer.SetFormatOptions(rapidjson::kFormatSingleLineArray); + //writer.SetFormatOptions(rapidjson::kFormatSingleLineArray); m_value.Accept(writer); } return buffer.GetString(); } + } // namespace rapidjson_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent) +{ + using namespace std::string_literals; + + rapidjson_serializer::DocumentWrapper jsonDoc; + const auto jsonValue = jsonDoc.CreateValue(value); + const auto jsonString = jsonValue.AsString(static_cast(indent)); + const auto result = std::accumulate(jsonString.begin(), jsonString.end(), ""s, [](const auto &str, const auto &c) + { + switch (c) + { + case '<': + return str + "\\u003c"; + break; + case '>': + return str +"\\u003e"; + break; + case '&': + return str +"\\u0026"; + break; + case '\'': + return str +"\\u0027"; + break; + default: + return str + c; + break; + } + }); + std::cout << jsonString < m_document; }; +using DocumentWrapper = jinja2::rapidjson_serializer::DocumentWrapper; + + } // namespace rapidjson_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent); + } // namespace jinja2 #endif // JINJA2CPP_SRC_RAPID_JSON_SERIALIZER_H diff --git a/src/filters.cpp b/src/filters.cpp index 3ab9f338..3700a600 100644 --- a/src/filters.cpp +++ b/src/filters.cpp @@ -1,6 +1,5 @@ #include "filters.h" -#include "binding/rapid_json_serializer.h" #include "generic_adapters.h" #include "out_stream.h" #include "testers.h" diff --git a/src/serialize_filters.cpp b/src/serialize_filters.cpp index e467fd8c..5b4cd5fc 100644 --- a/src/serialize_filters.cpp +++ b/src/serialize_filters.cpp @@ -15,10 +15,8 @@ #ifdef JINJA2CPP_WITH_JSON_BINDINGS_BOOST #include "binding/boost_json_serializer.h" -using DocumentWrapper = jinja2::boost_json_serializer::DocumentWrapper; #else #include "binding/rapid_json_serializer.h" -using DocumentWrapper = jinja2::rapidjson_serializer::DocumentWrapper; #endif @@ -149,32 +147,8 @@ InternalValue Serialize::Filter(const InternalValue& value, RenderContext& conte if (m_mode == JsonMode) { const auto indent = ConvertToInt(this->GetArgumentValue("indent", context)); - DocumentWrapper jsonDoc; - const auto jsonValue = jsonDoc.CreateValue(value); - const auto jsonString = jsonValue.AsString(static_cast(indent)); - const auto result = std::accumulate(jsonString.begin(), jsonString.end(), ""s, [](const auto &str, const auto &c) - { - switch (c) - { - case '<': - return str + "\\u003c"; - break; - case '>': - return str +"\\u003e"; - break; - case '&': - return str +"\\u0026"; - break; - case '\'': - return str +"\\u0027"; - break; - default: - return str + c; - break; - } - }); - return result; + return ToJson(value, indent); } return InternalValue(); diff --git a/src/template_impl.h b/src/template_impl.h index 8f4ad3a6..9ac43d7d 100644 --- a/src/template_impl.h +++ b/src/template_impl.h @@ -2,59 +2,31 @@ #define JINJA2CPP_SRC_TEMPLATE_IMPL_H #include "internal_value.h" -#include "jinja2cpp/binding/rapid_json.h" #include "jinja2cpp/template_env.h" #include "jinja2cpp/value.h" #include "renderer.h" #include "template_parser.h" #include "value_visitors.h" +#ifdef JINJA2CPP_WITH_JSON_BINDINGS_BOOST +#include "binding/boost_json_parser.h" +#include "binding/boost_json_serializer.h" +#include "jinja2cpp/binding/boost_json.h" +#else +#include "binding/rapid_json_parser.h" +#include "jinja2cpp/binding/rapid_json.h" +#endif + +#include +#include #include #include #include -#include #include namespace jinja2 { -namespace detail -{ -template -struct RapidJsonEncodingType; - -template<> -struct RapidJsonEncodingType<1> -{ - using type = rapidjson::UTF8; -}; - -#ifdef BOOST_ENDIAN_BIG_BYTE -template<> -struct RapidJsonEncodingType<2> -{ - using type = rapidjson::UTF16BE; -}; - -template<> -struct RapidJsonEncodingType<4> -{ - using type = rapidjson::UTF32BE; -}; -#else -template<> -struct RapidJsonEncodingType<2> -{ - using type = rapidjson::UTF16LE; -}; - -template<> -struct RapidJsonEncodingType<4> -{ - using type = rapidjson::UTF32LE; -}; -#endif -} // namespace detail extern void SetupGlobals(InternalValueMap& globalParams); @@ -64,7 +36,6 @@ class ITemplateImpl virtual ~ITemplateImpl() = default; }; - template struct TemplateLoader; @@ -350,6 +321,17 @@ class TemplateImpl : public ITemplateImpl if (m_metadataInfo.metadataType == "json") { + auto result = Parse(metadataString, m_metadataJson); + //throw; + if (!result) + { + typename ErrorInfoTpl::Data errorData; + errorData.code = ErrorCode::MetadataParseError; + errorData.srcLoc = m_metadataInfo.location; + errorData.extraParams.push_back(Value(result.error())); + return nonstd::make_unexpected(ErrorInfoTpl(errorData)); + } +/* m_metadataJson = JsonDocumentType(); rapidjson::ParseResult res = m_metadataJson.value().Parse(metadataString.data(), metadataString.size()); if (!res) @@ -361,7 +343,8 @@ class TemplateImpl : public ITemplateImpl errorData.extraParams.push_back(Value(std::move(jsonError))); return nonstd::make_unexpected(ErrorInfoTpl(errorData)); } - m_metadata = std::move(nonstd::get(Reflect(m_metadataJson.value()).data())); +*/ + m_metadata = std::move(nonstd::get(result.value().data())); return m_metadata.value(); } return GenericMap(); @@ -384,8 +367,8 @@ class TemplateImpl : public ITemplateImpl return false; if (m_metadata != other.m_metadata) return false; - if (m_metadataJson != other.m_metadataJson) - return false; +// if (m_metadataJson != other.m_metadataJson) +// return false; if (m_metadataInfo != other.m_metadataInfo) return false; return true; @@ -467,10 +450,10 @@ class TemplateImpl : public ITemplateImpl } private: - ThisType* m_host; + ThisType* m_host{}; }; private: - using JsonDocumentType = rapidjson::GenericDocument::type>; +// using JsonDocumentType = rapidjson::GenericDocument::type/>; TemplateEnv* m_env{}; Settings m_settings; @@ -478,7 +461,7 @@ class TemplateImpl : public ITemplateImpl std::string m_templateName; RendererPtr m_renderer; mutable nonstd::optional m_metadata; - mutable nonstd::optional m_metadataJson; + mutable boost::anys::unique_any m_metadataJson; MetadataInfo m_metadataInfo; }; diff --git a/test/metadata_test.cpp b/test/metadata_test.cpp index 9bc0779e..5694e51e 100644 --- a/test/metadata_test.cpp +++ b/test/metadata_test.cpp @@ -66,7 +66,7 @@ TEST(MetadataTest, Metadata_Invalid) EXPECT_TRUE(!metadataValue); auto error = metadataValue.error().ToString(); std::cout << error << std::endl; - EXPECT_EQ("noname.j2tpl:2:1: error: Error occurred during template metadata parsing. Error: Invalid value.\n", error); + EXPECT_THAT(error, testing::ContainsRegex("Error occurred during template metadata parsing. Error:")); } TEST(MetadataTest, Metadata_JsonData_Narrow) diff --git a/test/test_tools.h b/test/test_tools.h index 930604a0..8804ef51 100644 --- a/test/test_tools.h +++ b/test/test_tools.h @@ -3,13 +3,15 @@ #include "../src/helpers.h" -#include #include #include #include #include #include +#include +#include + struct InputOutputPair { std::string tpl; diff --git a/test/tojson_filter_test.cpp b/test/tojson_filter_test.cpp index ba8a920f..2dfaaf5f 100644 --- a/test/tojson_filter_test.cpp +++ b/test/tojson_filter_test.cpp @@ -10,7 +10,7 @@ SUBSTITUTION_TEST_P(JsonFilterSubstitutionTest) INSTANTIATE_TEST_SUITE_P(ToJson, JsonFilterSubstitutionTest, ::testing::Values(InputOutputPair{ "(1, 2, 3) | tojson", "[1,2,3]" }, - InputOutputPair{ "(1, 2, 3) | tojson(indent = 1)", "[1, 2, 3]" }, + InputOutputPair{ "(1, 2, 3) | tojson(indent = 1)", "[\n 1,\n 2,\n 3\n]" }, InputOutputPair{ "'\"ba&r\\'' | tojson", "\"\\\"ba\\u0026r\\u0027\"" }, InputOutputPair{ "'' | tojson", "\"\\u003cbar\\u003e\"" })); @@ -104,7 +104,11 @@ TEST_F(ToJsonIndentationTest, SerializeObjectWithIndent) const auto expectedResult = R"({ "map": { - "array": [1, 2, 3] + "array": [ + 1, + 2, + 3 + ] } })"; diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 57c8f8ee..efaa7566 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -50,6 +50,7 @@ if(JINJA2CPP_BUILD_TESTS) if(gtest_FOUND) imported_target_alias(gtest ALIAS gtest::gtest) + imported_target_alias(gmock ALIAS gmock::gmock) else() message(STATUS "gtest not found, building own one") if(MSVC) @@ -70,8 +71,17 @@ endif() if (NOT DEFINED JINJA2_PRIVATE_LIBS_INT) set(JINJA2CPP_PRIVATE_LIBS ${JINJA2CPP_PRIVATE_LIBS} - Boost::variant Boost::filesystem Boost::algorithm Boost::lexical_cast Boost::json - Boost::regex Boost::numeric_conversion fmt RapidJson) + Boost::algorithm + Boost::any + Boost::filesystem + Boost::json + Boost::lexical_cast + Boost::numeric_conversion + Boost::regex + Boost::variant + fmt + RapidJson + nlohmann_json::nlohmann_json) else () set (JINJA2CPP_PRIVATE_LIBS ${JINJA2_PRIVATE_LIBS_INT}) endif () diff --git a/thirdparty/external_boost_deps.cmake b/thirdparty/external_boost_deps.cmake index e6305d27..1a065113 100644 --- a/thirdparty/external_boost_deps.cmake +++ b/thirdparty/external_boost_deps.cmake @@ -20,6 +20,9 @@ if (MSVC) endif () find_package(boost_algorithm ${FIND_BOOST_PACKAGE_QUIET}) +find_package(boost_any ${FIND_BOOST_PACKAGE_QUIET}) +find_package(boost_assert ${FIND_BOOST_PACKAGE_QUIET}) +find_package(boost_atomic ${FIND_BOOST_PACKAGE_QUIET}) find_package(boost_filesystem ${FIND_BOOST_PACKAGE_QUIET}) find_package(boost_numeric_conversion ${FIND_BOOST_PACKAGE_QUIET}) find_package(boost_json ${FIND_BOOST_PACKAGE_QUIET}) @@ -29,12 +32,14 @@ find_package(boost_regex ${FIND_BOOST_PACKAGE_QUIET}) find_package(boost_lexical_cast ${FIND_BOOST_PACKAGE_QUIET}) if (boost_algorithm_FOUND AND + boost_any_FOUND AND boost_filesystem_FOUND AND boost_numeric_conversion_FOUND AND boost_json_FOUND AND boost_optional_FOUND AND boost_variant_FOUND AND boost_regex_FOUND) imported_target_alias(boost_algorithm ALIAS boost_algorithm::boost_algorithm) + imported_target_alias(boost_any ALIAS boost_any::boost_any) imported_target_alias(boost_filesystem ALIAS boost_filesystem::boost_filesystem) imported_target_alias(boost_numeric_conversion ALIAS numeric_conversion::numeric_conversion) imported_target_alias(boost_json ALIAS boost_json::boost_json) @@ -42,12 +47,25 @@ if (boost_algorithm_FOUND AND imported_target_alias(boost_variant ALIAS boost_variant::boost_variant) imported_target_alias(boost_regex ALIAS boost_regex::boost_regex) imported_target_alias(boost_lexical_cast ALIAS boost_regex::lexical_cast) - + else () - find_package(Boost COMPONENTS system filesystem numeric_conversion json regex optional variant algorithm lexical_cast ${FIND_BOOST_PACKAGE_QUIET} REQUIRED) + find_package(Boost + COMPONENTS + algorithm + any + filesystem + json + lexical_cast + numeric_conversion + optional + regex + system + variant + ${FIND_BOOST_PACKAGE_QUIET} REQUIRED) if (Boost_FOUND) imported_target_alias(boost_algorithm ALIAS Boost::boost) + imported_target_alias(boost_any ALIAS Boost::boost_any) imported_target_alias(boost_filesystem ALIAS Boost::filesystem) imported_target_alias(boost_numeric_conversion ALIAS Boost::numeric_conversion) imported_target_alias(boost_json ALIAS Boost::json) @@ -64,7 +82,7 @@ if ("${JINJA2CPP_USE_REGEX}" STREQUAL "boost") endif() if(JINJA2CPP_INSTALL) - install(TARGETS boost_algorithm boost_filesystem boost_numeric_conversion boost_json boost_optional boost_variant ${_additional_boost_install_targets} + install(TARGETS boost_algorithm boost_any boost_filesystem boost_numeric_conversion boost_json boost_optional boost_variant ${_additional_boost_install_targets} EXPORT InstallTargets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/thirdparty/thirdparty-conan-build.cmake b/thirdparty/thirdparty-conan-build.cmake index e1bc3a4f..e649fe32 100644 --- a/thirdparty/thirdparty-conan-build.cmake +++ b/thirdparty/thirdparty-conan-build.cmake @@ -4,12 +4,38 @@ find_package(expected-lite REQUIRED) find_package(variant-lite REQUIRED) find_package(optional-lite REQUIRED) find_package(string-view-lite REQUIRED) -find_package(nlohmann_json REQUIRED) -find_package(Boost COMPONENTS algorithm filesystem numeric_conversion json optional variant regex REQUIRED) find_package(fmt REQUIRED) -find_package(RapidJSON REQUIRED) + +set(_test_dependencies) +if (JINJA2CPP_BUILD_TESTS) + find_package(nlohmann_json REQUIRED) + set(_test_dependencies "nlohmann_json::nlohmann_json") +endif() + +set(_bindings_json) +set(_bindings_find_package_boost) +if ("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "boost") + set(_bindings_find_package_boost "json") + set(_bindings_json "Boost::json") +endif() + +find_package(Boost COMPONENTS algorithm filesystem numeric_conversion ${_bindings_boost} optional variant regex REQUIRED) + +if("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "rapid") + find_package(RapidJSON REQUIRED) + set(_bindings_json "rapidjson") +endif() set(JINJA2_PRIVATE_LIBS_INT Boost::headers Boost::filesystem Boost::numeric_conversion) -set(JINJA2_PUBLIC_LIBS_INT Boost::json fmt::fmt rapidjson Boost::regex - nlohmann_json::nlohmann_json nonstd::expected-lite nonstd::variant-lite nonstd::optional-lite nonstd::string-view-lite) +set(JINJA2_PUBLIC_LIBS_INT + ${_bindings_json} + Boost::regex + fmt::fmt + ${_test_dependencies} + nonstd::expected-lite + nonstd::optional-lite + nonstd::string-view-lite + nonstd::variant-lite +) + diff --git a/thirdparty/thirdparty-internal.cmake b/thirdparty/thirdparty-internal.cmake index a47b272e..4536d5fc 100644 --- a/thirdparty/thirdparty-internal.cmake +++ b/thirdparty/thirdparty-internal.cmake @@ -5,6 +5,7 @@ include (./thirdparty/internal_deps.cmake) set(BOOST_ENABLE_CMAKE ON) list(APPEND BOOST_INCLUDE_LIBRARIES algorithm + any assert atomic filesystem