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/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);