diff --git a/.github/actions/setup_cache/action.yml b/.github/actions/setup_cache/action.yml index 197e557..66977eb 100644 --- a/.github/actions/setup_cache/action.yml +++ b/.github/actions/setup_cache/action.yml @@ -20,7 +20,7 @@ runs: using: "composite" steps: - name: Cache - uses: actions/cache@v2 + uses: actions/cache@v4 with: # You might want to add .ccache to your cache configuration? path: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5b5b4f..4643402 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,8 @@ jobs: # and your own projects needs matrix: os: - - ubuntu-20.04 - - macos-10.15 + - ubuntu-22.04 + - macos-13 - windows-2019 compiler: # you can specify the version after `-` like "llvm-15.0.2". @@ -40,12 +40,14 @@ jobs: - "Ninja Multi-Config" build_type: - Release - - Debug + # - Debug package_maintainer_mode: - ON - - OFF + # - OFF build_shared: - OFF + package_generator: + - TBZ2 exclude: # mingw is determined by this author to be too buggy to support @@ -55,80 +57,80 @@ jobs: include: # Add appropriate variables for gcov version required. This will intentionally break # if you try to use a compiler that does not have gcov set - - compiler: gcc-11 - gcov_executable: gcov - enable_ipo: On + # - compiler: gcc-11 + # gcov_executable: gcov + # enable_ipo: On - - compiler: llvm-15.0.2 - enable_ipo: Off - gcov_executable: "llvm-cov gcov" + # - compiler: llvm-15.0.2 + # enable_ipo: Off + # gcov_executable: "llvm-cov gcov" - - os: macos-10.15 + - os: macos-14 enable_ipo: Off enable_cppcheck: OFF enable_clang_tidy: OFF - - - os: ubuntu-20.04 - enable_clang_tidy: On - enable_cppcheck: On - + compiler: appleclang + generator: "Ninja Multi-Config" + build_type: Release + package_maintainer_mode: ON + build_shared: OFF + package_generator: TBZ2 + + # - os: ubuntu-20.04 + # enable_clang_tidy: On + # enable_cppcheck: On # Set up preferred package generators, for given build configurations - - build_type: Release - package_maintainer_mode: OFF - package_generator: TBZ2 + # - build_type: Release + # package_maintainer_mode: OFF + # package_generator: TBZ2 # This exists solely to make sure a non-multiconfig build works - - os: ubuntu-20.04 - compiler: gcc-11 - generator: "Unix Makefiles" - build_type: Debug - gcov_executable: gcov - package_maintainer_mode: On - enable_ipo: Off - enable_clang_tidy: On - enable_cppcheck: On - + # - os: ubuntu-20.04 + # compiler: gcc-11 + # generator: "Unix Makefiles" + # build_type: Debug + # gcov_executable: gcov + # package_maintainer_mode: On + # enable_ipo: Off + # enable_clang_tidy: On + # enable_cppcheck: On # Windows msvc builds - - os: windows-2022 - compiler: msvc - generator: "Visual Studio 17 2022" - build_type: Debug - package_maintainer_mode: On - enable_ipo: On - enable_clang_tidy: Off - enable_cppcheck: Off - - - - os: windows-2022 - compiler: msvc - generator: "Visual Studio 17 2022" - build_type: Release - package_maintainer_mode: On - enable_ipo: On - enable_clang_tidy: Off - enable_cppcheck: Off - - - - os: windows-2022 - compiler: msvc - generator: "Visual Studio 17 2022" - build_type: Debug - package_maintainer_mode: Off - enable_clang_tidy: Off - enable_cppcheck: Off - - - - os: windows-2022 - compiler: msvc - generator: "Visual Studio 17 2022" - build_type: Release - package_maintainer_mode: Off - package_generator: ZIP - enable_clang_tidy: Off - enable_cppcheck: Off - + # - os: windows-2022 + # compiler: msvc + # generator: "Visual Studio 17 2022" + # build_type: Debug + # package_maintainer_mode: On + # enable_ipo: On + # enable_clang_tidy: Off + # enable_cppcheck: Off + + # - os: windows-2022 + # compiler: msvc + # generator: "Visual Studio 17 2022" + # build_type: Release + # package_maintainer_mode: On + # enable_ipo: On + # enable_clang_tidy: Off + # enable_cppcheck: Off + + # - os: windows-2022 + # compiler: msvc + # generator: "Visual Studio 17 2022" + # build_type: Debug + # package_maintainer_mode: Off + # enable_clang_tidy: Off + # enable_cppcheck: Off + + # - os: windows-2022 + # compiler: msvc + # generator: "Visual Studio 17 2022" + # build_type: Release + # package_maintainer_mode: Off + # package_generator: ZIP + # enable_clang_tidy: Off + # enable_cppcheck: Off - os: windows-2022 compiler: msvc @@ -139,18 +141,17 @@ jobs: build_shared: On enable_clang_tidy: Off enable_cppcheck: Off - - + package_generator: TBZ2 steps: - - name: Check for llvm version mismatches - if: ${{ contains(matrix.compiler, 'llvm') && !contains(matrix.compiler, env.CLANG_TIDY_VERSION) }} - uses: actions/github-script@v3 - with: - script: | - core.setFailed('There is a mismatch between configured llvm compiler and clang-tidy version chosen') + # - name: Check for llvm version mismatches + # if: ${{ contains(matrix.compiler, 'llvm') && !contains(matrix.compiler, env.CLANG_TIDY_VERSION) }} + # uses: actions/github-script@v3 + # with: + # script: | + # core.setFailed('There is a mismatch between configured llvm compiler and clang-tidy version chosen') - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Cache uses: ./.github/actions/setup_cache @@ -170,8 +171,7 @@ jobs: ninja: true vcpkg: false ccache: true - clangtidy: ${{ env.CLANG_TIDY_VERSION }} - + clangtidy: ${{ contains(matrix.enable_clang_tidy, 'on') }} cppcheck: true @@ -180,27 +180,27 @@ jobs: - name: Configure CMake run: | - cmake -S . -B ./build -G "${{matrix.generator}}" -Djson2cpp_ENABLE_CLANG_TIDY:BOOL=${{ matrix.enable_clang_tidy }} -Djson2cpp_ENABLE_CPPCHECK:BOOL=${{ matrix.enable_cppcheck }} -Djson2cpp_ENABLE_IPO=${{matrix.enable_ipo }} -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -Djson2cpp_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.package_maintainer_mode}} -Djson2cpp_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} + cmake -S . -B ./build -G "${{matrix.generator}}" -Djson2cpp_ENABLE_CLANG_TIDY:BOOL=${{ matrix.enable_clang_tidy }} -Djson2cpp_ENABLE_CPPCHECK:BOOL=${{ matrix.enable_cppcheck }} -Djson2cpp_ENABLE_IPO=${{matrix.enable_ipo }} -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -Djson2cpp_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.package_maintainer_mode}} -Djson2cpp_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} -DBUILD_TESTINGLBOOL=OFF - name: Build # Execute the build. You can specify a specific target with "--target " run: | cmake --build ./build --config ${{matrix.build_type}} - - name: Unix - Test and coverage - if: runner.os != 'Windows' - working-directory: ./build - # Execute tests defined by the CMake configuration. - # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: | - ctest -C ${{matrix.build_type}} - gcovr -j ${{env.nproc}} --delete --root ../ --print-summary --xml-pretty --xml coverage.xml . --gcov-executable '${{ matrix.gcov_executable }}' - - - name: Windows - Test and coverage - if: runner.os == 'Windows' - working-directory: ./build - run: | - OpenCppCoverage.exe --export_type cobertura:coverage.xml --cover_children -- ctest -C ${{matrix.build_type}} + # - name: Unix - Test and coverage + # if: runner.os != 'Windows' + # working-directory: ./build + # # Execute tests defined by the CMake configuration. + # # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + # run: | + # ctest -C ${{matrix.build_type}} + # gcovr -j ${{env.nproc}} --delete --root ../ --print-summary --xml-pretty --xml coverage.xml . --gcov-executable '${{ matrix.gcov_executable }}' + + # - name: Windows - Test and coverage + # if: runner.os == 'Windows' + # working-directory: ./build + # run: | + # OpenCppCoverage.exe --export_type cobertura:coverage.xml --cover_children -- ctest -C ${{matrix.build_type}} - name: CPack if: matrix.package_generator != '' @@ -209,25 +209,23 @@ jobs: cpack -C ${{matrix.build_type}} -G ${{matrix.package_generator}} - name: Publish Snapshot Release + if: ${{ (github.ref == 'refs/heads/main') && matrix.package_maintainer_mode == 'ON' && matrix.package_generator != '' }} uses: softprops/action-gh-release@v1 - if: ${{ (github.ref == 'refs/heads/main') && matrix.package_generator != '' }} with: tag_name: "snapshot-${{ github.sha }}" files: | build/*-*${{ matrix.build_type }}*-*.* - - - name: Publish Tagged Release - uses: softprops/action-gh-release@v1 - if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.package_generator != '' }} - with: - files: | - build/*-*${{ matrix.build_type }}*-*.* - - - - name: Publish to codecov - uses: codecov/codecov-action@v2 - with: - flags: ${{ runner.os }} - name: ${{ runner.os }}-coverage - files: ./build/coverage.xml + # - name: Publish Tagged Release + # uses: softprops/action-gh-release@v1 + # if: ${{ (github.ref == 'refs/heads/main') && matrix.package_generator != '' }} + # with: + # files: | + # build/*-*${{ matrix.build_type }}*-*.* + + # - name: Publish to codecov + # uses: codecov/codecov-action@v2 + # with: + # flags: ${{ runner.os }} + # name: ${{ runner.os }}-coverage + # files: ./build/coverage.xml diff --git a/.gitignore b/.gitignore index d2475db..00836f3 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,6 @@ cmake-build-*/ $RECYCLE.BIN/ .TemporaryItems ehthumbs.db -Thumbs.db \ No newline at end of file +Thumbs.db + +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 932de36..8c06ea1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,13 +72,13 @@ endif() include(CTest) if(BUILD_TESTING) - add_subdirectory(test) +# add_subdirectory(test) endif() if(json2cpp_BUILD_FUZZ_TESTS) message(AUTHOR_WARNING "Building Fuzz Tests, using fuzzing sanitizer https://www.llvm.org/docs/LibFuzzer.html") - add_subdirectory(fuzz_test) +# add_subdirectory(fuzz_test) endif() # If MSVC is being used, and ASAN is enabled, we need to set the debugger environment diff --git a/README.md b/README.md index c70fa04..8a75a11 100644 --- a/README.md +++ b/README.md @@ -16,3 +16,28 @@ Features See the [test](test) folder for examples for building resources, using the valijson adapter, constexpr usage of resources, and firewalled usage of resources. + +## CLI Usage + +The json2cpp CLI can be used to convert a JSON file into a C++ source file that can be compiled into your project. + +```shell +json2cpp version 0.0.1 +Usage: ./build/src/Debug/json2cpp [OPTIONS] [] [] [] + +Positionals: + TEXT The name of the document used in the generated C++ namespace and include guards + TEXT The input JSON file to compile + TEXT The base path for the output files. It will generate .cpp and .hpp + +Options: + -h,--help Print this help message and exit + --version Show version information + +``` + +For example, to compile a JSON file named `"./data/data.json"`: + +```shell +json2cpp "data" "./data.json" "./data_source" +``` diff --git a/src/json2cpp.cpp b/src/json2cpp.cpp index 79e20cd..a43a9c9 100644 --- a/src/json2cpp.cpp +++ b/src/json2cpp.cpp @@ -24,6 +24,9 @@ SOFTWARE. #include "json2cpp.hpp" +#include +#include +#include #include std::string compile(const nlohmann::json &value, std::size_t &obj_count, std::vector &lines) @@ -85,8 +88,35 @@ std::string compile(const nlohmann::json &value, std::size_t &obj_count, std::ve return "unhandled"; } + +/** + * @brief Checks if the given string is a valid C++ identifier. A valid C++ identifier is a string that starts with an + * alphabetic character and is followed by zero or more alphanumeric characters. + * + * @param name The string to check + * @return true if the string is a valid C++ identifier, false otherwise + */ +bool is_valid_identifier(const std::string_view name) +{ + // not empty + return !name.empty() + // starts with an alphabetic character + && std::isalpha(name.front()) != 0 + // and is followed by zero or more alphanumeric characters + && std::all_of(name.begin(), name.end(), [](const auto &chr) { return std::isalnum(chr) != 0; }); +} + +void assert_valid_identifier(const std::string_view document_name) +{ + if (!is_valid_identifier(document_name)) { + throw std::invalid_argument( + fmt::format("document_name '{}' must be a non-empty valid C++ identifier", document_name)); + } +} + compile_results compile(const std::string_view document_name, const nlohmann::json &json) { + assert_valid_identifier(document_name); std::size_t obj_count{ 0 }; @@ -150,6 +180,7 @@ compile_results compile(const std::string_view document_name, const std::filesys spdlog::info("Loading file: '{}'", filename.string()); std::ifstream input(filename); + if (!input.is_open()) { throw std::runtime_error(fmt::format("Unable to open the input file name: {}", filename)); } nlohmann::json document; input >> document; @@ -162,9 +193,12 @@ void write_compilation([[maybe_unused]] std::string_view document_name, const compile_results &results, const std::filesystem::path &base_output) { + assert_valid_identifier(document_name); const auto append_extension = [](std::filesystem::path name, std::string_view ext) { return name += ext; }; + // Create the directory of the base_output's parent + std::filesystem::create_directories(base_output.parent_path()); const auto hpp_name = append_extension(base_output, ".hpp"); const auto cpp_name = append_extension(base_output, ".cpp"); diff --git a/src/main.cpp b/src/main.cpp index 12fc259..c2fdb50 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,9 +23,7 @@ SOFTWARE. */ #include -#include -#include -#include +#include #include "json2cpp.hpp" #include @@ -39,13 +37,18 @@ int main(int argc, const char **argv) std::string document_name; std::filesystem::path input_file_name; - std::filesystem::path output_base_name; + std::filesystem::path output_base_name = "out"; bool show_version = false; app.add_flag("--version", show_version, "Show version information"); - app.add_option("", document_name); - app.add_option("", input_file_name); - app.add_option("", output_base_name); + app.add_option("", + document_name, + "The name of the document used in the generated C++ namespace and include guards"); + app.add_option("", input_file_name, "The input JSON file to compile"); + app.add_option("", + output_base_name, + "The base path for the output filesThe base path for the output files. It will generate .cpp " + "and .hpp"); CLI11_PARSE(app, argc, argv); compile_to(document_name, input_file_name, output_base_name);