From 98a8e7d28ab19cd57ac0ea4db15912d8641105a3 Mon Sep 17 00:00:00 2001 From: Ruslan Morozov Date: Fri, 13 Jun 2025 15:53:03 +0300 Subject: [PATCH] add other json serializers and parsers fix ci pipelines --- .github/workflows/codeql-analysis.yml | 6 +- .github/workflows/linux-build.yml | 11 +- .github/workflows/windows-build.yml | 2 +- CMakeLists.txt | 141 +++++- .../0001-fix-custom_command-error.patch | 24 + cmake/patches/rapid_json_improvements.patch | 428 ++++++++++++++++++ include/jinja2cpp/polymorphic_value.h | 1 - src/binding/boost_json_parser.h | 28 ++ src/binding/boost_json_serializer.cpp | 137 ++++-- src/binding/boost_json_serializer.h | 5 + src/binding/nlohmann_json_parser.h | 30 ++ src/binding/nlohmann_json_serializer.cpp | 118 +++++ src/binding/nlohmann_json_serializer.h | 17 + src/binding/rapid_json_parser.h | 72 +++ src/binding/rapid_json_serializer.cpp | 37 +- src/binding/rapid_json_serializer.h | 7 + src/error_info.cpp | 78 ++-- src/filters.cpp | 1 - src/internal_value.cpp | 28 ++ src/internal_value.h | 35 +- src/lexer.cpp | 2 - src/out_stream.h | 1 - src/renderer.h | 1 - src/serialize_filters.cpp | 32 +- src/template_impl.h | 75 +-- src/value_visitors.h | 1 - test/errors_test.cpp | 3 +- test/filters_test.cpp | 22 +- test/metadata_test.cpp | 2 +- test/perf_test.cpp | 5 +- test/test_tools.h | 4 +- test/tojson_filter_test.cpp | 8 +- thirdparty/CMakeLists.txt | 12 +- thirdparty/external_boost_deps.cmake | 24 +- thirdparty/internal_deps.cmake | 46 +- thirdparty/thirdparty-conan-build.cmake | 36 +- thirdparty/thirdparty-internal.cmake | 5 +- 37 files changed, 1207 insertions(+), 278 deletions(-) create mode 100644 cmake/patches/0001-fix-custom_command-error.patch create mode 100644 cmake/patches/rapid_json_improvements.patch create mode 100644 src/binding/boost_json_parser.h create mode 100644 src/binding/nlohmann_json_parser.h create mode 100644 src/binding/nlohmann_json_serializer.cpp create mode 100644 src/binding/nlohmann_json_serializer.h create mode 100644 src/binding/rapid_json_parser.h diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 04e8819e..36b0b638 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -83,4 +83,4 @@ jobs: - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 769ad56e..bc59b4d0 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false max-parallel: 8 matrix: - compiler: [g++-9, g++-10, g++-11, clang++-12, clang++-13, clang++-14] + compiler: [g++-9, g++-10, g++-11, g++-12, g++-13, clang++-12, clang++-13, clang++-14, clang++-15, clang++-16, clang++-17, clang++-18, clang++-19, clang++-20] base-flags: ["", -DJINJA2CPP_CXX_STANDARD=17] build-config: [Release, Debug] build-shared: [TRUE, FALSE] @@ -38,9 +38,16 @@ jobs: extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF - compiler: g++-11 extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF + - compiler: g++-12 + extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF + - compiler: g++-13 + extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF + - compiler: g++-14 + extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup environment env: INPUT_COMPILER: ${{ matrix.compiler }} diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index b7a4635f..4de6fc29 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -46,7 +46,7 @@ jobs: steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build shell: cmd diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aabcff5..52ed266c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23.0) +cmake_minimum_required(VERSION 3.23.0..4.0) if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) @@ -19,9 +19,9 @@ 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) -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(JINJA2CPP_WITH_JSON_SUPPORTED_BINDINGS boost nlohmann rapid) +set(JINJA2CPP_WITH_JSON_BINDINGS boost CACHE STRING "Build with json support(boost|rapid|nlohmann) for serialization operations, like tojson filter. 'boost' is default.") +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,13 +144,29 @@ 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(PublicSources PublicHeaders ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include) add_library(${LIB_TARGET_NAME} ${LIB_LINK_TYPE} - ${Sources} - ${Headers} - ${PublicHeaders} + ${JINJA2CPP_SOURCES} ) target_sources(${LIB_TARGET_NAME} @@ -185,19 +201,6 @@ endif() set(JINJA2CPP_PRIVATE_LIBS "${JINJA2CPP_PRIVATE_LIBS}") include(thirdparty/CMakeLists.txt) -target_link_libraries( - ${LIB_TARGET_NAME} - PUBLIC - ${JINJA2CPP_PUBLIC_LIBS} - PRIVATE - ${JINJA2CPP_PRIVATE_LIBS} -) - -target_include_directories(${LIB_TARGET_NAME} - PUBLIC - $ - $ -) if (JINJA2CPP_STRICT_WARNINGS) if (UNIX) @@ -218,11 +221,44 @@ endif () if ("${JINJA2CPP_USE_REGEX}" STREQUAL "boost") set(_regex_define "-DJINJA2CPP_USE_REGEX_BOOST") endif() + 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 + ) + list(APPEND JINJA2CPP_PRIVATE_LIBS Boost::json) 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 + ) + list(APPEND JINJA2CPP_PRIVATE_LIBS RapidJson) +else() + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_NLOHMANN") + target_sources(${LIB_TARGET_NAME} + PRIVATE + src/binding/nlohmann_json_serializer.cpp + ) + list(APPEND JINJA2CPP_PRIVATE_LIBS nlohmann_json::nlohmann_json) endif() + +target_link_libraries( + ${LIB_TARGET_NAME} + PUBLIC + ${JINJA2CPP_PUBLIC_LIBS} + PRIVATE + ${JINJA2CPP_PRIVATE_LIBS} +) + +target_include_directories(${LIB_TARGET_NAME} + PUBLIC + $ + $ +) + target_compile_definitions(${LIB_TARGET_NAME} PUBLIC -DBOOST_SYSTEM_NO_DEPRECATED @@ -251,8 +287,32 @@ 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 + ) + + 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 +337,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 +446,4 @@ if(JINJA2CPP_INSTALL) DESTINATION ${JINJA2CPP_INSTALL_CONFIG_DIR} ) -endif() \ No newline at end of file +endif() diff --git a/cmake/patches/0001-fix-custom_command-error.patch b/cmake/patches/0001-fix-custom_command-error.patch new file mode 100644 index 00000000..5f4b2540 --- /dev/null +++ b/cmake/patches/0001-fix-custom_command-error.patch @@ -0,0 +1,24 @@ +From c5e3f11d98aa3116001361ef108c7bd4fa5e3a7f Mon Sep 17 00:00:00 2001 +Date: Fri, 13 Jun 2025 14:46:05 +0300 +Subject: [PATCH] fix custom_command error + +--- + CMakeLists.txt | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index be860c93..73f067c9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -161,8 +161,7 @@ if(RAPIDJSON_BUILD_DOC) + add_subdirectory(doc) + endif() + +-add_custom_target(travis_doc) +-add_custom_command(TARGET travis_doc ++add_custom_target(TARGET travis_doc + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/travis-doxygen.sh) + + if(RAPIDJSON_BUILD_EXAMPLES) +-- + diff --git a/cmake/patches/rapid_json_improvements.patch b/cmake/patches/rapid_json_improvements.patch new file mode 100644 index 00000000..f5c2c997 --- /dev/null +++ b/cmake/patches/rapid_json_improvements.patch @@ -0,0 +1,428 @@ +From 82801edefa7c53648cfca119ea8dd7f8787106e0 Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 17:50:54 +0200 +Subject: [PATCH 1/5] Use GNUInstallDirs defaults + +--- + CMakeLists.txt | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index be860c93d..7e4f799de 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -144,9 +144,11 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "XL") + endif() + + #add extra search paths for libraries and includes +-SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") +-SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE STRING "Directory where lib will install") +-SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}" CACHE PATH "Path to the documentation") ++include(GNUInstallDirs) ++ ++SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}" CACHE PATH "The directory the headers are installed in") ++SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE STRING "Directory where lib will install") ++SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_DOCDIR}" CACHE PATH "Path to the documentation") + + IF(UNIX OR CYGWIN) + SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}") + +From 792928b9390bedf0fc4aaf934f76895308688c86 Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 17:52:18 +0200 +Subject: [PATCH 2/5] Bump cmake policies + +--- + CMakeLists.txt | 2 +- + example/CMakeLists.txt | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7e4f799de..c22a425c0 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,4 +1,4 @@ +-CMAKE_MINIMUM_REQUIRED(VERSION 3.5) ++cmake_minimum_required(VERSION 3.5...4.0) + + SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules) + +diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt +index 9f53c9aad..67024d060 100644 +--- a/example/CMakeLists.txt ++++ b/example/CMakeLists.txt +@@ -1,4 +1,4 @@ +-cmake_minimum_required(VERSION 2.8) ++cmake_minimum_required(VERSION 2.8...4.0) + + if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) + +From 99bac253c925e7d47766f6beb54f14b37baba83f Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 18:05:33 +0200 +Subject: [PATCH 3/5] Use modern way to consume third-party gtest + +--- + .gitmodules | 3 --- + CMakeLists.txt | 3 +-- + CMakeModules/FindGTestSrc.cmake | 30 ------------------------- + test/CMakeLists.txt | 40 +++++++++++++++++++-------------- + test/perftest/CMakeLists.txt | 2 +- + test/perftest/perftest.h | 2 +- + test/unittest/CMakeLists.txt | 3 ++- + test/unittest/unittest.h | 2 +- + thirdparty/gtest | 1 - + 9 files changed, 29 insertions(+), 57 deletions(-) + delete mode 100644 .gitmodules + delete mode 100644 CMakeModules/FindGTestSrc.cmake + delete mode 160000 thirdparty/gtest + +diff --git a/.gitmodules b/.gitmodules +deleted file mode 100644 +index 5e41f7c97..000000000 +--- a/.gitmodules ++++ /dev/null +@@ -1,3 +0,0 @@ +-[submodule "thirdparty/gtest"] +- path = thirdparty/gtest +- url = https://github.com/google/googletest.git +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c22a425c0..a8bffe2fd 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -20,8 +20,6 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." ON) + option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." ON) + option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." ON) +-option(RAPIDJSON_BUILD_THIRDPARTY_GTEST +- "Use gtest installation in `thirdparty/gtest` by default if available" OFF) + + option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11" ON) + option(RAPIDJSON_BUILD_CXX17 "Build rapidjson with C++17" OFF) +@@ -172,6 +170,7 @@ if(RAPIDJSON_BUILD_EXAMPLES) + endif() + + if(RAPIDJSON_BUILD_TESTS) ++ enable_testing() + if(MSVC11) + # required for VS2012 due to missing support for variadic templates + add_definitions(-D_VARIADIC_MAX=10) +diff --git a/CMakeModules/FindGTestSrc.cmake b/CMakeModules/FindGTestSrc.cmake +deleted file mode 100644 +index f3cb8c990..000000000 +--- a/CMakeModules/FindGTestSrc.cmake ++++ /dev/null +@@ -1,30 +0,0 @@ +- +-SET(GTEST_SEARCH_PATH +- "${GTEST_SOURCE_DIR}" +- "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/gtest/googletest") +- +-IF(UNIX) +- IF(RAPIDJSON_BUILD_THIRDPARTY_GTEST) +- LIST(APPEND GTEST_SEARCH_PATH "/usr/src/gtest") +- ELSE() +- LIST(INSERT GTEST_SEARCH_PATH 1 "/usr/src/gtest") +- ENDIF() +-ENDIF() +- +-FIND_PATH(GTEST_SOURCE_DIR +- NAMES CMakeLists.txt src/gtest_main.cc +- PATHS ${GTEST_SEARCH_PATH}) +- +- +-# Debian installs gtest include directory in /usr/include, thus need to look +-# for include directory separately from source directory. +-FIND_PATH(GTEST_INCLUDE_DIR +- NAMES gtest/gtest.h +- PATH_SUFFIXES include +- HINTS ${GTEST_SOURCE_DIR} +- PATHS ${GTEST_SEARCH_PATH}) +- +-INCLUDE(FindPackageHandleStandardArgs) +-find_package_handle_standard_args(GTestSrc DEFAULT_MSG +- GTEST_SOURCE_DIR +- GTEST_INCLUDE_DIR) +diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt +index 11c1b04c7..336dbb564 100644 +--- a/test/CMakeLists.txt ++++ b/test/CMakeLists.txt +@@ -1,20 +1,26 @@ +-find_package(GTestSrc) ++cmake_minimum_required(VERSION 3.11...4.0) + +-IF(GTESTSRC_FOUND) +- enable_testing() ++include(FetchContent) + +- if (WIN32 AND (NOT CYGWIN) AND (NOT MINGW)) +- set(gtest_disable_pthreads ON) +- set(gtest_force_shared_crt ON) +- endif() ++set(BUILD_GMOCK OFF) ++set(INSTALL_GTEST OFF) ++if (WIN32 AND (NOT CYGWIN) AND (NOT MINGW)) ++ set(gtest_disable_pthreads ON) ++ set(gtest_force_shared_crt ON) ++endif() ++set(fetchcontent_extra_args) ++if(CMAKE_VERSION GREATER_EQUAL 3.24) ++ list(APPEND fetchcontent_extra_args ++ FIND_PACKAGE_ARGS CONFIG ++ ) ++endif() ++FetchContent_Declare(GTest ++ GIT_REPOSITORY https://github.com/google/googletest ++ GIT_TAG v1.17.0 ++ ${fetchcontent_extra_args} ++) ++FetchContent_MakeAvailable(GTest) + +- add_subdirectory(${GTEST_SOURCE_DIR} ${CMAKE_BINARY_DIR}/googletest) +- include_directories(SYSTEM ${GTEST_INCLUDE_DIR}) +- +- set(TEST_LIBRARIES gtest gtest_main) +- +- add_custom_target(tests ALL) +- add_subdirectory(perftest) +- add_subdirectory(unittest) +- +-ENDIF(GTESTSRC_FOUND) ++add_custom_target(tests ALL) ++add_subdirectory(perftest) ++add_subdirectory(unittest) +diff --git a/test/perftest/CMakeLists.txt b/test/perftest/CMakeLists.txt +index 035e544d9..4d2582fa5 100644 +--- a/test/perftest/CMakeLists.txt ++++ b/test/perftest/CMakeLists.txt +@@ -6,7 +6,7 @@ set(PERFTEST_SOURCES + schematest.cpp) + + add_executable(perftest ${PERFTEST_SOURCES}) +-target_link_libraries(perftest ${TEST_LIBRARIES}) ++target_link_libraries(perftest PRIVATE GTest::gtest GTest::gtest_main) + + add_dependencies(tests perftest) + +diff --git a/test/perftest/perftest.h b/test/perftest/perftest.h +index 31e3ca633..8ceaebc7f 100644 +--- a/test/perftest/perftest.h ++++ b/test/perftest/perftest.h +@@ -52,7 +52,7 @@ + #pragma GCC diagnostic ignored "-Weffc++" + #endif + +-#include "gtest/gtest.h" ++#include + + #if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic pop +diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt +index 9a369d404..4f93438d4 100644 +--- a/test/unittest/CMakeLists.txt ++++ b/test/unittest/CMakeLists.txt +@@ -70,9 +70,10 @@ endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRAPIDJSON_HAS_STDSTRING=1") + + add_library(namespacetest STATIC namespacetest.cpp) ++target_link_libraries(namespacetest PUBLIC GTest::gtest GTest::gtest_main) + + add_executable(unittest ${UNITTEST_SOURCES}) +-target_link_libraries(unittest ${TEST_LIBRARIES} namespacetest) ++target_link_libraries(unittest PRIVATE namespacetest) + + add_dependencies(tests unittest) + +diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h +index 0e64d3970..0db961747 100644 +--- a/test/unittest/unittest.h ++++ b/test/unittest/unittest.h +@@ -44,7 +44,7 @@ + #pragma GCC diagnostic ignored "-Weffc++" + #endif + +-#include "gtest/gtest.h" ++#include + #include + + #if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + +From b1ac740622aa087210fe98ff5c7bdedb5920b78f Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 18:09:29 +0200 +Subject: [PATCH 4/5] Use RapidJSON target internally + +--- + CMakeLists.txt | 7 ++++--- + test/perftest/CMakeLists.txt | 2 +- + test/unittest/CMakeLists.txt | 2 +- + 3 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index a8bffe2fd..492323ec9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -155,8 +155,6 @@ ELSEIF(WIN32) + ENDIF() + SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake files are installed in") + +-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +- + if(RAPIDJSON_BUILD_DOC) + add_subdirectory(doc) + endif() +@@ -196,7 +194,10 @@ install(FILES readme.md + # Add an interface target to export it + add_library(RapidJSON INTERFACE) + +-target_include_directories(RapidJSON INTERFACE $) ++target_include_directories(RapidJSON INTERFACE ++ $ ++ $ ++) + + install(DIRECTORY include/rapidjson + DESTINATION "${INCLUDE_INSTALL_DIR}" +diff --git a/test/perftest/CMakeLists.txt b/test/perftest/CMakeLists.txt +index 4d2582fa5..e1677f862 100644 +--- a/test/perftest/CMakeLists.txt ++++ b/test/perftest/CMakeLists.txt +@@ -6,7 +6,7 @@ set(PERFTEST_SOURCES + schematest.cpp) + + add_executable(perftest ${PERFTEST_SOURCES}) +-target_link_libraries(perftest PRIVATE GTest::gtest GTest::gtest_main) ++target_link_libraries(perftest PRIVATE RapidJSON GTest::gtest GTest::gtest_main) + + add_dependencies(tests perftest) + +diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt +index 4f93438d4..8a3c261c1 100644 +--- a/test/unittest/CMakeLists.txt ++++ b/test/unittest/CMakeLists.txt +@@ -70,7 +70,7 @@ endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRAPIDJSON_HAS_STDSTRING=1") + + add_library(namespacetest STATIC namespacetest.cpp) +-target_link_libraries(namespacetest PUBLIC GTest::gtest GTest::gtest_main) ++target_link_libraries(namespacetest PUBLIC RapidJSON GTest::gtest GTest::gtest_main) + + add_executable(unittest ${UNITTEST_SOURCES}) + target_link_libraries(unittest PRIVATE namespacetest) + +From d18b42edcce14d8697f5dc5c777c76bfa6574e79 Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 18:21:51 +0200 +Subject: [PATCH 5/5] Use modern Config.cmake design + +--- + CMakeLists.txt | 59 +++++++++++---------------------- + RapidJSONConfig.cmake.in | 4 +-- + RapidJSONConfigVersion.cmake.in | 10 ------ + 3 files changed, 22 insertions(+), 51 deletions(-) + delete mode 100644 RapidJSONConfigVersion.cmake.in + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 492323ec9..062f6d80c 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -216,44 +216,25 @@ install(DIRECTORY example/ + # =============================== + + ################################################################################ +-# Export package for use from the build tree +-EXPORT( PACKAGE ${PROJECT_NAME} ) +- +-# Create the RapidJSONConfig.cmake file for other cmake projects. +-# ... for the build tree +-SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +-SET( CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR}) +-SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_SOURCE_DIR}/include" ) +- +-INCLUDE(CMakePackageConfigHelpers) +-CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY ) +-CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake @ONLY) +- +-# ... for the install tree +-SET( CMAKECONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME} ) +-FILE( RELATIVE_PATH REL_INCLUDE_DIR +- "${CMAKECONFIG_INSTALL_DIR}" +- "${CMAKE_INSTALL_PREFIX}/include" ) +- +-SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" ) +-SET( CONFIG_SOURCE_DIR ) +-SET( CONFIG_DIR ) +-CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in +- ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake @ONLY ) +- +-INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake" +- DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) ++include(CMakePackageConfigHelpers) ++write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake ++ COMPATIBILITY AnyNewerVersion ++) ++configure_package_config_file( ++ ${PROJECT_NAME}Config.cmake.in ++ ${PROJECT_NAME}Config.cmake ++ INSTALL_DESTINATION ${CMAKE_INSTALL_DIR} ++) + + # Install files +-IF(CMAKE_INSTALL_DIR) +- INSTALL(FILES +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake +- DESTINATION "${CMAKE_INSTALL_DIR}" +- COMPONENT dev) +- +- INSTALL(TARGETS RapidJSON EXPORT RapidJSON-targets) +- INSTALL(EXPORT RapidJSON-targets DESTINATION ${CMAKE_INSTALL_DIR}) +-ENDIF() ++install(FILES ++ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" ++ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" ++ DESTINATION ${CMAKE_INSTALL_DIR} ++ COMPONENT dev ++) ++install(TARGETS RapidJSON EXPORT RapidJSON-targets) ++export(TARGETS RapidJSON ++ FILE RapidJSON-targets.cmake ++) ++install(EXPORT RapidJSON-targets DESTINATION ${CMAKE_INSTALL_DIR}) +diff --git a/RapidJSONConfig.cmake.in b/RapidJSONConfig.cmake.in +index 0ee1d9d3b..6a1e29397 100644 +--- a/RapidJSONConfig.cmake.in ++++ b/RapidJSONConfig.cmake.in +@@ -4,11 +4,11 @@ include ("${CMAKE_CURRENT_LIST_DIR}/RapidJSON-targets.cmake") + + ################################################################################ + # RapidJSON source dir +-set( RapidJSON_SOURCE_DIR "@CONFIG_SOURCE_DIR@") ++set( RapidJSON_SOURCE_DIR "RapidJSON_SOURCE_DIR-NOTFOUND") + + ################################################################################ + # RapidJSON build dir +-set( RapidJSON_DIR "@CONFIG_DIR@") ++set( RapidJSON_DIR "RapidJSON_DIR-NOTFOUND") + + ################################################################################ + # Compute paths +diff --git a/RapidJSONConfigVersion.cmake.in b/RapidJSONConfigVersion.cmake.in +deleted file mode 100644 +index 25741fc09..000000000 +--- a/RapidJSONConfigVersion.cmake.in ++++ /dev/null +@@ -1,10 +0,0 @@ +-SET(PACKAGE_VERSION "@LIB_VERSION_STRING@") +- +-IF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) +- SET(PACKAGE_VERSION_EXACT "true") +-ENDIF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) +-IF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) +- SET(PACKAGE_VERSION_COMPATIBLE "true") +-ELSE (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) +- SET(PACKAGE_VERSION_UNSUITABLE "true") +-ENDIF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) diff --git a/include/jinja2cpp/polymorphic_value.h b/include/jinja2cpp/polymorphic_value.h index c5cb86ac..28356ad9 100644 --- a/include/jinja2cpp/polymorphic_value.h +++ b/include/jinja2cpp/polymorphic_value.h @@ -31,7 +31,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// required variant_CPP17_OR_GREATER definition #include // diff --git a/src/binding/boost_json_parser.h b/src/binding/boost_json_parser.h new file mode 100644 index 00000000..58d5cd75 --- /dev/null +++ b/src/binding/boost_json_parser.h @@ -0,0 +1,28 @@ +#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) +{ + //intentionally ignore metadataJson + (void)metadataJson; + 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..f23d5740 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 @@ -20,7 +25,7 @@ struct JsonInserter : visitors::BaseVisitor boost::json::value operator()(const ListAdapter& list) const { - boost::json::array listValue; //(boost::json::kind::array); + boost::json::array listValue; for (auto& v : list) { @@ -31,7 +36,7 @@ struct JsonInserter : visitors::BaseVisitor boost::json::value operator()(const MapAdapter& map) const { - boost::json::object mapNode; //(boost::json::kind::object); + boost::json::object mapNode; const auto& keys = map.GetKeys(); for (auto& k : keys) @@ -44,7 +49,7 @@ struct JsonInserter : visitors::BaseVisitor boost::json::value operator()(const KeyValuePair& kwPair) const { - boost::json::object pairNode; //(boost::json::kind::object); + boost::json::object pairNode; pairNode.emplace(kwPair.key.c_str(), Apply(kwPair.value)); return pairNode; @@ -101,19 +106,19 @@ void PrettyPrint(fmt::basic_memory_buffer& os, const boost::json::value& j { switch (jv.kind()) { - case boost::json::kind::object: + case boost::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 (;;) - { + const auto& obj = jv.get_object(); + if (!obj.empty()) + { + auto it = obj.begin(); + for (;;) + { auto key = boost::json::serialize(it->key()); fmt::format_to( std::back_inserter(os), @@ -123,62 +128,73 @@ void PrettyPrint(fmt::basic_memory_buffer& os, const boost::json::value& j ":", (indent == 0) ? 0 : 2 ); - PrettyPrint(os, it->value(), indent, level + 1); - if (++it == obj.end()) - break; + 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; - } + break; + } - case boost::json::kind::array: + case boost::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); - } - } + bool singleLineArray = false; + auto const& arr = jv.get_array(); + if (!arr.empty()) + { + 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 : 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)); + } fmt::format_to(std::back_inserter(os), "]"); - break; - } + break; + } - case boost::json::kind::string: + case boost::json::kind::string: { fmt::format_to(std::back_inserter(os), "{}", boost::json::serialize(jv.get_string())); - break; - } + break; + } - case boost::json::kind::uint64: - case boost::json::kind::int64: - case boost::json::kind::double_: + case boost::json::kind::uint64: + case boost::json::kind::int64: + case boost::json::kind::double_: { fmt::format_to(std::back_inserter(os), "{}", jv); break; } - case boost::json::kind::bool_: + case boost::json::kind::bool_: { fmt::format_to(std::back_inserter(os), "{}", jv.get_bool()); - break; + break; } - case boost::json::kind::null: + case boost::json::kind::null: { fmt::format_to(std::back_inserter(os), "null"); - break; + break; } } } @@ -186,9 +202,40 @@ void PrettyPrint(fmt::basic_memory_buffer& os, const boost::json::value& j std::string ValueWrapper::AsString(const uint8_t indent) const { fmt::memory_buffer out; - PrettyPrint(out, m_value, indent); - return fmt::to_string(out); + PrettyPrint(out, m_value, indent); + return fmt::to_string(out); } } // 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..e3d15b2a --- /dev/null +++ b/src/binding/nlohmann_json_parser.h @@ -0,0 +1,30 @@ +#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) +{ + //intentionally ignore metadataJson + (void)metadataJson; + try + { + auto value = nlohmann::json::parse(json.data(), json.data() + json.size()); + return Reflect(value); + } + catch(std::exception& ex) + { + return nonstd::make_unexpected(ex.what()); + } +} + +} // 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..ec542960 --- /dev/null +++ b/src/binding/nlohmann_json_serializer.cpp @@ -0,0 +1,118 @@ +#include "nlohmann_json_serializer.h" + +#include "../value_visitors.h" + +#include + +#include +#include +#include + + +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); + } + + nlohmann::json operator()(const nonstd::string_view& str) const + { + return nlohmann::json(str); + } + + nlohmann::json operator()(const std::wstring& str) const + { + auto s = ConvertString(str); + return nlohmann::json(s); + } + + nlohmann::json operator()(const nonstd::wstring_view& str) const + { + auto s = ConvertString(str); + return nlohmann::json(s); + } + + 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 +} // namespace nlohmann_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent) +{ + using namespace std::literals; + auto jsonValue = Apply(value); + auto jsonString = jsonValue.dump(indent == 0 ? -1 : 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..81b1b199 --- /dev/null +++ b/src/binding/nlohmann_json_serializer.h @@ -0,0 +1,17 @@ +#ifndef JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H +#define JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H + +#include "../internal_value.h" + +#include + +#include + +namespace jinja2 +{ + +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..0a3d85da --- /dev/null +++ b/src/binding/rapid_json_parser.h @@ -0,0 +1,72 @@ +#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(); // persist parse result + auto& jsonDoc = boost::any_cast(metadataJson); + rapidjson::ParseResult res = jsonDoc.Parse(json.data(), json.size()); + if (!res) + { + std::string jsonError = rapidjson::GetParseError_En(res.Code()); + return nonstd::make_unexpected(jsonError); + } + 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..149b4d06 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,45 @@ 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; + } + }); + return result; +} + } // namespace jinja2 diff --git a/src/binding/rapid_json_serializer.h b/src/binding/rapid_json_serializer.h index 66e26c14..d5b4c4b5 100644 --- a/src/binding/rapid_json_serializer.h +++ b/src/binding/rapid_json_serializer.h @@ -10,6 +10,7 @@ namespace jinja2 { + namespace rapidjson_serializer { @@ -44,7 +45,13 @@ class DocumentWrapper std::shared_ptr 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/error_info.cpp b/src/error_info.cpp index b60cc8e2..cb802995 100644 --- a/src/error_info.cpp +++ b/src/error_info.cpp @@ -102,7 +102,7 @@ struct formatter } template - auto format(const jinja2::Value& val, FormatContext& ctx) + auto format(const jinja2::Value& val, FormatContext& ctx) const { nonstd::visit(ValueRenderer(&ctx), val.data()); return fmt::format_to(ctx.out(), UNIVERSAL_STR("").GetValue()); @@ -126,53 +126,53 @@ void RenderErrorInfo(std::basic_string& result, const ErrorInfoTpl switch (errCode) { case ErrorCode::Unspecified: - format_to(std::back_inserter(out), UNIVERSAL_STR("Parse error").GetValue()); - break; + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Parse error").GetValue()); + break; case ErrorCode::UnexpectedException: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected exception occurred during template processing. Exception: {}").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected exception occurred during template processing. Exception: {}").GetValue(), extraParams[0]); break; } case ErrorCode::MetadataParseError: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Error occurred during template metadata parsing. Error: {}").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Error occurred during template metadata parsing. Error: {}").GetValue(), extraParams[0]); break; } case ErrorCode::YetUnsupported: - format_to(std::back_inserter(out), UNIVERSAL_STR("This feature has not been supported yet").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("This feature has not been supported yet").GetValue()); break; case ErrorCode::FileNotFound: - format_to(std::back_inserter(out), UNIVERSAL_STR("File not found").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("File not found").GetValue()); break; case ErrorCode::ExpectedStringLiteral: - format_to(std::back_inserter(out), UNIVERSAL_STR("String expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("String expected").GetValue()); break; case ErrorCode::ExpectedIdentifier: - format_to(std::back_inserter(out), UNIVERSAL_STR("Identifier expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Identifier expected").GetValue()); break; case ErrorCode::ExpectedSquareBracket: - format_to(std::back_inserter(out), UNIVERSAL_STR("']' expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("']' expected").GetValue()); break; case ErrorCode::ExpectedRoundBracket: - format_to(std::back_inserter(out), UNIVERSAL_STR("')' expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("')' expected").GetValue()); break; case ErrorCode::ExpectedCurlyBracket: - format_to(std::back_inserter(out), UNIVERSAL_STR("'}}' expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("'}}' expected").GetValue()); break; case ErrorCode::ExpectedToken: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token '{}'").GetValue(), extraParams[0]); if (extraParams.size() > 1) { - format_to(std::back_inserter(out), UNIVERSAL_STR(". Expected: ").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR(". Expected: ").GetValue()); for (std::size_t i = 1; i < extraParams.size(); ++ i) { if (i != 1) - format_to(std::back_inserter(out), UNIVERSAL_STR(", ").GetValue()); - format_to(std::back_inserter(out), UNIVERSAL_STR("\'{}\'").GetValue(), extraParams[i]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR(", ").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("\'{}\'").GetValue(), extraParams[i]); } } break; @@ -180,84 +180,84 @@ void RenderErrorInfo(std::basic_string& result, const ErrorInfoTpl case ErrorCode::ExpectedExpression: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected expression, got: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected expression, got: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::ExpectedEndOfStatement: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of statement, got: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of statement, got: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::ExpectedRawEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of raw block").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of raw block").GetValue()); break; case ErrorCode::ExpectedMetaEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of meta block").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of meta block").GetValue()); break; case ErrorCode::UnexpectedToken: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::UnexpectedStatement: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::UnexpectedCommentBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment begin").GetValue()); break; case ErrorCode::UnexpectedCommentEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment end").GetValue()); break; case ErrorCode::UnexpectedRawBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block begin").GetValue()); break; case ErrorCode::UnexpectedRawEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block end").GetValue()); break; case ErrorCode::UnexpectedMetaBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block begin").GetValue()); break; case ErrorCode::UnexpectedMetaEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block end").GetValue()); break; case ErrorCode::UnexpectedExprBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block begin").GetValue()); break; case ErrorCode::UnexpectedExprEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block end").GetValue()); break; case ErrorCode::UnexpectedStmtBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block begin").GetValue()); break; case ErrorCode::UnexpectedStmtEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block end").GetValue()); break; case ErrorCode::TemplateNotParsed: - format_to(std::back_inserter(out), UNIVERSAL_STR("Template not parsed").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Template not parsed").GetValue()); break; case ErrorCode::TemplateNotFound: - format_to(std::back_inserter(out), UNIVERSAL_STR("Template(s) not found: {}").GetValue(), errInfo.GetExtraParams()[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Template(s) not found: {}").GetValue(), errInfo.GetExtraParams()[0]); break; case ErrorCode::InvalidTemplateName: - format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid template name: {}").GetValue(), errInfo.GetExtraParams()[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid template name: {}").GetValue(), errInfo.GetExtraParams()[0]); break; case ErrorCode::InvalidValueType: - format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid value type").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid value type").GetValue()); break; case ErrorCode::ExtensionDisabled: - format_to(std::back_inserter(out), UNIVERSAL_STR("Extension disabled").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Extension disabled").GetValue()); break; case ErrorCode::TemplateEnvAbsent: - format_to(std::back_inserter(out), UNIVERSAL_STR("Template environment doesn't set").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Template environment doesn't set").GetValue()); break; } format_to(std::back_inserter(out), UNIVERSAL_STR("\n{}").GetValue(), errInfo.GetLocationDescr()); - result = to_string(out); + result = {out.data(), out.size()}; } template<> 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/internal_value.cpp b/src/internal_value.cpp index 3f64eeff..fc02d430 100644 --- a/src/internal_value.cpp +++ b/src/internal_value.cpp @@ -8,6 +8,34 @@ namespace jinja2 { +void InternalValue::SetParentData(const InternalValue& val) +{ + m_parentData = val.GetData(); +} + +void InternalValue::SetParentData(InternalValue&& val) +{ + m_parentData = std::move(val.GetData()); +} + + +void ListAdapter::Iterator::increment() +{ + m_isFinished = !m_iterator->MoveNext(); + ++ m_currentIndex; + m_currentVal = m_isFinished ? InternalValue() : m_iterator->GetCurrent(); +} + +bool ListAdapter::Iterator::equal(const Iterator& other) const +{ + if (!this->m_iterator) + return !other.m_iterator ? true : other.equal(*this); + + if (!other.m_iterator) + return this->m_isFinished; + return this->m_iterator->GetCurrent() == other.m_iterator->GetCurrent() && this->m_currentIndex == other.m_currentIndex; +} + std::atomic_uint64_t UserCallable::m_gen{}; bool Value::IsEqual(const Value& rhs) const diff --git a/src/internal_value.h b/src/internal_value.h index b674db1c..084749f9 100644 --- a/src/internal_value.h +++ b/src/internal_value.h @@ -52,7 +52,7 @@ class ReferenceWrapper T* m_ptr; }; -template +template class RecursiveWrapper { public: @@ -382,15 +382,9 @@ class InternalValue auto& GetParentData() {return m_parentData;} auto& GetParentData() const {return m_parentData;} - void SetParentData(const InternalValue& val) - { - m_parentData = val.GetData(); - } + void SetParentData(const InternalValue& val); - void SetParentData(InternalValue&& val) - { - m_parentData = std::move(val.GetData()); - } + void SetParentData(InternalValue&& val); bool ShouldExtendLifetime() const { @@ -444,27 +438,9 @@ class ListAdapter::Iterator private: friend class boost::iterator_core_access; - void increment() - { - m_isFinished = !m_iterator->MoveNext(); - ++ m_currentIndex; - m_currentVal = m_isFinished ? InternalValue() : m_iterator->GetCurrent(); - } + void increment(); - bool equal(const Iterator& other) const - { - if (!this->m_iterator) - return !other.m_iterator ? true : other.equal(*this); - - if (!other.m_iterator) - return this->m_isFinished; -// return true; - //const InternalValue& lhs = *(this->m_iterator); - //const InternalValue& rhs = *(other.m_iterator); - //return lhs == rhs; - return this->m_iterator->GetCurrent() == other.m_iterator->GetCurrent() && this->m_currentIndex == other.m_currentIndex; - ///return *(this->m_iterator) == *(other.m_iterator) && this->m_currentIndex == other.m_currentIndex; - } + bool equal(const Iterator& other) const; const InternalValue& dereference() const { @@ -639,6 +615,7 @@ class Callable CallableHolder m_callable; }; + inline bool IsEmpty(const InternalValue& val) { return val.IsEmpty() || nonstd::get_if(&val.GetData()) != nullptr; diff --git a/src/lexer.cpp b/src/lexer.cpp index dfd33eb3..a59fdf39 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -1,7 +1,5 @@ #include "lexer.h" -#include - namespace jinja2 { diff --git a/src/out_stream.h b/src/out_stream.h index 8e3f001b..554a8a81 100644 --- a/src/out_stream.h +++ b/src/out_stream.h @@ -4,7 +4,6 @@ #include "internal_value.h" #include -#include #include namespace jinja2 diff --git a/src/renderer.h b/src/renderer.h index 9c08d237..cda9e860 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/src/serialize_filters.cpp b/src/serialize_filters.cpp index a8e4363a..8bf3b825 100644 --- a/src/serialize_filters.cpp +++ b/src/serialize_filters.cpp @@ -15,10 +15,10 @@ #ifdef JINJA2CPP_WITH_JSON_BINDINGS_BOOST #include "binding/boost_json_serializer.h" -using DocumentWrapper = jinja2::boost_json_serializer::DocumentWrapper; +#elif JINJA2CPP_WITH_JSON_BINDINGS_NLOHMANN +#include "binding/nlohmann_json_serializer.h" #else #include "binding/rapid_json_serializer.h" -using DocumentWrapper = jinja2::rapidjson_serializer::DocumentWrapper; #endif @@ -149,25 +149,7 @@ 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)); - std::string result = ""s; - result.reserve(jsonString.size()); - for (char c : jsonString) { - if (c == '<') { - result.append("\\u003c"); - } else if (c == '>') { - result.append("\\u003e"); - } else if (c == '&') { - result.append("\\u0026"); - } else if (c == '\'') { - result.append("\\u0027"); - } else { - result.push_back(c); - } - } - return result; + return ToJson(value, indent); } return InternalValue(); @@ -233,10 +215,10 @@ struct FormatArgumentConverter : visitors::BaseVisitor { m_store.push_back(fmt::arg(m_name.c_str(), t)); } - return fmt::detail::make_arg(t); + return fmt::basic_format_arg(t); } - const RenderContext* m_context; + const RenderContext* m_context{}; FormatDynamicArgsStore& m_store; const std::string m_name; bool m_named = false; @@ -427,8 +409,8 @@ class XmlAttrPrinter : public visitors::BaseVisitor } private: - RenderContext* m_context; - bool m_isFirstLevel; + RenderContext* m_context{}; + bool m_isFirstLevel{}; }; XmlAttrFilter::XmlAttrFilter(FilterParams) {} diff --git a/src/template_impl.h b/src/template_impl.h index 8f4ad3a6..06884520 100644 --- a/src/template_impl.h +++ b/src/template_impl.h @@ -2,59 +2,34 @@ #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" +#elif JINJA2CPP_WITH_JSON_BINDINGS_NLOHMANN +#include "binding/nlohmann_json_parser.h" +#include "jinja2cpp/binding/nlohmann_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 +39,6 @@ class ITemplateImpl virtual ~ITemplateImpl() = default; }; - template struct TemplateLoader; @@ -350,18 +324,16 @@ class TemplateImpl : public ITemplateImpl if (m_metadataInfo.metadataType == "json") { - m_metadataJson = JsonDocumentType(); - rapidjson::ParseResult res = m_metadataJson.value().Parse(metadataString.data(), metadataString.size()); - if (!res) + auto result = Parse(metadataString, m_metadataJson); + if (!result) { typename ErrorInfoTpl::Data errorData; errorData.code = ErrorCode::MetadataParseError; errorData.srcLoc = m_metadataInfo.location; - std::string jsonError = rapidjson::GetParseError_En(res.Code()); - errorData.extraParams.push_back(Value(std::move(jsonError))); + errorData.extraParams.push_back(Value(result.error())); 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 +356,9 @@ class TemplateImpl : public ITemplateImpl return false; if (m_metadata != other.m_metadata) return false; - if (m_metadataJson != other.m_metadataJson) - return false; + // m_metadataJson - only for persistence purposes + //if (m_metadataJson != other.m_metadataJson) + // return false; if (m_metadataInfo != other.m_metadataInfo) return false; return true; @@ -467,18 +440,16 @@ class TemplateImpl : public ITemplateImpl } private: - ThisType* m_host; + ThisType* m_host{}; }; private: - using JsonDocumentType = rapidjson::GenericDocument::type>; - TemplateEnv* m_env{}; Settings m_settings; std::basic_string m_template; 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/src/value_visitors.h b/src/value_visitors.h index 193680c5..182cb13f 100644 --- a/src/value_visitors.h +++ b/src/value_visitors.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/test/errors_test.cpp b/test/errors_test.cpp index bcb137b1..9331fd8b 100644 --- a/test/errors_test.cpp +++ b/test/errors_test.cpp @@ -40,8 +40,7 @@ TEST_F(TemplateEnvFixture, EnvironmentAbsentErrorsTest_Wide) TemplateW tpl1; auto parseResult = tpl1.Load(L"{% extends 'module' %}"); ASSERT_FALSE(parseResult.has_value()); - - EXPECT_EQ(L"noname.j2tpl:1:4: error: Template environment doesn't set\n{% extends 'module' %}\n---^-------", ErrorToString(parseResult.error())); + EXPECT_EQ(std::wstring(L"noname.j2tpl:1:4: error: Template environment doesn't set\n{% extends 'module' %}\n---^-------"), ErrorToString(parseResult.error())); parseResult = tpl1.Load(L"{% include 'module' %}"); ASSERT_FALSE(parseResult.has_value()); diff --git a/test/filters_test.cpp b/test/filters_test.cpp index aa67bacb..e6c04809 100644 --- a/test/filters_test.cpp +++ b/test/filters_test.cpp @@ -238,10 +238,10 @@ INSTANTIATE_TEST_SUITE_P(Map, ListIteratorTest, ::testing::Values( InputOutputPair{"reflectedList | map(attribute='intValue', default='99')", "0, 1, 2, 3, 4, 5, 6, 7, 8, 9"}, InputOutputPair{"reflectedList | map(attribute='intEvenValue')", "0, , 2, , 4, , 6, , 8, "}, - InputOutputPair{"reflectedList | map(attribute='intEvenValue', default='99')", - "0, 99, 2, 99, 4, 99, 6, 99, 8, 99"}, + InputOutputPair{"reflectedList | map(attribute='intEvenValue', default='99')", + "0, 99, 2, 99, 4, 99, 6, 99, 8, 99"}, InputOutputPair{"reflectedList | map(attribute='nonexistent')", ", , , , , , , , , "}, - InputOutputPair{"reflectedList | map(attribute='nonexistent', default='99')", + InputOutputPair{"reflectedList | map(attribute='nonexistent', default='99')", "99, 99, 99, 99, 99, 99, 99, 99, 99, 99"}, InputOutputPair{"[[0, 1], [1, 2], [2, 3], [3, 4]] | map('first')", "0, 1, 2, 3"}, InputOutputPair{"[['str1', 'Str2'], ['str2', 'Str3'], ['str3', 'Str4'], ['str4', 'Str5']] | map('min')", @@ -586,7 +586,7 @@ struct XmlAttr : ::testing::Test using String = std::basic_string; using Regex = std::basic_regex; using RegexTokenIterator = std::regex_token_iterator; - + AttributeSet result; const Regex pattern(ConvertString(std::string("(\\S+=[\"].*?[\"])"))); std::copy(RegexTokenIterator(attributeString.begin(), attributeString.end(), pattern, 0), @@ -621,14 +621,14 @@ struct XmlAttr : ::testing::Test TemplateType tpl; if (!tpl.Load(source)) return false; - + return tpl.RenderAsString(params).has_value(); } void PerformNegativeTest(const std::string& source, const jinja2::ValuesMap& params = {}) { EXPECT_FALSE(ParseTemplate