From c021a0d8b13094dd62544612643105546fd23191 Mon Sep 17 00:00:00 2001 From: Jon-Michael Hartway Date: Mon, 15 Jan 2024 21:37:52 -0500 Subject: [PATCH] completely overhauled the template management system --- include/Frate/Frate.hpp | 20 +- include/Frate/Lua/Exceptions.hpp | 11 + include/Frate/{ => Lua}/LuaAPI.hpp | 15 +- include/Frate/Lua/TemplateEnvironment.hpp | 201 ++++++++++++++ include/Frate/Lua/Utils.hpp | 11 + include/Frate/Project/Config.hpp | 4 +- include/Frate/Project/Exceptions.hpp | 35 +++ include/Frate/Project/InstalledTemplate.hpp | 103 ++++--- include/Frate/Project/Template.hpp | 10 +- include/Frate/Project/TemplateManager.hpp | 25 +- include/Frate/Project/TemplateMeta.hpp | 83 +++++- include/Frate/Utils/FileFilter.hpp | 37 ++- include/Frate/Utils/General.hpp | 4 - include/Frate/Utils/Logging.hpp | 2 +- src/Command/Helpers/Interface.cpp | 1 + src/Command/Helpers/Project.cpp | 2 +- src/Command/Helpers/Template.cpp | 8 +- src/Command/Helpers/TemplateMeta.cpp | 259 +++++++++++++++++ src/Generators/CMakeTemplates.cpp | 8 +- src/Generators/DockerTemplate.cpp | 8 +- .../ProjectGenerator/InitializeLua.cpp | 8 +- .../ProjectGenerator/LoadTemplateConfig.cpp | 4 +- .../ProjectGenerator/ProjectGenerator.cpp | 107 +------ .../ProjectGenerator/TemplatePrompts.cpp | 6 +- src/Lua/API.cpp | 47 +++- src/Lua/FetchJson.cpp | 5 +- src/Lua/FetchText.cpp | 4 +- src/Lua/Format.cpp | 6 +- src/Lua/GetOS.cpp | 6 +- src/Lua/GetPath.cpp | 6 +- src/Lua/GetPathsRecurse.cpp | 6 +- src/Lua/PrintTable.cpp | 10 +- src/Lua/RegisterProject.cpp | 6 +- src/Lua/TemplateEnvironment.cpp | 262 ++++++++++++++++++ src/Lua/Utils.cpp | 80 +++--- src/Project/InstalledTemplate.cpp | 152 ++++++++-- src/Project/TemplateManager.cpp | 195 ++++--------- src/Utils/ConfigManager/Config.cpp | 2 +- src/Utils/Debug.cpp | 7 +- src/Utils/FileFilter.cpp | 89 ++++-- 40 files changed, 1386 insertions(+), 469 deletions(-) create mode 100644 include/Frate/Lua/Exceptions.hpp rename include/Frate/{ => Lua}/LuaAPI.hpp (85%) create mode 100644 include/Frate/Lua/TemplateEnvironment.hpp create mode 100644 include/Frate/Lua/Utils.hpp create mode 100644 include/Frate/Project/Exceptions.hpp create mode 100644 src/Lua/TemplateEnvironment.cpp diff --git a/include/Frate/Frate.hpp b/include/Frate/Frate.hpp index 18d2628..7dc6c48 100644 --- a/include/Frate/Frate.hpp +++ b/include/Frate/Frate.hpp @@ -1,4 +1,22 @@ #pragma once +#include "Frate/Utils/CLI.hpp" #include -namespace Frate {} // namespace Frate +namespace Frate { + class FrateException : public std::exception { + protected: + std::string message; + + public: + FrateException(const std::string message) + : message(Utils::CLI::Ansi::RED + message + Utils::CLI::Ansi::RESET) { + + + } + + [[nodiscard]] const char *what() const noexcept override { + return (message.c_str()); + }; + }; + +} // namespace Fra diff --git a/include/Frate/Lua/Exceptions.hpp b/include/Frate/Lua/Exceptions.hpp new file mode 100644 index 0000000..c17594a --- /dev/null +++ b/include/Frate/Lua/Exceptions.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "Frate/Frate.hpp" + +namespace Frate::Lua { + + class LuaException : public FrateException { + public: + LuaException(const std::string &message) : FrateException(message) {} + }; + +} // namespace Frate::Lua diff --git a/include/Frate/LuaAPI.hpp b/include/Frate/Lua/LuaAPI.hpp similarity index 85% rename from include/Frate/LuaAPI.hpp rename to include/Frate/Lua/LuaAPI.hpp index 9fd18f7..842ceb0 100644 --- a/include/Frate/LuaAPI.hpp +++ b/include/Frate/Lua/LuaAPI.hpp @@ -3,11 +3,11 @@ #include "inja.hpp" #include #include -#include #include #include -namespace Frate::LuaAPI { +namespace Frate::Lua { + using nlohmann::json; using Project::Config; using std::filesystem::path; @@ -28,32 +28,35 @@ namespace Frate::LuaAPI { static sol::table fetch_json(const std::string &url, sol::this_state lua); }; - sol::table to_table(nlohmann::json json, sol::state_view &lua); - nlohmann::json from_table(sol::table in_table); - /* * Registers the project scripts with the project that is specifed */ + [[deprecated("Use TemplateEnvironment")]] bool registerProjectScripts(inja::Environment &env, sol::state &lua, path script_path, std::shared_ptr project); + /* * Registers the project with the user types for the project struct */ + [[deprecated("Use TemplateEnvironment")]] bool registerProject(sol::state &lua, std::shared_ptr pro); /* * Registers api functions for the lua state * and initializes the lua state */ + [[deprecated("Use TemplateEnvironment")]] void registerAPI(sol::state &lua); /* * Runs __init__ scripts */ + [[deprecated("Use TemplateEnvironment")]] bool initScripts(sol::state &lua, std::shared_ptr project); /* * Runs __post__ scripts */ + [[deprecated("Use TemplateEnvironment")]] bool postScripts(sol::state &lua, std::shared_ptr project); -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/include/Frate/Lua/TemplateEnvironment.hpp b/include/Frate/Lua/TemplateEnvironment.hpp new file mode 100644 index 0000000..ff13cba --- /dev/null +++ b/include/Frate/Lua/TemplateEnvironment.hpp @@ -0,0 +1,201 @@ +#pragma once +#include "Frate/Project/Config.hpp" +#include "Frate/Utils/Logging.hpp" +#include "inja.hpp" +#include +#include +#include +#include + +namespace Frate::Lua { + + class TemplateEnvironmentException : public FrateException { + public: + explicit TemplateEnvironmentException(const std::string &message) + : FrateException(message) {} + }; + /* + * This class is responsible for handling the lua template environent, + * managing macros, pre and post scripts, and templating files. It is designed + * in such a way that allows complete control of over the template render + * pipeline. + * To use this, start by registering all of your scripts using your method of + * choice, then use templateFile to render your file to the output path + */ + class TemplateEnvironment { + private: + std::shared_ptr lua; + inja::Environment env; + std::unordered_map macro_scripts; + std::unordered_map init_scripts; + std::unordered_map post_scripts; + std::shared_ptr pro; + + void register_user_types(); + void register_frate_api(); + void register_inja_function(std::string name, std::string lua_script_text); + + public: + /* + * A project config is required to register the project with the lua + * environment + * @param pro: The project config + * @throws LuaException: If there is an error while registering the project + */ + TemplateEnvironment(std::shared_ptr pro) + : pro(pro) { + + lua = std::make_shared(); + + macro_scripts = std::unordered_map(); + init_scripts = std::unordered_map(); + post_scripts = std::unordered_map(); + + if(pro == nullptr){ + throw TemplateEnvironmentException("Project is null on construction"); + } + + try { + Utils::verbose << "Registering frate api" << std::endl; + register_frate_api(); + + } catch (std::exception &e) { + Utils::error << e.what() << std::endl; + throw TemplateEnvironmentException("Error registering frate api"); + } + + try { + Utils::verbose << "Registering user types" << std::endl; + register_user_types(); + + } catch (std::exception &e) { + Utils::error << e.what() << std::endl; + throw TemplateEnvironmentException("Error registering user types"); + } + }; + + ~TemplateEnvironment() = default; + /* + * Registers a new macro script the name of the script is the identifier + * @note Using relativePathToNamespace to get a namespaced script name is + * advised + * @param name: The name of the script + * @param script_text: The text of the script + * @throws LuaException: If there is an error while registering the script + */ + void registerMacroScript(std::string name, std::string script_text); + /* + * Registers a new init script the name of the script is the identifier + * currently the name is not used for anything + * @param name: The name of the script + * @param script_text: The text of the script + * @throws LuaException: If there is an error while registering the script + */ + void registerInitScript(std::string name, std::string script_text); + /* + * Registers a new post script the name of the script is the identifier + * currently the name is not used for anything + * @param name: The name of the script + * @param script_text: The text of the script + * @throws LuaException: If there is an error while registering the script + */ + void registerPostScript(std::string name, std::string script_text); + + /* + * @param input_file: The path to the input .inja file + * @param output_file: The path to the output file + * @throws LuaException: If there is an error while templating the file + * @throws std::exception: If there is an error while writing the file + */ + void templateFile(std::filesystem::path input_file, + std::filesystem::path output_file); + /* + * Runs __init__ scripts assuming they have been registered + * @throws LuaException: If there is an error while running the script + */ + void runInitScripts(); + /* + * Runs __post__ scripts assuming they have been registered + * @throws LuaException: If there is an error while running the script + */ + void runPostScripts(); + + /* + * Providing this a root path and a file path it will return the dot + * notation version of the path while also removing the extension + * @param root_path: The root path which you would like to base the relative + * path off of + * @param file_path: The file path which you would like to convert to dot + * notation + * @return: The dot notation version of the file path + * @example: relativePathToNamespace("/home/user/project", + * "/home/user/project/src/main.cpp") -> "src.main" + */ + static std::string relativePathToNamespace(std::filesystem::path root_path, + std::filesystem::path file_path); + /* + * Providing this a file path it will return the dot notation version of the + * path while also removing the extension + * @param file_path: The file path which you would like to convert to dot + * notation + * @return: The dot notation version of the file path + * @example: relativePathToNamespace("src/main.cpp") -> "src.main" + */ + static std::string + relativePathToNamespace(std::filesystem::path relative_path); + + + // Getters + + [[nodiscard]] const std::shared_ptr &getProjectConfig() const { + return pro; + } + + [[nodiscard]] const std::unordered_map &getMacroScripts() const { + return macro_scripts; + } + + [[nodiscard]] const std::unordered_map &getInitScripts() const { + return init_scripts; + } + + [[nodiscard]] const std::unordered_map &getPostScripts() const { + return post_scripts; + } + + [[nodiscard]] const std::shared_ptr &getLuaState() const { + return lua; + } + + [[nodiscard]] const inja::Environment &getInjaEnv() const { + return env; + } + + //Setters + + void setProjectConfig(const std::shared_ptr &pro) { + TemplateEnvironment::pro = pro; + } + + // Display + + friend std::ostream &operator<<(std::ostream &os, + const TemplateEnvironment &environment) { + + for(auto [name, script] : environment.macro_scripts){ + os << "Macro Script: " << name << std::endl; + os << script << std::endl; + } + for(auto [name, script] : environment.init_scripts){ + os << "Init Script: " << name << std::endl; + os << script << std::endl; + } + for(auto [name, script] : environment.post_scripts){ + os << "Post Script: " << name << std::endl; + os << script << std::endl; + } + return os; + } + + }; +} // namespace Frate::Lua diff --git a/include/Frate/Lua/Utils.hpp b/include/Frate/Lua/Utils.hpp new file mode 100644 index 0000000..2282da7 --- /dev/null +++ b/include/Frate/Lua/Utils.hpp @@ -0,0 +1,11 @@ +#pragma once +#include +#include +namespace Frate::Lua { + + sol::table to_table(nlohmann::json in_json, sol::state_view &lua); + nlohmann::json from_table(sol::table in_table); + bool is_table_array(const sol::table &table); + + +} diff --git a/include/Frate/Project/Config.hpp b/include/Frate/Project/Config.hpp index 5222771..8928ef0 100644 --- a/include/Frate/Project/Config.hpp +++ b/include/Frate/Project/Config.hpp @@ -21,6 +21,8 @@ namespace Frate::Project { bool loaded_json{false}; std::filesystem::path template_path; TemplateMeta current_template; + std::vector template_files; + std::vector script_files; std::string name; std::string description; std::string type{""}; @@ -74,7 +76,7 @@ namespace Frate::Project { std::unordered_map global{}; friend void from_json(const json &json_obj, Config &pro); friend void to_json(json &json_obj, const Config &pro); - void fromTemplate(Template &_template); + void fromTemplate(TemplateConfig &_template); }; } // namespace Frate::Project diff --git a/include/Frate/Project/Exceptions.hpp b/include/Frate/Project/Exceptions.hpp new file mode 100644 index 0000000..e97e70e --- /dev/null +++ b/include/Frate/Project/Exceptions.hpp @@ -0,0 +1,35 @@ +#include "Frate/Frate.hpp" +#include + +namespace Frate::Project { + + class TemplateException : public FrateException { + public: + TemplateException(const std::string &message) : FrateException(message) {} + }; + + class TemplateNotInstalled : public TemplateException { + public: + TemplateNotInstalled(const std::string &message) + : TemplateException(message) {} + }; + + class TemplateIndexNotLoaded : public TemplateException { + public: + TemplateIndexNotLoaded(const std::string &message) + : TemplateException(message) {} + }; + + class TemplateConfigNotFound : public TemplateException { + public: + TemplateConfigNotFound(const std::string &message) + : TemplateException(message) {} + }; + + class TemplateFileMapEmpty : public TemplateException { + public: + TemplateFileMapEmpty(const std::string &message) + : TemplateException(message) {} + }; + +} // namespace Frate::Project diff --git a/include/Frate/Project/InstalledTemplate.hpp b/include/Frate/Project/InstalledTemplate.hpp index 496e41d..99a01be 100644 --- a/include/Frate/Project/InstalledTemplate.hpp +++ b/include/Frate/Project/InstalledTemplate.hpp @@ -2,51 +2,68 @@ #include "Frate/System/GitCommit.hpp" #include #include +#include #include namespace Frate::Project { - class InstalledTemplate { - private: - // This will be the latest commit hash, if getLatestCommit() is called more - // than once, it will return the cached value - System::GitCommit latest; - std::string name; - // Git url to template - std::string git; - // The latest commit hash - std::vector commits; - - public: - InstalledTemplate() = default; - - // Returns the latest commit hash - System::GitCommit &getLatestCommit(); - /* - * Builds the template into the the project path - * and transfers the template config to the project config - * @param config the project config to build the template into - */ - void build(std::shared_ptr config); - /* - * Refreshes the template by overwriting the CMakeLists.txt file - * @param config the project config to refresh the template into - */ - void refresh(std::shared_ptr config); - - friend void to_json(nlohmann::json &json_obj, - const InstalledTemplate &_template); - friend void from_json(const nlohmann::json &json_obj, - InstalledTemplate &_template); - friend std::ostream &operator<<(std::ostream &os_stream, - const InstalledTemplate &temp); - - [[nodiscard]] std::string &getName() { return name; } - - [[nodiscard]] std::string &getGit() { return git; } - - [[nodiscard]] std::vector &getCommits() { - return commits; - } - }; + // class InstalledTemplate { + // private: + // /* + // * This will be the latest commit hash, if getLatestCommit() is called + // more + // * than once, it will return the cached value + // */ + // System::GitCommit latest; + // + // std::string name; + // // Git url to template + // std::string git; + // // The latest commit hash + // std::vector commits; + // + // /* + // * Returns true if the template is installed + // * @param template_hash the hash of the template to check + // */ + // bool validate_installed(std::string &template_hash); + // + // TemplateConfig get_template_info(std::string &template_hash); + // + // void render_template(std::shared_ptr config); + // + // public: + // InstalledTemplate() = default; + // + // // Returns the latest commit hash + // System::GitCommit &getLatestCommit(); + // /* + // * Builds the template into the the project path + // * and transfers the template config to the project config + // * WARNING this will overwrite everything in the project path + // * @param config the project config to build the template into + // */ + // void build(std::shared_ptr config); + // /* + // * Refreshes the template by rendering the CMakeLists.txt into the + // project + // * @param config the project config to refresh the template into + // */ + // void refresh(std::shared_ptr config); + // + // friend void to_json(nlohmann::json &json_obj, + // const InstalledTemplate &_template); + // friend void from_json(const nlohmann::json &json_obj, + // InstalledTemplate &_template); + // friend std::ostream &operator<<(std::ostream &os_stream, + // const InstalledTemplate &temp); + // + // [[nodiscard]] std::string &getName() { return name; } + // + // [[nodiscard]] std::string &getGit() { return git; } + // + // [[nodiscard]] std::vector &getCommits() { + // return commits; + // } + // }; } // namespace Frate::Project diff --git a/include/Frate/Project/Template.hpp b/include/Frate/Project/Template.hpp index ec6e26c..0d48325 100644 --- a/include/Frate/Project/Template.hpp +++ b/include/Frate/Project/Template.hpp @@ -12,9 +12,9 @@ namespace Frate::Project { using nlohmann::json; class Config; - class Template { + class TemplateConfig { public: - Template(); + TemplateConfig(); std::string name; std::string description; std::string version; @@ -34,8 +34,8 @@ namespace Frate::Project { std::vector keywords{}; std::unordered_map prompts{}; std::unordered_map global{}; - friend void from_json(const json &json_obj, Template &temp); - friend void to_json(json &json_obj, const Template &temp); - friend void to_project(Config &pro, const Template &temp); + friend void from_json(const json &json_obj, TemplateConfig &temp); + friend void to_json(json &json_obj, const TemplateConfig &temp); + friend void to_project(Config &pro, const TemplateConfig &temp); }; } // namespace Frate::Project diff --git a/include/Frate/Project/TemplateManager.hpp b/include/Frate/Project/TemplateManager.hpp index b50236e..3fc5cd1 100644 --- a/include/Frate/Project/TemplateManager.hpp +++ b/include/Frate/Project/TemplateManager.hpp @@ -9,7 +9,13 @@ namespace Frate::Project { private: std::vector index; - std::vector installed; + + std::unordered_map> + installed; + + std::unordered_map latest_hashes; + bool index_loaded = false; /* * Loads the index from the github repo @@ -21,7 +27,8 @@ namespace Frate::Project { * @param hash the hash of the template if it is installed * @return true if the template is installed */ - Project::InstalledTemplate &find_template(const std::string &name); + Project::TemplateMeta &find_template(const std::string &name, + std::string &hash); /* * Checks if a template specifed is installed * nice helper function @@ -66,16 +73,18 @@ namespace Frate::Project { * @param name the name of the template to create * @param hash the hash of the template to create */ - std::filesystem::path - makeTemplate(const std::filesystem::path &override_path, - const std::string &name, std::string &hash); - void save(); - void load(); + // std::filesystem::path makeTemplate(const std::filesystem::path + // &override_path,const std::string &name, std::string &hash); + + void save(); // Saves this object to the config file + void load(); // Loads this object from the config file /* * Grabs the current installed templates from the config file * @return a vector of TemplateMeta objects */ - std::vector &getInstalled(); + std::unordered_map> & + getInstalled(); /* * Grabs the current index from the the github repo, if called more than * once then it will pull the index from memory diff --git a/include/Frate/Project/TemplateMeta.hpp b/include/Frate/Project/TemplateMeta.hpp index a939cef..e238433 100644 --- a/include/Frate/Project/TemplateMeta.hpp +++ b/include/Frate/Project/TemplateMeta.hpp @@ -1,19 +1,96 @@ #pragma once +#include "Frate/Constants.hpp" #include #include +#include +namespace Frate::Lua { + class TemplateEnvironment; +} namespace Frate::Project { + class Config; + class TemplateMeta { - public: - TemplateMeta() = default; - TemplateMeta(const nlohmann::json &json_obj); + private: std::string name; std::string description; std::string hash; std::string git; + std::filesystem::path install_path; + std::shared_ptr env; + bool scripts_loaded{false}; + /* + * Contains the mapping of the template and the relative path to the project + * path + */ + std::unordered_map file_map; + + std::vector non_template_files; + + + + void load_scripts(); + /* + * Middleware function to render .inja files in the project path + * after it's rendered the template files then it will delete the .inja + * files + * @param config the project config to render the template into + */ + void render(std::shared_ptr config); + + void install_cpm(std::shared_ptr config); + + public: + TemplateMeta(); + TemplateMeta(const nlohmann::json &json_obj); friend void from_json(const nlohmann::json &json_obj, TemplateMeta &temp); friend void to_json(nlohmann::json &json_obj, const TemplateMeta &temp); friend std::ostream &operator<<(std::ostream &os_stream, const TemplateMeta &temp); + /* + * Builds the template into the the project path + * and transfers the template config to the project config + * WARNING this will overwrite everything in the project path + * @param config the project config to build the template into + */ + void build(std::shared_ptr config); + /* + * Refreshes the template by rendering the CMakeLists.txt into the + project + * @param config the project config to refresh the template into + */ + void refresh(std::shared_ptr config); + + // Getters + + std::vector &getTemplateFiles(); + + std::string &getName() { return this->name; } + + std::string &getDescription() { return this->description; } + + std::string &getHash() { return this->hash; } + + std::string &getGit() { return this->git; } + + std::filesystem::path &getInstallPath() { return this->install_path; } + + void setName(std::string name) { + this->install_path = + Constants::INSTALLED_TEMPLATE_PATH / name / this->hash; + this->name = name; + } + + void setDescription(std::string description) { + this->description = description; + } + + void setHash(std::string hash) { + this->install_path = + Constants::INSTALLED_TEMPLATE_PATH / this->name / hash; + this->hash = hash; + } + + void setGit(std::string git) { this->git = git; } }; } // namespace Frate::Project diff --git a/include/Frate/Utils/FileFilter.hpp b/include/Frate/Utils/FileFilter.hpp index 6e915f5..3effc8f 100644 --- a/include/Frate/Utils/FileFilter.hpp +++ b/include/Frate/Utils/FileFilter.hpp @@ -1,36 +1,55 @@ +#include #include #include namespace Frate::Utils { + class FileFilterException : public FrateException { + public: + FileFilterException(const std::string &message) : FrateException(message) {} + }; class FileFilter { private: - std::vector extensions; - std::vector prefixes; - std::vector paths; + std::vector filter_extensions; + std::vector filter_prefixes; + std::vector filter_files; + std::vector filter_dirs; std::filesystem::path root_path; + std::vector paths_to_filter; + std::vector filtered_paths; + + bool paths_generated = false; + bool is_valid(const std::filesystem::path &path); + void gen_paths(); + public: - FileFilter(std::filesystem::path &root_path); + FileFilter(std::filesystem::path &root_path) : root_path(root_path) {} - std::vector filterOut(std::filesystem::path &path); + FileFilter(std::vector &paths) : paths_to_filter(paths) { + paths_generated = true; + } + + std::vector filterOut(); static std::vector filterOut(std::vector &filters, std::filesystem::path &path); - std::vector filterIn(std::filesystem::path &path); + std::vector filterIn(); static std::vector filterIn(std::vector &filters, std::filesystem::path &path); FileFilter &addExtension(std::string &extension); - FileFilter &addPrefix(std::string &prefix); - FileFilter &addPath(std::filesystem::path &path); - FileFilter &addPaths(std::initializer_list paths); FileFilter &addExtensions(std::initializer_list extensions); + FileFilter &addPrefix(std::string &prefix); FileFilter &addPrefixes(std::initializer_list prefixes); + FileFilter &addPath(std::filesystem::path &path); + FileFilter &addDirs(std::initializer_list paths); + FileFilter &addFile(std::string &file); + FileFilter &addFiles(std::initializer_list files); }; } // namespace Frate::Utils diff --git a/include/Frate/Utils/General.hpp b/include/Frate/Utils/General.hpp index 8d25dc0..0790085 100644 --- a/include/Frate/Utils/General.hpp +++ b/include/Frate/Utils/General.hpp @@ -3,11 +3,7 @@ #include #include #include -#include #include -#define DEBUGTHIS(x) \ - std::cout << termcolor::bright_red << "##x" \ - << " -> " << x << std::endl; #ifdef DEBUG #include diff --git a/include/Frate/Utils/Logging.hpp b/include/Frate/Utils/Logging.hpp index c518b17..afce84a 100644 --- a/include/Frate/Utils/Logging.hpp +++ b/include/Frate/Utils/Logging.hpp @@ -1,6 +1,6 @@ #pragma once +#include #include -#include #include namespace Frate::Utils { diff --git a/src/Command/Helpers/Interface.cpp b/src/Command/Helpers/Interface.cpp index 5b7d1fd..56125ea 100644 --- a/src/Command/Helpers/Interface.cpp +++ b/src/Command/Helpers/Interface.cpp @@ -211,6 +211,7 @@ namespace Frate::Command { inter->pro->save(); } inter->config->save(); + inter->templates->save(); return true; } } diff --git a/src/Command/Helpers/Project.cpp b/src/Command/Helpers/Project.cpp index a20e80a..0762838 100644 --- a/src/Command/Helpers/Project.cpp +++ b/src/Command/Helpers/Project.cpp @@ -53,7 +53,7 @@ namespace Frate::Project { return "'" + std::filesystem::path(this->path).string() + "'"; }; - void Project::Config::fromTemplate(Template &temp) { + void Project::Config::fromTemplate(TemplateConfig &temp) { this->src_dir = temp.src_dir; this->include_dir = temp.include_dir; this->build_dir = temp.build_dir; diff --git a/src/Command/Helpers/Template.cpp b/src/Command/Helpers/Template.cpp index e3be1f8..03a5abf 100644 --- a/src/Command/Helpers/Template.cpp +++ b/src/Command/Helpers/Template.cpp @@ -5,9 +5,9 @@ namespace Frate::Project { - Template::Template() = default; + TemplateConfig::TemplateConfig() = default; - void from_json(const json &json_obj, Template &template_) { + void from_json(const json &json_obj, TemplateConfig &template_) { FROM_JSON_FIELD(template_, name); FROM_JSON_FIELD(template_, description); FROM_JSON_FIELD(template_, version); @@ -29,7 +29,7 @@ namespace Frate::Project { FROM_JSON_FIELD(template_, global); } - void to_json(json &json_obj, const Template &template_) { + void to_json(json &json_obj, const TemplateConfig &template_) { Utils::info << "Template to json" << std::endl; TO_JSON_FIELD(template_, name); TO_JSON_FIELD(template_, description); @@ -52,7 +52,7 @@ namespace Frate::Project { TO_JSON_FIELD(template_, global); } - void to_project(Project::Config &pro, const Template &template_) { + void to_project(Project::Config &pro, const TemplateConfig &template_) { Utils::info << "Template to project" << std::endl; pro.name = template_.name; pro.description = template_.description; diff --git a/src/Command/Helpers/TemplateMeta.cpp b/src/Command/Helpers/TemplateMeta.cpp index 313a650..656db91 100644 --- a/src/Command/Helpers/TemplateMeta.cpp +++ b/src/Command/Helpers/TemplateMeta.cpp @@ -1,11 +1,32 @@ +#include "Frate/Constants.hpp" +#include "Frate/Lua/TemplateEnvironment.hpp" +#include +#include #include +#include #include #include +#include +#include +#include namespace Frate::Project { + using std::filesystem::directory_entry; TemplateMeta::TemplateMeta(const nlohmann::json &json_obj) { + Utils::verbose << "Creating template meta from json with contents: " + << json_obj << std::endl; + from_json(json_obj, *this); + this->install_path = + Constants::INSTALLED_TEMPLATE_PATH / this->name / this->hash; + file_map = {}; + } + + TemplateMeta::TemplateMeta() { + this->install_path = + Constants::INSTALLED_TEMPLATE_PATH / this->name / this->hash; + file_map = {}; } void from_json(const nlohmann::json &json_obj, TemplateMeta &temp) { @@ -30,4 +51,242 @@ namespace Frate::Project { return os_stream; } + void TemplateMeta::build(std::shared_ptr config) { + + Utils::verbose << "Building template from template at: " << install_path + << std::endl; + + std::filesystem::path template_config_path = install_path / "template.json"; + + if (!std::filesystem::exists(install_path)) { + throw TemplateNotInstalled("Template not installed in path"); + } + + if (!std::filesystem::exists(template_config_path)) { + throw TemplateConfigNotFound("Template config not found"); + } + + for (const directory_entry &file : + std::filesystem::recursive_directory_iterator(install_path)) { + + file_map[std::filesystem::relative(file.path(), install_path)] = + file.path(); + } + + Utils::verbose << "Opening template config: " << template_config_path + << std::endl; + + std::ifstream template_config_file; + try { + + template_config_file.open(template_config_path); + + } catch (std::exception &e) { + + Utils::error << "Failed to open template config: " << template_config_path + << std::endl; + + Utils::error << "Error: " << e.what() << std::endl; + } + + nlohmann::json template_config_json; + + template_config_file >> template_config_json; + + template_config_file.close(); + + TemplateConfig template_config = template_config_json; + + config->fromTemplate(template_config); + + Utils::info << "Template built" << std::endl; + install_cpm(config); + render(config); + } + + void TemplateMeta::refresh(std::shared_ptr config) { + + std::filesystem::path override_path = config->path / "override"; + + Utils::FileFilter template_filter(install_path); + template_filter.addDirs({"scripts", "__init__", "__post__", "cmake_includes"}); + template_filter.addFiles({"CMakeLists.txt"}); + + + auto install_path_files = template_filter.filterIn(); + + + for (auto &file : install_path_files) { + std::filesystem::path relative_path = + std::filesystem::relative(file, install_path); + file_map[relative_path.string()] = file; + } + + Utils::FileFilter override_filter(override_path); + override_filter.addDirs({"scripts", "__init__", "__post__", "cmake_includes"}); + override_filter.addFiles({"CMakeLists.txt"}); + + auto override_path_files = override_filter.filterIn(); + + for (auto &file : override_path_files) { + std::filesystem::path relative_path = + std::filesystem::relative(file, override_path); + file_map[relative_path.string()] = file; + } + + render(config); + } + + void TemplateMeta::load_scripts() { + if (file_map.empty()) { + throw TemplateFileMapEmpty("File map is empty"); + } + + const std::string script_key = "scripts."; + const std::string init_key = "__init__."; + const std::string post_key = "__post__."; + + for (auto [relative_path, file_path] : file_map) { + + if (file_path.extension() == ".lua") { + std::string namespace_str = + Lua::TemplateEnvironment::relativePathToNamespace(relative_path); + + if (namespace_str.empty()) { + throw Lua::LuaException("Namespace string is empty"); + } + + std::ifstream script_file; + std::string script_text; + try { + script_file.open(file_path); + while (script_file.good()) { + std::string line; + std::getline(script_file, line); + script_text += line + "\n"; + } + } catch (std::exception &e) { + Utils::error << "Error reading script file: " << file_path + << std::endl; + Utils::error << "Error: " << e.what() << std::endl; + } + + if (namespace_str.starts_with("scripts")) { + namespace_str.erase(0, script_key.length()); + Utils::verbose << "Registering script: " << namespace_str + << std::endl; + env->registerMacroScript(namespace_str, script_text); + } + else if (namespace_str.starts_with("__init__")) { + namespace_str.erase(0, init_key.length()); + Utils::verbose << "Registering init script: " << namespace_str + << std::endl; + env->registerInitScript(namespace_str, script_text); + } + else if (namespace_str.starts_with("__post__")) { + namespace_str.erase(0, post_key.length()); + Utils::verbose << "Registering post script: " << namespace_str + << std::endl; + env->registerPostScript(namespace_str, script_text); + } + else { + Utils::verbose << "Registering script: " << namespace_str + << std::endl; + env->registerMacroScript(namespace_str, script_text); + } + } + } + scripts_loaded = true; + } + + void TemplateMeta::install_cpm(std::shared_ptr config) { + std::string cpm; + + cpm = Utils::fetchText("https://raw.githubusercontent.com/cpm-cmake/" + "CPM.cmake/v0.38.6/cmake/CPM.cmake"); + + std::ofstream cpm_file; + try { + if (!std::filesystem::exists(config->path / "cmake")){ + std::filesystem::create_directories(config->path / "cmake"); + } + cpm_file.open(config->path / "cmake/CPM.cmake"); + } catch (...) { + Utils::error << "Error while opening file: CPM.cmake" << std::endl; + throw Lua::LuaException("Error while opening file: CPM.cmake"); + } + + cpm_file << cpm; + } + + void TemplateMeta::render(std::shared_ptr config) { + Utils::info << "Rendering template" << std::endl; + this->env = std::make_shared(config); + + + if (!scripts_loaded) { + + try { + load_scripts(); + } catch (std::exception &e) { + Utils::error << "Error loading scripts: " << e.what() << std::endl; + throw Lua::LuaException("Error loading scripts"); + } + } + + + // Generate a list of all files that are not templates + std::vector all_files; + + for (auto [relative_path, file_path] : file_map) { + all_files.emplace_back(file_path); + } + + Utils::FileFilter non_template_filter(all_files); + + non_template_filter.addExtensions({".lua", ".inja"}); + non_template_filter.addDirs( + {"scripts", "__init__", "__post__", "cmake_includes"}); + non_template_filter.addFiles({"template.json"}); + + + all_files = non_template_filter.filterOut(); + + for (auto &file : all_files) { + // Finds the relative path i nthe original file map and copies it to the + // output directory + for (auto [relative_path, file_path] : file_map) { + if (file_path == file) { + std::filesystem::path output_file = config->path / relative_path; + Utils::verbose << "Copying file: " << file_path << " to " + << output_file << std::endl; + if (!std::filesystem::exists(output_file.parent_path())) { + std::filesystem::create_directories(output_file.parent_path()); + } + std::filesystem::copy( + file_path, output_file, + std::filesystem::copy_options::recursive | + std::filesystem::copy_options::overwrite_existing); + } + } + } + + for (auto [relative_path, file_path] : file_map) { + + if (file_path.extension() == ".inja") { + Utils::verbose << "Rendering template: " << file_path << std::endl; + std::string output_file = config->path / relative_path; + + output_file = output_file.substr(0, output_file.find(".inja")); + + if (env->getProjectConfig() == nullptr) { + throw Lua::LuaException( + "Project config is null before templating file"); + } + + env->templateFile(file_path, output_file); + } + } + } + } // namespace Frate::Project diff --git a/src/Generators/CMakeTemplates.cpp b/src/Generators/CMakeTemplates.cpp index e06e50f..6f83a93 100644 --- a/src/Generators/CMakeTemplates.cpp +++ b/src/Generators/CMakeTemplates.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -36,14 +36,14 @@ namespace Frate::Generators::CMakeList { CPMFile << CPM; inja::Environment env; sol::state lua; - LuaAPI::registerAPI(lua); + Lua::registerAPI(lua); - if (!LuaAPI::registerProject(lua, pro)) { + if (!Lua::registerProject(lua, pro)) { Utils::debug("Error while registering project"); return false; } - if (!LuaAPI::registerProjectScripts(env, lua, + if (!Lua::registerProjectScripts(env, lua, pro->path / "templates/scripts", pro)) { Utils::debug("Error while registering project scripts"); return false; diff --git a/src/Generators/DockerTemplate.cpp b/src/Generators/DockerTemplate.cpp index bbf20cb..fcb75e8 100644 --- a/src/Generators/DockerTemplate.cpp +++ b/src/Generators/DockerTemplate.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include @@ -23,14 +23,14 @@ namespace Frate::Generators::DockerTemplate { bool create(std::shared_ptr inter) { inja::Environment env; sol::state lua; - LuaAPI::registerAPI(lua); + Lua::registerAPI(lua); - if (!LuaAPI::registerProject(lua, inter->pro)) { + if (!Lua::registerProject(lua, inter->pro)) { Utils::debug("Error while registering project"); return false; } - if (!LuaAPI::registerProjectScripts( + if (!Lua::registerProjectScripts( env, lua, inter->pro->path / "templates/scripts", inter->pro)) { Utils::debug("Error while registering project scripts"); return false; diff --git a/src/Generators/ProjectGenerator/InitializeLua.cpp b/src/Generators/ProjectGenerator/InitializeLua.cpp index 2e515ea..e85380b 100644 --- a/src/Generators/ProjectGenerator/InitializeLua.cpp +++ b/src/Generators/ProjectGenerator/InitializeLua.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include namespace Frate::Generators::Project { @@ -7,15 +7,15 @@ namespace Frate::Generators::Project { bool initializeLua(Environment &env, sol::state &lua, std::shared_ptr pro) { - LuaAPI::registerAPI(lua); + Lua::registerAPI(lua); Utils::info << "Registering project" << std::endl; - if (!LuaAPI::registerProject(lua, pro)) { + if (!Lua::registerProject(lua, pro)) { Utils::error << "Error while registering project" << std::endl; return false; } Utils::info << "Registering project scripts at: " << pro->template_path / "scripts" << std::endl; - if (!LuaAPI::registerProjectScripts(env, lua, + if (!Lua::registerProjectScripts(env, lua, pro->template_path / "scripts", pro)) { Utils::error << "Error while registering project scripts" << std::endl; return false; diff --git a/src/Generators/ProjectGenerator/LoadTemplateConfig.cpp b/src/Generators/ProjectGenerator/LoadTemplateConfig.cpp index a042564..c3932e3 100644 --- a/src/Generators/ProjectGenerator/LoadTemplateConfig.cpp +++ b/src/Generators/ProjectGenerator/LoadTemplateConfig.cpp @@ -5,7 +5,7 @@ #include namespace Frate::Generators::Project { - using ::Frate::Project::Template; + using ::Frate::Project::TemplateConfig; bool loadTemplateConfig(std::shared_ptr pro) { @@ -24,7 +24,7 @@ namespace Frate::Generators::Project { json j = json::parse(template_config_file); - Template template_config = j; + TemplateConfig template_config = j; pro->fromTemplate(template_config); return true; } diff --git a/src/Generators/ProjectGenerator/ProjectGenerator.cpp b/src/Generators/ProjectGenerator/ProjectGenerator.cpp index fbe8a2e..152170f 100644 --- a/src/Generators/ProjectGenerator/ProjectGenerator.cpp +++ b/src/Generators/ProjectGenerator/ProjectGenerator.cpp @@ -1,7 +1,5 @@ -#include "Frate/LuaAPI.hpp" #include "Frate/System/Build.hpp" #include "Frate/Utils/General.hpp" -#include "inja.hpp" #include #include #include @@ -28,7 +26,7 @@ namespace Frate::Generators::Project { Project::TemplateMeta current_template; for (TemplateMeta templ : index) { - if (inter->pro->type == templ.name) { + if (inter->pro->type == templ.getName()) { has_template = true; current_template = templ; break; @@ -42,117 +40,28 @@ namespace Frate::Generators::Project { return false; } current_template = TemplateMeta(templ); - Utils::info << "Creating project from template: " << templ.name + Utils::info << "Creating project from template: " << templ.getName() << std::endl; } std::filesystem::create_directories(inter->pro->path / "override"); - TemplateMeta installed_template = - inter->templates->install(current_template.name); + inter->pro->current_template = + inter->templates->install(current_template.getName()); - const std::filesystem::path override_path = inter->pro->path / "override"; + Utils::verbose << "Installed template: " + << inter->pro->current_template.getName() << " with hash of: " << inter->pro->current_template.getHash() << std::endl; - std::filesystem::path render_path = inter->templates->makeTemplate( - override_path, installed_template.name, installed_template.hash); + inter->pro->current_template.build(inter->pro); - inter->pro->template_path = render_path; - inter->pro->current_template = installed_template; - - Utils::info << "Installed template" << installed_template << std::endl; - - Utils::info << "Copying template to project" << std::endl; - - std::filesystem::copy( - inter->pro->template_path, inter->pro->path, - std::filesystem::copy_options::recursive | - std::filesystem::copy_options::overwrite_existing); - - Utils::info << "Loading template config" << std::endl; - if (!loadTemplateConfig(inter->pro)) { - Utils::error << "Error while loading template config" << std::endl; - return false; - } - Utils::info << "Running template prompts" << std::endl; - if (!runTemplatePrompts(inter->pro)) { - Utils::error << "Error while running template prompts" << std::endl; - return false; - } - - inja::Environment env; - sol::state lua; - - Utils::info << "Initializing lua" << std::endl; - if (!initializeLua(env, lua, inter->pro)) { - Utils::error << "Error while initializing lua" << std::endl; - return false; - } - - LuaAPI::initScripts(lua, inter->pro); - - Utils::info << "Rendering template" << std::endl; - if (!renderTemplate(env, inter->pro)) { - Utils::error << "Error while rendering template to tmp" << std::endl; - return false; - } - - LuaAPI::postScripts(lua, inter->pro); - - Utils::info << "Copying rendered template to project" << std::endl; - - std::filesystem::copy( - render_path, inter->pro->path, - std::filesystem::copy_options::recursive | - std::filesystem::copy_options::overwrite_existing); - - // This ia bit of a hack, because create doesn't actually load a project so - // we have to emulate the loading process inter->pro->loaded_json = true; inter->pro->save(); - std::filesystem::remove_all(render_path); - return true; } bool refresh(std::shared_ptr inter) { - inja::Environment env; - sol::state lua; - - const std::filesystem::path override_path = inter->pro->path / "override"; - - std::filesystem::path render_path = inter->templates->makeTemplate( - override_path, inter->pro->current_template.name, - inter->pro->current_template.hash); - - inter->pro->template_path = render_path; - - if (!initializeLua(env, lua, inter->pro)) { - Utils::error << "Error while initializing lua" << std::endl; - return false; - } - - Utils::verbose << "Initializing lua scripts at: " - << inter->pro->path / "template/scripts" << std::endl; - LuaAPI::initScripts(lua, inter->pro); - - Utils::verbose << "Refreshing template" << std::endl; - if (!refreshTemplate(env, inter->pro)) { - Utils::error << "Error while rendering template to tmp" << std::endl; - return false; - } - - LuaAPI::postScripts(lua, inter->pro); - - Utils::verbose << "Copying rendered template to project" << std::endl; - - std::filesystem::copy( - render_path, inter->pro->path, - std::filesystem::copy_options::recursive | - std::filesystem::copy_options::overwrite_existing); - - std::filesystem::remove_all(render_path); - + inter->pro->current_template.refresh(inter->pro); return true; } } // namespace Frate::Generators::Project diff --git a/src/Generators/ProjectGenerator/TemplatePrompts.cpp b/src/Generators/ProjectGenerator/TemplatePrompts.cpp index 9c9cc88..ec16674 100644 --- a/src/Generators/ProjectGenerator/TemplatePrompts.cpp +++ b/src/Generators/ProjectGenerator/TemplatePrompts.cpp @@ -9,7 +9,7 @@ namespace Frate::Generators::Project { std::pair promptForTemplateName(json index) { Prompt template_name_prompt("Project type: "); for (TemplateMeta templ : index) { - template_name_prompt.addOption(templ.name); + template_name_prompt.addOption(templ.getName()); } template_name_prompt.printValidOptions(); @@ -25,13 +25,13 @@ namespace Frate::Generators::Project { TemplateMeta templ; for (TemplateMeta temp : index) { - if (temp.name == template_id) { + if (temp.getName() == template_id) { templ = temp; break; } } - if (templ.name.empty()) { + if (templ.getName().empty()) { Utils::error << "TemplateMeta not found" << std::endl; return {false, TemplateMeta()}; } diff --git a/src/Lua/API.cpp b/src/Lua/API.cpp index b5e9a7c..21981f7 100644 --- a/src/Lua/API.cpp +++ b/src/Lua/API.cpp @@ -1,8 +1,10 @@ +#include "Frate/Lua/Exceptions.hpp" +#include "Frate/Lua/LuaAPI.hpp" #include "Frate/Project/Config.hpp" #include "Frate/Utils/General.hpp" +#include #include "inja.hpp" #include -#include #include #include #include @@ -10,10 +12,31 @@ #include #include -namespace Frate::LuaAPI { +namespace Frate::Lua { using Project::Config; using std::filesystem::path; + void registerTemplateMacroScript(inja::Environment &env, sol::state &lua, + path script_path, + std::shared_ptr project) { + + if (!std::filesystem::exists(script_path)) { + Utils::error << "Script not found at path: " << script_path << std::endl; + throw LuaException("Script not found at path: " + script_path.string()); + } + + lua.set("project", project); + + auto result = lua.script_file(script_path); + + if (!result.valid()) { + throw LuaException("Error while executing lua script at: " + + script_path.string()); + } + + project = lua.get>("project"); + } + bool registerProjectScripts(inja::Environment &env, sol::state &lua, path script_path, std::shared_ptr project) { @@ -103,13 +126,19 @@ namespace Frate::LuaAPI { void registerAPI(sol::state &lua) { lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::string); - + // clang-format off lua.new_usertype( - "frate", "new", sol::no_constructor, "get_os", &FrateApi::get_os, - "get_path", &FrateApi::get_path, "get_paths_recurse", - &FrateApi::get_paths_recurse, "format", &FrateApi::format, - "print_table", &FrateApi::print_table, "fetch_text", - &FrateApi::fetch_text, "fetch_json", &FrateApi::fetch_json); + "frate", + "new", sol::no_constructor, + "get_os", &FrateApi::get_os, + "get_path", &FrateApi::get_path, + "get_paths_recurse", &FrateApi::get_paths_recurse, + "format", &FrateApi::format, + "print_table", &FrateApi::print_table, + "fetch_text", &FrateApi::fetch_text, + "fetch_json", &FrateApi::fetch_json + ); + // clang-format on } bool initScripts(sol::state &lua, std::shared_ptr project) { @@ -192,4 +221,4 @@ namespace Frate::LuaAPI { } return true; } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/src/Lua/FetchJson.cpp b/src/Lua/FetchJson.cpp index e5f996b..755b017 100644 --- a/src/Lua/FetchJson.cpp +++ b/src/Lua/FetchJson.cpp @@ -1,7 +1,8 @@ -#include +#include +#include #include -sol::table Frate::LuaAPI::FrateApi::fetch_json(const std::string &url, +sol::table Frate::Lua::FrateApi::fetch_json(const std::string &url, sol::this_state inner_state) { lua_State *lua_state = inner_state; sol::state_view lua(lua_state); diff --git a/src/Lua/FetchText.cpp b/src/Lua/FetchText.cpp index 75d2979..f72e904 100644 --- a/src/Lua/FetchText.cpp +++ b/src/Lua/FetchText.cpp @@ -1,6 +1,6 @@ -#include "Frate/LuaAPI.hpp" +#include "Frate/Lua/LuaAPI.hpp" #include "Frate/Utils/General.hpp" -std::string Frate::LuaAPI::FrateApi::fetch_text(const std::string &url) { +std::string Frate::Lua::FrateApi::fetch_text(const std::string &url) { return Utils::fetchText(url); } diff --git a/src/Lua/Format.cpp b/src/Lua/Format.cpp index 528ccaf..b35ce97 100644 --- a/src/Lua/Format.cpp +++ b/src/Lua/Format.cpp @@ -1,7 +1,7 @@ #include "Frate/Utils/Logging.hpp" -#include +#include -namespace Frate::LuaAPI { +namespace Frate::Lua { enum token_type { open_bracket, close_bracket, char_literal, specifier }; struct token { @@ -121,4 +121,4 @@ namespace Frate::LuaAPI { // Utils::info << result << std::endl; return result; } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/src/Lua/GetOS.cpp b/src/Lua/GetOS.cpp index f626171..077948b 100644 --- a/src/Lua/GetOS.cpp +++ b/src/Lua/GetOS.cpp @@ -1,8 +1,8 @@ #include -#include +#include -namespace Frate::LuaAPI { +namespace Frate::Lua { using std::filesystem::path; std::string FrateApi::get_os() { return Frate::Constants::BUILD_OS; } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/src/Lua/GetPath.cpp b/src/Lua/GetPath.cpp index e06f238..62b02db 100644 --- a/src/Lua/GetPath.cpp +++ b/src/Lua/GetPath.cpp @@ -1,7 +1,7 @@ -#include +#include #include -namespace Frate::LuaAPI { +namespace Frate::Lua { std::string FrateApi::get_path() { return std::filesystem::current_path().string() #ifdef DEBUG @@ -9,4 +9,4 @@ namespace Frate::LuaAPI { #endif ; } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/src/Lua/GetPathsRecurse.cpp b/src/Lua/GetPathsRecurse.cpp index 46ab2ce..619b417 100644 --- a/src/Lua/GetPathsRecurse.cpp +++ b/src/Lua/GetPathsRecurse.cpp @@ -1,8 +1,8 @@ #include "Frate/Utils/Logging.hpp" -#include +#include #include -namespace Frate::LuaAPI { +namespace Frate::Lua { std::vector FrateApi::get_paths_recurse(std::string input_path) { std::filesystem::path deepest_path = std::filesystem::current_path(); Utils::info << "Getting paths from " << input_path << std::endl; @@ -29,4 +29,4 @@ namespace Frate::LuaAPI { return paths; } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/src/Lua/PrintTable.cpp b/src/Lua/PrintTable.cpp index 4e550ae..b0695cf 100644 --- a/src/Lua/PrintTable.cpp +++ b/src/Lua/PrintTable.cpp @@ -1,6 +1,6 @@ -#include +#include -namespace Frate::LuaAPI { +namespace Frate::Lua { void print_table(sol::table in_table, int indent = 0) { for (auto [key, value] : in_table) { if (value.is()) { @@ -26,8 +26,8 @@ namespace Frate::LuaAPI { } } } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua -void Frate::LuaAPI::FrateApi::print_table(sol::table in_table) { - Frate::LuaAPI::print_table(in_table); +void Frate::Lua::FrateApi::print_table(sol::table in_table) { + Frate::Lua::print_table(in_table); } diff --git a/src/Lua/RegisterProject.cpp b/src/Lua/RegisterProject.cpp index ac95949..67c544d 100644 --- a/src/Lua/RegisterProject.cpp +++ b/src/Lua/RegisterProject.cpp @@ -1,9 +1,9 @@ -#include +#include #include #include #include -namespace Frate::LuaAPI { +namespace Frate::Lua { bool registerProject(sol::state &lua, std::shared_ptr pro) { lua.set("project", pro); @@ -133,4 +133,4 @@ namespace Frate::LuaAPI { // clang-format on return true; } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/src/Lua/TemplateEnvironment.cpp b/src/Lua/TemplateEnvironment.cpp new file mode 100644 index 0000000..39f7931 --- /dev/null +++ b/src/Lua/TemplateEnvironment.cpp @@ -0,0 +1,262 @@ +#include +#include +#include +#include + +namespace Frate::Lua { + + void TemplateEnvironment::registerMacroScript(std::string name, + std::string script_text) { + macro_scripts[name] = script_text; + register_inja_function(name, script_text); + }; + + void TemplateEnvironment::registerInitScript(std::string name, + std::string script_text) { + init_scripts[name] = script_text; + }; + + void TemplateEnvironment::registerPostScript(std::string name, + std::string script_text) { + post_scripts[name] = script_text; + }; + + void TemplateEnvironment::templateFile(std::filesystem::path input_file, + std::filesystem::path output_file) { + Utils::verbose << "Templating file: " << input_file << std::endl; + if(!std::filesystem::exists(input_file)){ + throw LuaException("Input file does not exist"); + } + + if(!std::filesystem::exists(output_file.parent_path())){ + std::filesystem::create_directories(output_file.parent_path()); + } + + if(pro == nullptr){ + throw LuaException("Project is null while templating file"); + } + + try { + + std::string rendered_file = env.render_file(input_file.string(), *pro); + std::ofstream output_stream(output_file); + output_stream << rendered_file; + output_stream.close(); + + } catch (std::exception &e) { + + Utils::error << e.what() << std::endl; + throw LuaException("Error templating file"); + + } + } + + std::string TemplateEnvironment::relativePathToNamespace(std::filesystem::path root_path ,std::filesystem::path file_path){ + std::filesystem::path relative_path = std::filesystem::relative(file_path, root_path); + std::string namespace_str = relative_path.string(); + // Remove the extension + namespace_str = namespace_str.substr(0, namespace_str.find(".")); + // Replace the slashes with dots + namespace_str = namespace_str.replace(namespace_str.begin(), namespace_str.end(), "/", "."); + return namespace_str; + } + + std::string TemplateEnvironment::relativePathToNamespace(std::filesystem::path file_path){ + std::string namespace_str; + namespace_str += file_path.string(); + // Remove the extension + if(file_path.has_extension()){ + namespace_str = namespace_str.substr(0, namespace_str.find(".")); + } + // Replace the slashes with dots + std::replace(namespace_str.begin(), namespace_str.end(), '/', '.'); + + return namespace_str; + } + + void TemplateEnvironment::register_frate_api(){ + lua->open_libraries(sol::lib::base, sol::lib::package, sol::lib::string); + // clang-format off + lua->new_usertype( + "frate", + "new", sol::no_constructor, + "get_os", &FrateApi::get_os, + "get_path", &FrateApi::get_path, + "get_paths_recurse", &FrateApi::get_paths_recurse, + "format", &FrateApi::format, + "print_table", &FrateApi::print_table, + "fetch_text", &FrateApi::fetch_text, + "fetch_json", &FrateApi::fetch_json + ); + // clang-format on + + }; + + void + TemplateEnvironment::register_inja_function(std::string name, + std::string lua_script_text) { + // Creating lua and project state so that we can pass them to the callback + + const auto script_callback = + [lua_script_text, lua_state = this->lua, + pro_config = this->pro](inja::Arguments &input_args) { + + sol::table args_table = lua_state->create_table(); + for (const nlohmann::json *arg : input_args) { + if (arg->is_string()) { + args_table.add(arg->get()); + } + else if (arg->is_number()) { + args_table.add(arg->get()); + } + else if (arg->is_boolean()) { + args_table.add(arg->get()); + } + else { + throw Frate::Lua::LuaException("Invalid argument type"); + } + } + lua_state->set("args", args_table); + lua_state->set("global", pro_config->global); + + auto result = lua_state->script(lua_script_text); + + if (!result.valid()) { + throw Frate::Lua::LuaException(result.get().what()); + } + return result; + }; + + env.add_callback(name, script_callback); + } + + + void TemplateEnvironment::register_user_types() { + + lua->set("project", pro); + // clang-format off + lua->new_usertype("Package", + "new", + sol::no_constructor, + "name", + &Command::Package::name, + "git", + &Command::Package::git, + "git_short", + &Command::Package::git_short, + "git_prefixed", + &Command::Package::git_prefixed, + "versions", + &Command::Package::versions, + "target_link", + &Command::Package::target_link, + "description", + &Command::Package::description, + "git_description", + &Command::Package::git_description, + "language", + &Command::Package::language, + "license", + &Command::Package::license, + "owner", + &Command::Package::owner, + "owner_type", + &Command::Package::owner_type, + "stars", + &Command::Package::stars, + "forks", + &Command::Package::forks, + "open_issues", + &Command::Package::open_issues, + "watchers", + &Command::Package::watchers); + lua->new_usertype("Dependency", + "new", + sol::no_constructor, + "name", + &Command::Dependency::name, + "version", + &Command::Dependency::version, + "git", + &Command::Dependency::git, + "git_short", + &Command::Dependency::git_short, + "git_prefixed", + &Command::Dependency::git_prefixed, + "target_link", + &Command::Dependency::target_link); + + lua->new_usertype("Mode", + "new", + sol::no_constructor, + "name", + &Project::Mode::name, + "flags", + &Project::Mode::flags, + "dependencies", + &Project::Mode::dependencies); + + lua->new_usertype("Project" + "new", + sol::no_constructor, + "name", + &Project::Config::name, + "version", + &Project::Config::version, + "description", + &Project::Config::description, + "authors", + &Project::Config::authors, + "dependencies", + &Project::Config::dependencies, + "toolchains", + &Project::Config::toolchains, + "flags", + &Project::Config::flags, + "modes", + &Project::Config::modes, + "libs", + &Project::Config::libs, + "license", + &Project::Config::license, + "git", + &Project::Config::git, + "cmake_version", + &Project::Config::cmake_version, + "build_command", + &Project::Config::build_command, + "build_dir", + &Project::Config::build_dir, + "src_dir", + &Project::Config::src_dir, + "include_dir", + &Project::Config::include_dir, + "lang_version", + &Project::Config::lang_version, + "lang", + &Project::Config::lang, + "project_type", + &Project::Config::type, + "keywords", + &Project::Config::keywords, + "prompts", + &Project::Config::prompts); + + lua->new_usertype( + "ProjectPrompt", + "value", + &Project::ProjectPrompt::value, + "default", + &Project::ProjectPrompt::default_value, + "getstr", + &Project::ProjectPrompt::get, + "getint", + &Project::ProjectPrompt::get, + "getbool", + &Project::ProjectPrompt::get, + "getfloat", + &Project::ProjectPrompt::get); + + } + +} // namespace Frate::Lua diff --git a/src/Lua/Utils.cpp b/src/Lua/Utils.cpp index a4e402c..e30a1b0 100644 --- a/src/Lua/Utils.cpp +++ b/src/Lua/Utils.cpp @@ -1,7 +1,7 @@ -#include +#include #include -namespace Frate::LuaAPI { +namespace Frate::Lua { sol::table to_table(nlohmann::json in_json, sol::state_view &lua) { sol::table out_table = lua.create_table(); if (in_json.is_null()) { @@ -19,57 +19,71 @@ namespace Frate::LuaAPI { sol::table new_table = to_table(value, lua); out_table.add(new_table); - } else if (value.is_array()) { + } + else if (value.is_array()) { sol::table new_table = to_table(value, lua); out_table.add(new_table); - } else if (value.is_string()) { + } + else if (value.is_string()) { out_table.add(value.get()); - } else if (value.is_number_integer()) { + } + else if (value.is_number_integer()) { out_table.add(value.get()); - } else if (value.is_number_float()) { + } + else if (value.is_number_float()) { out_table.add(value.get()); - } else if (value.is_boolean()) { + } + else if (value.is_boolean()) { out_table.add(value.get()); - } else if (value.is_null()) { - + } + else if (value.is_null()) { out_table.add(sol::nil); - } else { + } + else { out_table.add(sol::nil); } } - } else { + } + else { for (auto [key, value] : in_json.items()) { if (value.is_object()) { sol::table new_table = to_table(value, lua); out_table[key] = new_table; - } else if (value.is_array()) { + } + else if (value.is_array()) { sol::table new_table = to_table(value, lua); out_table[key] = new_table; - } else if (value.is_string()) { + } + else if (value.is_string()) { out_table[key] = value.get(); - } else if (value.is_number_integer()) { + } + else if (value.is_number_integer()) { out_table[key] = value.get(); - } else if (value.is_number_float()) { + } + else if (value.is_number_float()) { out_table[key] = value.get(); - } else if (value.is_boolean()) { + } + else if (value.is_boolean()) { out_table[key] = value.get(); - } else if (value.is_null()) { + } + else if (value.is_null()) { out_table[key] = sol::nil; - } else { + } + else { out_table[key] = sol::nil; } @@ -94,37 +108,37 @@ namespace Frate::LuaAPI { nlohmann::json new_json = from_table(value.as()); out_json[key.as()] = new_json; - - } else if (is_table_array(value)) { + } + else if (is_table_array(value)) { nlohmann::json new_json = from_table(value.as()); out_json[key.as()] = new_json; - - } else if (value.is()) { + } + else if (value.is()) { out_json[key.as()] = value.as(); - - } else if (value.is()) { + } + else if (value.is()) { out_json[key.as()] = value.as(); - - } else if (value.is()) { + } + else if (value.is()) { out_json[key.as()] = value.as(); - - } else if (value.is()) { + } + else if (value.is()) { out_json[key.as()] = value.as(); - - } else if (value.is()) { + } + else if (value.is()) { out_json[key.as()] = nullptr; - - } else { + } + else { out_json[key.as()] = nullptr; } } return out_json; } -} // namespace Frate::LuaAPI +} // namespace Frate::Lua diff --git a/src/Project/InstalledTemplate.cpp b/src/Project/InstalledTemplate.cpp index 0afb723..6608c19 100644 --- a/src/Project/InstalledTemplate.cpp +++ b/src/Project/InstalledTemplate.cpp @@ -1,37 +1,129 @@ +#include "Frate/Constants.hpp" #include "Frate/System/GitCommit.hpp" #include "Frate/Utils/Macros.hpp" #include #include +#include namespace Frate::Project { - System::GitCommit &InstalledTemplate::getLatestCommit() { - if (&latest == nullptr) { - // TODO: Iterrate through commits and find the latest - } - return latest; - } - - void to_json(nlohmann::json &json_obj, - const InstalledTemplate &template_obj) { - TO_JSON_FIELD(template_obj, name); - TO_JSON_FIELD(template_obj, commits); - TO_JSON_FIELD(template_obj, git); - } - - void from_json(const nlohmann::json &json_obj, - InstalledTemplate &template_obj) { - FROM_JSON_FIELD(template_obj, name); - FROM_JSON_FIELD(template_obj, commits); - FROM_JSON_FIELD(template_obj, git); - } - - std::ostream &operator<<(std::ostream &os_stream, - const InstalledTemplate &template_obj) { - os_stream << "Name: " << template_obj.name << std::endl; - os_stream << "Commits: " << std::endl; - for (System::GitCommit commit : template_obj.commits) { - os_stream << commit << std::endl; - } - return os_stream; - } + // System::GitCommit &InstalledTemplate::getLatestCommit() { + // if (&latest == nullptr) { + // // TODO: Iterrate through commits and find the latest + // } + // return latest; + // } + // + // bool InstalledTemplate::validate_installed(std::string &template_hash) { + // + // if (template_hash.empty()) { + // Utils::error << "Template hash is empty"; + // return false; + // } + // + // return false; + // } + // + // TemplateConfig + // InstalledTemplate::get_template_info(std::string &template_hash) { + // + // std::filesystem::path template_config_path = + // Constants::INSTALLED_TEMPLATE_PATH / template_hash / + // "template.json"; + // + // if (!std::filesystem::exists(template_config_path)) { + // throw TemplateNotInstalled(("Template config file does not exist at: + // " + + // template_config_path.string())); + // } + // + // json template_json; + // std::ifstream template_file; + // + // try { + // + // template_file = std::ifstream(Constants::INSTALLED_TEMPLATE_PATH / + // template_hash / "template.json"); + // + // } catch (std::exception &e) { + // throw std::runtime_error("Failed to open template config file at " + + // Constants::INSTALLED_TEMPLATE_PATH.string() + // + + // "/" + template_hash + + // "/template.json - error: " + e.what()); + // } + // + // template_file >> template_json; + // + // return TemplateConfig(template_json); + // } + // + // void InstalledTemplate::build(std::shared_ptr config) { + // + // std::string hash = config->current_template.hash; + // + // std::filesystem::path template_path = + // Constants::INSTALLED_TEMPLATE_PATH / hash; + // + // std::filesystem::path override_path = config->path / "override"; + // + // std::unordered_map + // template_file_map; + // + // if (!validate_installed(hash)) { + // throw TemplateNotInstalled("Template is not installed"); + // } + // + // for (auto &file : std::filesystem::recursive_directory_iterator( + // template_path / "template")) { + // + // std::filesystem::path relative_path = + // std::filesystem::relative(file.path(), template_path); + // + // template_file_map[relative_path.string()] = file.path(); + // } + // + // if (std::filesystem::exists(override_path)) { + // for (auto &file : + // std::filesystem::recursive_directory_iterator(override_path)) { + // + // std::filesystem::path relative_path = + // std::filesystem::relative(file.path(), override_path); + // + // template_file_map[relative_path.string()] = file.path(); + // } + // } + // + // for (auto [relative_path, file_path] : template_file_map) { + // Utils::info << "relative path: " << relative_path + // << " file path: " << file_path << std::endl; + // } + // + // exit(-1); + // } + // + // void InstalledTemplate::refresh(std::shared_ptr config) {} + // + // void to_json(nlohmann::json &json_obj, + // const InstalledTemplate &template_obj) { + // TO_JSON_FIELD(template_obj, name); + // TO_JSON_FIELD(template_obj, commits); + // TO_JSON_FIELD(template_obj, git); + // } + // + // void from_json(const nlohmann::json &json_obj, + // InstalledTemplate &template_obj) { + // FROM_JSON_FIELD(template_obj, name); + // FROM_JSON_FIELD(template_obj, commits); + // FROM_JSON_FIELD(template_obj, git); + // } + // + // std::ostream &operator<<(std::ostream &os_stream, + // const InstalledTemplate &template_obj) { + // os_stream << "Name: " << template_obj.name << std::endl; + // os_stream << "Commits: " << std::endl; + // for (System::GitCommit commit : template_obj.commits) { + // os_stream << commit << std::endl; + // } + // return os_stream; + // } } // namespace Frate::Project diff --git a/src/Project/TemplateManager.cpp b/src/Project/TemplateManager.cpp index c30e0fb..5134cea 100644 --- a/src/Project/TemplateManager.cpp +++ b/src/Project/TemplateManager.cpp @@ -4,6 +4,7 @@ #include "Frate/Utils/General.hpp" #include "Frate/Utils/Logging.hpp" #include +#include #include #include #include @@ -11,7 +12,6 @@ #include namespace Frate::Project { - using Project::InstalledTemplate; void TemplateManager::load_index() { try { @@ -24,7 +24,7 @@ namespace Frate::Project { } catch (std::exception &e) { Utils::error << "Failed to load template index" << std::endl; Utils::error << e.what() << std::endl; - throw std::runtime_error("Failed to load template index"); + throw TemplateIndexNotLoaded("Failed to load template index"); } } @@ -36,26 +36,34 @@ namespace Frate::Project { return false; } - for (Project::InstalledTemplate template_obj : installed) { - if (template_obj.getName() == name) { - for (System::GitCommit commit : template_obj.getCommits()) { - if (commit.hash == hash) { - return true; - } - } - } + std::filesystem::path template_path = + Constants::INSTALLED_TEMPLATE_PATH / name / hash; + + if (!installed.contains(name)) { + Utils::error << "Template not installed in config" << std::endl; + return false; + } + + if (!installed[name].contains(hash)) { + Utils::error << "Template not installed in config" << std::endl; + return false; } - return false; + + if (!std::filesystem::exists(template_path)) { + Utils::error << "Template not installed in template path at: " + << template_path << std::endl; + return false; + } + + return true; } - Project::InstalledTemplate & - TemplateManager::find_template(const std::string &name) { - for (Project::InstalledTemplate &template_obj : installed) { - if (template_obj.getName() == name) { - return template_obj; - } + Project::TemplateMeta &TemplateManager::find_template(const std::string &name, + std::string &hash) { + if (!is_installed(name, hash)) { + throw TemplateNotInstalled("Template not installed"); } - throw std::runtime_error("Template not found"); + return installed[name][hash]; } void TemplateManager::template_to_installed_path( @@ -65,15 +73,14 @@ namespace Frate::Project { Utils::verbose << "Transferring template from" << tmp_path << " to " << new_template_path << std::endl; Utils::FileFilter filter(tmp_path); - filter.addPaths({ + filter.addDirs({ ".git", ".gitignore", ".gitmodules", ".gitattributes", }); - std::vector files_to_copy = - filter.filterOut(tmp_path); + std::vector files_to_copy = filter.filterOut(); for (std::filesystem::path tmp_filtered_path : files_to_copy) { @@ -84,6 +91,10 @@ namespace Frate::Project { Utils::verbose << "Copying: " << tmp_filtered_path << " to " << new_file_path << std::endl; + if(!std::filesystem::exists(new_file_path.parent_path())){ + std::filesystem::create_directories(new_file_path.parent_path()); + } + try { std::filesystem::copy( tmp_filtered_path, new_file_path, @@ -108,103 +119,9 @@ namespace Frate::Project { return index; } - std::filesystem::path - TemplateManager::makeTemplate(const std::filesystem::path &override_path, - const std::string &name, std::string &hash) { - - std::filesystem::path tmp_gen_path = - Utils::randomTmpPath("frate-template-render-"); - - std::filesystem::path template_path = - Constants::INSTALLED_TEMPLATE_PATH / name / hash; - - InstalledTemplate installed_temp; - - try { - installed_temp = find_template(name); - } catch (std::exception &e) { - Utils::warning << "Couldn't find template with name: " << name - << " and hash: " << hash << std::endl; - Utils::warning << "Installing template" << std::endl; - try { - install(name, hash); - } catch (std::exception &e) { - Utils::error << "Failed to install template" << std::endl; - Utils::error << e.what() << std::endl; - throw std::runtime_error("Failed to install template"); - } - } - - if (installed.empty()) { - throw std::runtime_error("Installed templates is empty"); - } - - try { - installed_temp = find_template(name); - } catch (std::exception &e) { - Utils::error << e.what() << std::endl; - throw std::runtime_error("Failed to find template after failing to " - "install it the second time"); - } - - if (!std::filesystem::exists(template_path)) { - throw std::runtime_error("Template not found in path"); - } - - Utils::verbose << "Copying template from: " << template_path << " to " - << tmp_gen_path << std::endl; - - for (std::filesystem::directory_entry entry : - std::filesystem::recursive_directory_iterator(template_path)) { - std::string relative_path = - entry.path().string().substr(template_path.string().length() + 1); - - std::filesystem::path new_file_path = tmp_gen_path / relative_path; - - Utils::verbose << "Copying: " << relative_path + " to " - << new_file_path.string() << std::endl; - - try { - - if (std::filesystem::is_directory(entry)) { - std::filesystem::create_directories(new_file_path); - continue; - } - - } catch (std::exception &e) { - - throw std::runtime_error("Failed to create directories: " + - tmp_gen_path.string()); - } - - try { - - std::filesystem::copy_file(entry.path(), new_file_path); - - } catch (std::exception &e) { - throw std::runtime_error("Failed to copy file: " + relative_path); - } - } - - if (std::filesystem::exists(override_path)) { - Utils::verbose << "Copying override files from: " << override_path - << " to " << tmp_gen_path << std::endl; - - for (std::filesystem::directory_entry entry : - std::filesystem::recursive_directory_iterator(override_path)) { - std::string relative_path = - entry.path().string().substr(override_path.string().length() + 1); - - std::filesystem::path new_file_path = tmp_gen_path / relative_path; - - Utils::verbose << "Copying: " << relative_path + " to " - << new_file_path.string() << std::endl; - } - } - return tmp_gen_path; - } - - std::vector &TemplateManager::getInstalled() { + std::unordered_map> & + TemplateManager::getInstalled() { return installed; } @@ -214,7 +131,7 @@ namespace Frate::Project { try { load_index(); - } catch (std::exception &e) { + } catch (TemplateIndexNotLoaded &e) { Utils::error << e.what() << std::endl; throw std::runtime_error("Failed to load template index"); } @@ -230,13 +147,13 @@ namespace Frate::Project { for (TemplateMeta template_info : index) { // Searching for the template in index - if (template_info.name == name) { - Utils::info << "Downloading template at: " << template_info.git + if (template_info.getName() == name) { + Utils::info << "Downloading template at: " << template_info.getGit() << std::endl; try { - git.setNoCheckout(true).clone(template_info.git).log(); + git.setNoCheckout(true).clone(template_info.getGit()).log(); } catch (std::exception &e) { Utils::error << e.what() << std::endl; @@ -254,12 +171,12 @@ namespace Frate::Project { // appending on the hash to the path so we can have multiple versions new_template_path /= latest_commit.hash; - template_info.hash = latest_commit.hash; + template_info.setHash(latest_commit.hash); if (!hash.empty()) { - template_info.hash = hash; + template_info.setHash(hash); } - if (is_installed(name, template_info.hash)) { + if (is_installed(name, template_info.getHash())) { Utils::warning << "Template already installed" << std::endl; return template_info; } @@ -294,6 +211,7 @@ namespace Frate::Project { } catch (std::exception &e) { + Utils::error << e.what() << std::endl; throw std::runtime_error("Failed to create directories: " + new_template_path.string()); } @@ -304,21 +222,24 @@ namespace Frate::Project { latest_commit.hash); } catch (std::exception &e) { - + Utils::error << e.what() << std::endl; throw std::runtime_error("Failed to transfer template"); } - InstalledTemplate installed_template; - installed_template.getName() = name; - installed_template.getGit() = template_info.git; - // We only want to add the latest commit - installed_template.getCommits().push_back(latest_commit); - - Utils::info << "Putting template in config: " << installed_template - << std::endl; - - // Installing the template in the config - installed.push_back(installed_template); + installed[name][latest_commit.hash] = template_info; + + // InstalledTemplate installed_template; + // installed_template.getName() = name; + // installed_template.getGit() = template_info.git; + // // We only want to add the latest commit + // installed_template.getCommits().push_back(latest_commit); + // + // Utils::info << "Putting template in config: " << + // installed_template + // << std::endl; + // + // // Installing the template in the config + // installed.push_back(installed_template); // Cleanup diff --git a/src/Utils/ConfigManager/Config.cpp b/src/Utils/ConfigManager/Config.cpp index bdecf3f..8c7e03f 100644 --- a/src/Utils/ConfigManager/Config.cpp +++ b/src/Utils/ConfigManager/Config.cpp @@ -23,7 +23,7 @@ namespace Frate::Config { Command::RemoteServer ConfigManager::getBuildServer() { if (this->build_server.empty()) { Utils::error << "No build server set" << std::endl; - exit(1); + throw std::runtime_error("No build server set"); } for (auto &server : this->build_servers) { if (server.name == this->build_server) { diff --git a/src/Utils/Debug.cpp b/src/Utils/Debug.cpp index c207277..a7cd75a 100644 --- a/src/Utils/Debug.cpp +++ b/src/Utils/Debug.cpp @@ -1,15 +1,14 @@ #include -#include -#include +#include namespace Frate::Utils { void debug(std::string something) { #ifdef DEBUG cpptrace::generate_trace().print(); - DEBUGTHIS(something); + Utils::error << something << std::endl; #elif TEST cpptrace::generate_trace().print(); - DEBUGTHIS(something); + Utils::error << something << std::endl; #else std::cout << termcolor::bright_red << something << std::endl; #endif diff --git a/src/Utils/FileFilter.cpp b/src/Utils/FileFilter.cpp index 4ecbfa1..1138e0d 100644 --- a/src/Utils/FileFilter.cpp +++ b/src/Utils/FileFilter.cpp @@ -5,48 +5,66 @@ namespace Frate::Utils { using std::filesystem::path; - FileFilter::FileFilter(std::filesystem::path &root_path) { - this->root_path = root_path; + void FileFilter::gen_paths() { + + if (paths_generated) { + return; + } + + if (!std::filesystem::exists(root_path)) { + throw FileFilterException("Root path does not exist: " + root_path.string()); + } + + for (auto &entry : + std::filesystem::recursive_directory_iterator(root_path)) { + paths_to_filter.emplace_back(entry.path()); + } + + paths_generated = true; } bool FileFilter::is_valid(const std::filesystem::path &check_path) { if (!std::filesystem::exists(check_path)) { - throw std::runtime_error("Path does not exist"); + throw FileFilterException("Check path does not exist: " + check_path.string()); } - for (std::string &extension : extensions) { + for (std::string &extension : filter_extensions) { if (check_path.extension() == extension) { return true; } } - for (std::string &prefix : prefixes) { + for (std::string &prefix : filter_prefixes) { if (check_path.filename().string().find(prefix) != std::string::npos) { return true; } } - for (path &curr_path : paths) { + for (path &curr_path : filter_dirs) { if (check_path.string().find(root_path.string() + "/" + curr_path.string()) != std::string::npos) { return true; } } + for (std::string &file : filter_files) { + if (check_path.filename().string() == file) { + return true; + } + } return false; } std::vector - FileFilter::filterOut(std::filesystem::path &root_path) { - std::vector filtered_paths{}; + FileFilter::filterOut() { - if (!std::filesystem::exists(root_path)) { - throw std::runtime_error("Path does not exist"); - } + gen_paths(); - for (std::filesystem::directory_entry entry : - std::filesystem::recursive_directory_iterator(root_path)) { - if (!is_valid(entry.path())) { - filtered_paths.emplace_back(entry.path()); + std::vector filtered_paths{}; + + for(auto &path : paths_to_filter){ + if(!is_valid(path)){ + filtered_paths.emplace_back(path); } } + return filtered_paths; } @@ -67,16 +85,16 @@ namespace Frate::Utils { return filtered_paths; } - std::vector - FileFilter::filterIn(std::filesystem::path &root_path) { + std::vector FileFilter::filterIn() { - std::vector filtered_paths{}; - for (std::filesystem::directory_entry entry : - std::filesystem::recursive_directory_iterator(root_path)) { - if (is_valid(entry.path())) { - filtered_paths.emplace_back(entry.path()); + gen_paths(); + + for(auto &path : paths_to_filter){ + if(is_valid(path)){ + filtered_paths.emplace_back(path); } } + return filtered_paths; } @@ -85,6 +103,7 @@ namespace Frate::Utils { std::filesystem::path &root_path) { std::vector filtered_paths{}; + for (std::filesystem::directory_entry entry : std::filesystem::recursive_directory_iterator(root_path)) { for (FileFilter &filter : filters) { @@ -97,24 +116,24 @@ namespace Frate::Utils { }; FileFilter &FileFilter::addExtension(std::string &extension) { - extensions.emplace_back(extension); + filter_extensions.emplace_back(extension); return *this; } FileFilter &FileFilter::addPrefix(std::string &prefix) { - prefixes.emplace_back(prefix); + filter_prefixes.emplace_back(prefix); return *this; } FileFilter &FileFilter::addPath(std::filesystem::path &path) { - paths.emplace_back(path); + filter_dirs.emplace_back(path); return *this; } FileFilter & - FileFilter::addPaths(std::initializer_list paths) { + FileFilter::addDirs(std::initializer_list paths) { for (std::filesystem::path path : paths) { - this->paths.emplace_back(path); + this->filter_dirs.emplace_back(path); } return *this; } @@ -122,7 +141,7 @@ namespace Frate::Utils { FileFilter & FileFilter::addExtensions(std::initializer_list extensions) { for (std::string extension : extensions) { - this->extensions.emplace_back(extension); + this->filter_extensions.emplace_back(extension); } return *this; } @@ -130,7 +149,19 @@ namespace Frate::Utils { FileFilter & FileFilter::addPrefixes(std::initializer_list prefixes) { for (std::string prefix : prefixes) { - this->prefixes.emplace_back(prefix); + this->filter_prefixes.emplace_back(prefix); + } + return *this; + } + + FileFilter &FileFilter::addFile(std::string &file) { + this->filter_files.emplace_back(file); + return *this; + } + + FileFilter &FileFilter::addFiles(std::initializer_list files) { + for (std::string file : files) { + this->filter_files.emplace_back(file); } return *this; }