diff --git a/container/src/root/common/type.hh b/container/src/root/common/type.hh index 95650a8..ebb54ce 100644 --- a/container/src/root/common/type.hh +++ b/container/src/root/common/type.hh @@ -25,6 +25,7 @@ #include #include +#include //! \namespace dfi //! \since docker-finance 1.0.0 @@ -96,6 +97,227 @@ struct InvalidArgument final : public Exception { } }; + +//! \brief Data type of underlying operating system locations for pluggable pseudo-paths +//! \todo C++23 concept pair-like +//! \since docker-finance 1.1.0 +class PluggablePath +{ + using t_pair = std::pair; + + public: + //! \brief Construct with {repository, custom} pluggable directory locations + //! \param pair An object where the first element is the repository + //! absolute directory path and the second element is the custom + //! absolute directory path. + explicit PluggablePath(const t_pair& pair) : m_pair(pair) {} + ~PluggablePath() = default; + + PluggablePath(const PluggablePath&) = default; + PluggablePath& operator=(const PluggablePath&) = default; + + PluggablePath(PluggablePath&&) = default; + PluggablePath& operator=(PluggablePath&&) = default; + + public: + //! \brief Set operating system path to repository pluggable + //! \return NPI reference + auto& repo(const std::string& arg) + { + m_pair.first = arg; + return *this; + } + + //! \brief Set operating system path to custom pluggable + //! \return NPI reference + auto& custom(const std::string& arg) + { + m_pair.second = arg; + return *this; + } + + //! \return Operating system path to repository pluggable + const std::string& repo() const { return m_pair.first; } + + //! \return Operating system path to custom pluggable + const std::string& custom() const { return m_pair.second; } + + private: + t_pair m_pair; +}; + +//! \brief Data type for the pluggable's namespace +//! \todo C++23 concept pair-like +//! \since docker-finance 1.1.0 +class PluggableSpace +{ + using t_pair = std::pair; + + public: + //! \brief Construct data type with most-outer and inner namespaces + explicit PluggableSpace(const t_pair& pair) : m_pair(pair) {} + ~PluggableSpace() = default; + + PluggableSpace(const PluggableSpace&) = default; + PluggableSpace& operator=(const PluggableSpace&) = default; + + PluggableSpace(PluggableSpace&&) = default; + PluggableSpace& operator=(PluggableSpace&&) = default; + + public: + //! \brief Set pluggable's most-outer namespace + //! \return NPI reference + auto& outer(const std::string& arg) + { + m_pair.first = arg; + return *this; + } + + //! \brief Set pluggable's inner namespace(s) + //! \return NPI reference + auto& inner(const std::string& arg) + { + m_pair.second = arg; + return *this; + } + + //! \return Pluggable's most-outer namespace + const std::string& outer() const { return m_pair.first; } + + //! \return Pluggable's inner namespace(s) + const std::string& inner() const { return m_pair.second; } + + private: + t_pair m_pair; +}; + +//! \brief Data type for pluggable auto-(un)loader arguments +//! \todo C++23 concept pair-like +//! \since docker-finance 1.1.0 +class PluggableArgs +{ + using t_pair = std::pair; + + public: + //! \brief Construct data type with load/unload arguments + explicit PluggableArgs(const t_pair& pair) : m_pair(pair) {} + ~PluggableArgs() = default; + + PluggableArgs(const PluggableArgs&) = default; + PluggableArgs& operator=(const PluggableArgs&) = default; + + PluggableArgs(PluggableArgs&&) = default; + PluggableArgs& operator=(PluggableArgs&&) = default; + + public: + //! \brief Set pluggable loader argument + //! \return NPI reference + auto& load(const std::string& arg) + { + m_pair.first = arg; + return *this; + } + + //! \brief Set pluggable unloader argument + //! \return NPI reference + auto& unload(const std::string& arg) + { + m_pair.second = arg; + return *this; + } + + //! \return Pluggable loader argument + const std::string& load() const { return m_pair.first; } + + //! \return Pluggable unloader argument + const std::string& unload() const { return m_pair.second; } + + private: + t_pair m_pair; +}; + +//! \concept dfi::common::type::PPath +//! \brief Pluggable constrained data type (PluggablePath) +//! \ref dfi::common::type::PluggablePath +//! \since docker-finance 1.1.0 +template +concept PPath = requires(t_path path, PluggablePath plug) { + path.operator()().operator=(plug); + path.operator()().repo("").custom(""); + path.operator()().repo(); + path.operator()().custom(); +}; + +//! \concept dfi::common::type::PSpace +//! \brief Pluggable constrained data type (PluggableSpace) +//! \ref dfi::common::type::PluggableSpace +//! \since docker-finance 1.1.0 +template +concept PSpace = requires(t_space space, PluggableSpace plug) { + space.operator()().operator=(plug); + space.operator()().outer("").inner(""); + space.operator()().outer(); + space.operator()().inner(); +}; + +//! \concept dfi::common::type::PArgs +//! \brief Pluggable constrained data type (PluggableArgs) +//! \ref dfi::common::type::PluggableArgs +//! \since docker-finance 1.1.0 +template +concept PArgs = requires(t_args args, PluggableArgs plug) { + args.operator()().operator=(plug); + args.operator()().load("").unload(""); + args.operator()().load(); + args.operator()().unload(); +}; + +//! \brief Data type for pluggable handler +//! \since docker-finance 1.1.0 +template +class Pluggable +{ + public: + explicit Pluggable( + const t_path& path, + const t_space& space, + const t_args& args) + : m_path(path), m_space(space), m_args(args) + { + } + ~Pluggable() = default; + + Pluggable(const Pluggable&) = default; + Pluggable& operator=(const Pluggable&) = default; + + Pluggable(Pluggable&&) = default; + Pluggable& operator=(Pluggable&&) = default; + + public: + //! \brief Mutator to underlying path type + t_path& path() { return m_path; } + + //! \brief Mutator to underlying space type + t_space& space() { return m_space; } + + //! \brief Mutator to underlying args type + t_args& args() { return m_args; } + + public: + //! \brief Accessor to underlying path type + const t_path& path() const { return m_path; } + + //! \brief Accessor to underlying space type + const t_space& space() const { return m_space; } + + //! \brief Accessor to underlying args type + const t_args& args() const { return m_args; } + + private: + t_path m_path; + t_space m_space; + t_args m_args; +}; } // namespace type } // namespace common } // namespace dfi diff --git a/container/src/root/common/utility.hh b/container/src/root/common/utility.hh index 1d50aa4..fc1a220 100644 --- a/container/src/root/common/utility.hh +++ b/container/src/root/common/utility.hh @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "./type.hh" @@ -273,6 +274,305 @@ std::string make_timestamp() std::strftime(time.data(), time.size(), "%FT%TZ", std::gmtime(&t)); return std::string{time.data()}; } + +//! \brief Base pseudo-path handler for various pluggables (plugins and macros) +//! +//! \details Handles pluggable paths, typically used by pluggable +//! implementations when auto-(un|re)loading. +//! +//! \ingroup cpp_plugin_impl cpp_macro_impl +//! \since docker-finance 1.1.0 +class PluggablePath +{ + public: + //! \brief Parses (or re-parses) constructed types + //! \warning Only call this function after constructing if underlying type::PluggablePath has been changed (post-construction) + //! \return NPI reference + auto& parse() + { + // Parse out pseudo tag + const std::string type{m_pseudo.substr(0, m_pseudo.find('/'))}; + + std::string pruned{m_pseudo}; + pruned.erase(0, pruned.find('/') + 1); + + // Set family group + m_family = pruned; + + // Set parent directory + m_parent = pruned.substr(0, pruned.find('/')); + + // Set child (filename) + m_child = pruned.substr(pruned.find_last_of('/') + 1); + + // Set absolute path + std::string absolute{pruned}; + if (type == "repo") + { + m_is_repo = true; + absolute.insert(0, m_path.repo()); + } + else if (type == "custom") + { + m_is_custom = true; + absolute.insert(0, m_path.custom()); + } + else + { + throw_ex( + "must be of type 'repo/' or 'custom/'"); + } + m_absolute = std::move(absolute); + + return *this; + } + + protected: + //! \brief Sets pluggable absolute path from given pseudo path + //! \param pseudo Pseudo-path ('repo/' or 'custom/') + //! \param base Operating system absolute paths to pluggable locations + //! \exception type::RuntimeError If not a valid pseudo-path + PluggablePath(const std::string& pseudo, const type::PluggablePath& path) + : m_pseudo(pseudo), m_path(path), m_is_repo(false), m_is_custom(false) + { + parse(); + } + ~PluggablePath() = default; + + PluggablePath(const PluggablePath&) = default; + PluggablePath& operator=(const PluggablePath&) = default; + + PluggablePath(PluggablePath&&) = default; + PluggablePath& operator=(PluggablePath&&) = default; + + public: + //! \brief Mutator to underlying type::PluggablePath + //! \warning Use with caution: underlying implementation may change + auto& operator()() { return m_path; } + + //! \brief Accessor to underlying type::PluggablePath + //! \warning Use with caution: underlying implementation may change + const auto& operator()() const { return m_path; } + + public: + //! \return The pluggable's pseudo-path + const std::string& pseudo() const { return m_pseudo; } + + //! \return The pluggable's operating system absolute path + const std::string& absolute() const { return m_absolute; } + + // TODO(unassigned): relative() to current working dir + + //! \return The pluggable's parent directory + //! \note This also represents the expected namespace used for pluggable auto-loading + const std::string& parent() const { return m_parent; } + + //! \return The pluggable's child (filename) + const std::string& child() const { return m_child; } + + //! \return The pluggable's group of parent member and child filename + const std::string& family() const { return m_family; } + + //! \return true if pseudo-path describes a repository location + bool is_repo() const { return m_is_repo; } + + //! \return true if pseudo-path describes a custom location + bool is_custom() const { return m_is_custom; } + + private: + type::PluggablePath m_path; + std::string m_pseudo, m_absolute; + std::string m_parent, m_family, m_child; + bool m_is_repo, m_is_custom; +}; + +//! \brief Base namespace handler for various pluggables (plugins and macros) +//! +//! \details Handles pluggable namespace, typically used by pluggable +//! implementations when auto-(un|re)loading. +//! +//! \ingroup cpp_plugin_impl cpp_macro_impl +//! \since docker-finance 1.1.0 +class PluggableSpace +{ + protected: + explicit PluggableSpace(const type::PluggableSpace& space) : m_space(space) {} + ~PluggableSpace() = default; + + PluggableSpace(const PluggableSpace&) = default; + PluggableSpace& operator=(const PluggableSpace&) = default; + + PluggableSpace(PluggableSpace&&) = default; + PluggableSpace& operator=(PluggableSpace&&) = default; + + public: + //! \brief Mutator to underlying type::PluggableSpace + //! \warning Use with caution: underlying implementation may change + auto& operator()() { return m_space; } + + //! \brief Accessor to underlying type::PluggableSpace + //! \warning Use with caution: underlying implementation may change + const auto& operator()() const { return m_space; } + + public: + //! \return The pluggable's outer namespace + const std::string& outer() const { return m_space.outer(); } + + //! \return The pluggable's inner namespace + const std::string& inner() const { return m_space.inner(); } + + //! \return true if the outer namespace was set + bool has_outer() const { return !m_space.outer().empty(); } + + //! \return true if the inner namespace was set + bool has_inner() const { return !m_space.inner().empty(); } + + private: + type::PluggableSpace m_space; +}; + +//! \brief Base argument handler for various pluggables (plugins and macros) +//! +//! \details Handles pluggable arguments, typically used by pluggable +//! implementations when auto-(un|re)loading. +//! +//! \ingroup cpp_plugin_impl cpp_macro_impl +//! \since docker-finance 1.1.0 +class PluggableArgs +{ + protected: + explicit PluggableArgs(const type::PluggableArgs& args) : m_args(args) {} + ~PluggableArgs() = default; + + PluggableArgs(const PluggableArgs&) = default; + PluggableArgs& operator=(const PluggableArgs&) = default; + + PluggableArgs(PluggableArgs&&) = default; + PluggableArgs& operator=(PluggableArgs&&) = default; + + public: + //! \brief Mutator to underlying type::PluggableArgs + //! \warning Use with caution: underlying implementation may change + auto& operator()() { return m_args; } + + //! \brief Accessor to underlying type::PluggableArgs + //! \warning Use with caution: underlying implementation may change + const auto& operator()() const { return m_args; } + + public: + //! \return The pluggable's loader argument + const std::string& load() const { return m_args.load(); } + + //! \return The pluggable's unloader argument + const std::string& unload() const { return m_args.unload(); } + + //! \return true if the load argument was set + bool has_load() const { return !m_args.load().empty(); } + + //! \return true if the unload argument was set + bool has_unload() const { return !m_args.unload().empty(); } + + private: + type::PluggableArgs m_args; +}; + +//! \concept dfi::common::PPath +//! \brief Pluggable base implementation constraint (PluggablePath) +//! \ref dfi::common::PluggablePath +//! \ingroup cpp_plugin_impl cpp_macro_impl +//! \since docker-finance 1.1.0 +template +concept PPath = std::derived_from; + +//! \concept dfi::common::PSpace +//! \brief Pluggable base implementation constraint (PluggableSpace) +//! \ref dfi::common::PluggableSpace +//! \ingroup cpp_plugin_impl cpp_macro_impl +//! \since docker-finance 1.1.0 +template +concept PSpace = std::derived_from; + +//! \concept dfi::common::PArgs +//! \brief Pluggable base implementation constraint (PluggableArgs) +//! \ref dfi::common::PluggableArgs +//! \ingroup cpp_plugin_impl cpp_macro_impl +//! \since docker-finance 1.1.0 +template +concept PArgs = std::derived_from; + +//! \brief Base pluggable handler +//! \ingroup cpp_plugin_impl cpp_macro_impl +//! \since docker-finance 1.1.0 +template +class Pluggable +{ + protected: + explicit Pluggable(const type::Pluggable& plug) + : m_plug(plug) + { + } + ~Pluggable() = default; + + Pluggable(const Pluggable&) = default; + Pluggable& operator=(const Pluggable&) = default; + + Pluggable(Pluggable&&) = default; + Pluggable& operator=(Pluggable&&) = default; + + public: + //! \brief Mutator to underlying type::Pluggable + //! \warning Use with caution: underlying implementation may change + auto& operator()() { return m_plug; } + + //! \brief Accessor to underlying type::Pluggable + //! \warning Use with caution: underlying implementation may change + const auto& operator()() const { return m_plug; } + + public: + //! \brief Loads a single pluggable + //! \return NPI reference + //! \ingroup cpp_plugin_impl cpp_macro_impl + //! \since docker-finance 1.1.0 + const auto& load() const + { + // Load pluggable file + ::dfi::common::load(m_plug.path().absolute()); + + // Execute pluggable's loader + const std::string s{ + "dfi::" + m_plug.space().outer() + "::" + m_plug.space().inner()}; + ::dfi::common::line(s + "::load(\"" + m_plug.args().load() + "\")"); + + return *this; + } + + //! \brief Unloads a single pluggable + //! \return NPI reference + //! \ingroup cpp_plugin_impl cpp_macro_impl + //! \since docker-finance 1.1.0 + const auto& unload() const + { + // Execute pluggable's unloader + const std::string s{ + "dfi::" + m_plug.space().outer() + "::" + m_plug.space().inner()}; + ::dfi::common::line(s + "::unload(\"" + m_plug.args().unload() + "\")"); + + // Unload pluggable file + ::dfi::common::unload(m_plug.path().absolute()); + + return *this; + } + + //! \brief Reloads a single pluggable + //! \return NPI reference + //! \ingroup cpp_plugin_impl cpp_macro_impl + //! \since docker-finance 1.1.0 + const auto& reload() const { return unload().load(); } + + private: + type::Pluggable m_plug; +}; + } // namespace common } // namespace dfi diff --git a/container/src/root/macro/common/utility.hh b/container/src/root/macro/common/utility.hh index 518082e..c61dc8c 100644 --- a/container/src/root/macro/common/utility.hh +++ b/container/src/root/macro/common/utility.hh @@ -42,47 +42,110 @@ namespace macro //! \since docker-finance 1.0.0 namespace common { -//! \brief Load file by path -//! \ingroup cpp_macro_impl +//! \brief Load macro file by path //! \details //! Example: -//!
  root [0] dfi::macro::load("test/unit.C")
+//!
  root [0] dfi::macro::load("macro/test/unit.C")
//! //! Will load: //!
  root/macro/test/unit.C
//! -//! \note Parent directory is `root/macro` -//! \todo Isolate for macros only (similar to plugins) +//! \note Parent directory is `root` by default but accepts absolute paths outside of `root` +//! \todo Refactor with dfi::common::Pluggable +//! \ingroup cpp_macro_impl void load(const std::string& path) { - ::dfi::common::Command::load(path); + ::dfi::common::load(path); } -//! \brief Wrapper to load files by list of paths -//! \ingroup cpp_macro_impl +//! \brief Load macro files by list of paths //! \details //! Example: -//!
  root [0] dfi::macro::load({"test/unit.C", "test/benchmark.C"})
+//!
  root [0] dfi::macro::load({"macro/test/unit.C", "macro/test/benchmark.C"})
//! //! Will load: -//!
  root/macro/test/unit.C and root/test/benchmark.C
+//!
  root/macro/test/unit.C and root/macro/test/benchmark.C
//! -//! \note Parent directory is `root/macro` -//! \todo Isolate for macros only (similar to plugins) +//! \note Parent directory is `root` by default but accepts absolute paths outside of `root` +//! \ingroup cpp_macro_impl +//! \todo Refactor with dfi::common::Pluggable void load(const std::initializer_list& paths) { - for (const auto& path : paths) - ::dfi::common::Command::load(path); + ::dfi::common::load(paths); } -//! \deprecated This will be removed in the v2 API; use `dfi::common` namespace instead +//! \brief Unload macro file by path +//! \details +//! Example: +//!
  root [0] dfi::macro::unload("macro/test/unit.C")
+//! +//! Will unload: +//!
  root/macro/test/unit.C
+//! +//! \note Parent directory is `root` by default but accepts absolute paths outside of `root` +//! \ingroup cpp_macro_impl +//! \todo Refactor with dfi::common::Pluggable +void unload(const std::string& path) +{ + ::dfi::common::unload(path); +} + +//! \brief Unload macro files by list of paths +//! \details +//! Example: +//!
  root [0] dfi::macro::unload({"macro/test/unit.C", "macro/test/benchmark.C"})
+//! +//! Will unload: +//!
  root/macro/test/unit.C and root/macro/test/benchmark.C
+//! +//! \note Parent directory is `root` by default but accepts absolute paths outside of `root` +//! \ingroup cpp_macro_impl +//! \todo Refactor with dfi::common::Pluggable +void unload(const std::initializer_list& paths) +{ + ::dfi::common::unload(paths); +} + +//! \deprecated This will be removed in the v2 API; use the `dfi::common` free functions instead //! \todo Remove in 2.0.0 //! \since docker-finance 1.0.0 class Command : public ::dfi::common::Command { }; -//! \deprecated This will be removed in the v2 API; use `dfi::common` namespace instead +//! \brief Reload macro file by path +//! \details +//! Example: +//!
  root [0] dfi::macro::reload("macro/test/unit.C")
+//! +//! Will reload: +//!
  root/macro/test/unit.C
+//! +//! \note Parent directory is `root` by default but accepts absolute paths outside of `root` +//! \ingroup cpp_macro_impl +//! \todo Refactor with dfi::common::Pluggable +void reload(const std::string& path) +{ + ::dfi::common::reload(path); +} + +//! \brief Reload macro files by list of paths +//! \ingroup cpp_macro_impl +//! \details +//! Example: +//!
  root [0] dfi::macro::reload({"macro/test/unit.C", "macro/test/benchmark.C"})
+//! +//! Will reload: +//!
  root/macro/test/unit.C and root/macro/test/benchmark.C
+//! +//! \note Parent directory is `root` by default but accepts absolute paths outside of `root` +//! \todo Refactor with dfi::common::Pluggable +void reload(const std::initializer_list& paths) +{ + ::dfi::common::reload(paths); +} + +//! \deprecated This will be removed in the v2 API; use the `dfi::common` free function instead //! \todo Remove in 2.0.0 //! \since docker-finance 1.0.0 std::string get_env(const std::string& var) @@ -90,7 +153,7 @@ std::string get_env(const std::string& var) return ::dfi::common::get_env(var); } -//! \deprecated This will be removed in the v2 API; use `dfi::common` namespace instead +//! \deprecated This will be removed in the v2 API; use the `dfi::common` free function instead //! \todo Remove in 2.0.0 //! \since docker-finance 1.0.0 int exec(const std::string& cmd) @@ -98,7 +161,7 @@ int exec(const std::string& cmd) return ::dfi::common::exec(cmd); } -//! \deprecated This will be removed in the v2 API; use `dfi::common` namespace instead +//! \deprecated This will be removed in the v2 API; use the `dfi::common` free function instead //! \todo Remove in 2.0.0 //! \since docker-finance 1.0.0 std::string make_timestamp() @@ -111,6 +174,7 @@ std::string make_timestamp() //! \ref dfi::macro::common::load(const std::string& path) //! \ingroup cpp_macro //! \since docker-finance 1.1.0 +//! \todo Refactor with dfi::common::Pluggable void load(const std::string& path) { common::load(path); @@ -119,12 +183,49 @@ void load(const std::string& path) //! \ref dfi::macro::common::load(const std::initializer_list& paths) //! \ingroup cpp_macro //! \since docker-finance 1.1.0 +//! \todo Refactor with dfi::common::Pluggable void load(const std::initializer_list& paths) { common::load(paths); } -// TODO(afiore): unload +//! \brief Convenience wrapper to inner common unloader +//! \ref dfi::macro::common::unload(const std::string& path) +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +//! \todo Refactor with dfi::common::Pluggable +void unload(const std::string& path) +{ + common::unload(path); +} +//! \brief Convenience wrapper to inner common unloader +//! \ref dfi::macro::common::unload(const std::initializer_list& paths) +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +//! \todo Refactor with dfi::common::Pluggable +void unload(const std::initializer_list& paths) +{ + common::unload(paths); +} + +//! \brief Convenience wrapper to inner common reloader +//! \ref dfi::macro::common::reload(const std::string& path) +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +//! \todo Refactor with dfi::common::Pluggable +void reload(const std::string& path) +{ + common::reload(path); +} +//! \brief Convenience wrapper to inner common reloader +//! \ref dfi::macro::common::reload(const std::initializer_list& paths) +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +//! \todo Refactor with dfi::common::Pluggable +void reload(const std::initializer_list& paths) +{ + common::reload(paths); +} } // namespace macro } // namespace dfi diff --git a/container/src/root/plugin/common/utility.hh b/container/src/root/plugin/common/utility.hh index 498933d..c75035c 100644 --- a/container/src/root/plugin/common/utility.hh +++ b/container/src/root/plugin/common/utility.hh @@ -25,7 +25,9 @@ #include #include +#include +#include "../../common/type.hh" #include "../../common/utility.hh" //! \namespace dfi @@ -35,7 +37,7 @@ namespace dfi //! \namespace dfi::plugin //! \brief docker-finance plugins //! \warning All plugins (repo/custom) must exist within this namespace -//! and work within their own inner namespace +//! and work within their own inner namespace //! \since docker-finance 1.0.0 namespace plugin { @@ -44,94 +46,544 @@ namespace plugin //! \since docker-finance 1.1.0 namespace common { -//! \brief Load plugin by pseudo-paths -//! \details Wrapper to load from directory outside of default tree -//! \param path Must be of string "repo/file" or "custom/file" where file is -//! a plugin filename that exists in repository plugin path or -//! a client-side end-user's custom plugin path. -//! \ingroup cpp_plugin_impl -//! \details -//! Example: -//!
  root [0] dfi::plugin::load("repo/example.cc")
-//! -//! Will load: -//!
  ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example.cc
-//! -//! Example: -//!
  root [0] dfi::plugin::load("custom/example.cc")
-//! -//! Will load: -//!
  ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example.cc
-//! -//! \note Parent directory for all plugins are outside of repository's `root` directory -void load(const std::string& path) +//! \brief Plugin's pseudo-path handler +//! \note Parent directory for all plugins are outside of repository's `root` directory +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +class PluginPath final : public ::dfi::common::PluggablePath { - const std::string type{path.substr(0, path.find("/"))}; + public: + //! \param pseudo Must be string "repo/" or "custom/" + //! where "repo" is a repository plugin, "custom" is a custom plugin, + //! and where is a pseudo-path relative to plugin's + //! operating system location. + explicit PluginPath(const std::string& pseudo) + : ::dfi::common::PluggablePath( + pseudo, + ::dfi::common::type::PluggablePath( + {::dfi::common::get_env("DOCKER_FINANCE_CONTAINER_REPO") + + "/plugins/root/", + ::dfi::common::get_env("DOCKER_FINANCE_CONTAINER_PLUGINS") + + "/root/"})) + { + } +}; - std::string file{path}; - file.erase(0, file.find("/") + 1); +//! \brief Plugin's namespace handler +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +class PluginSpace final : public ::dfi::common::PluggableSpace +{ + public: + explicit PluginSpace(const std::string& inner) + : ::dfi::common::PluggableSpace( + ::dfi::common::type::PluggableSpace({"plugin", inner})) + { + } +}; - if (type == "repo") - { - file.insert( - 0, - ::dfi::common::get_env("DOCKER_FINANCE_CONTAINER_REPO") - + "/plugins/root/"); - } - else if (type == "custom") - { - file.insert( - 0, - ::dfi::common::get_env("DOCKER_FINANCE_CONTAINER_PLUGINS") - + "/root/"); - } - else - { - ::dfi::common::throw_ex<::dfi::common::type::RuntimeError>( - "must be of type 'repo/' or 'custom/'"); - } +//! \brief Plugin's arguments handler +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +class PluginArgs final : public ::dfi::common::PluggableArgs +{ + public: + explicit PluginArgs(const std::string& load, const std::string& unload) + : ::dfi::common::PluggableArgs( + ::dfi::common::type::PluggableArgs({load, unload})) + { + } +}; - ::dfi::common::Command::load(file); +//! \brief Plugin handler +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +class Plugin final + : public ::dfi::common::Pluggable +{ + public: + explicit Plugin( + ::dfi::common::type::Pluggable + plugin) + : ::dfi::common::Pluggable(plugin) + { + } +}; +} // namespace common + +// +// Loader +// + +//! \brief Loads a single plugin +//! +//! \param path common::PluginPath +//! \param args common::PluginArgs +//! +//! \ref dfi::plugin::common::Plugin +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::load({"repo/example/example.cc"}, {"using foo = int; foo f;"}) +//! +//! Will load: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc +//! +//! and pass `using foo = int; foo f;` to the plugin's loader (plugin-implementation defined). +//! +//! +//! Example 2: +//! +//!   root [0] dfi::plugin::load({"custom/example/example.cc"}, {"using bar = char; bar b;"}) +//! +//! Will load: +//! +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! and pass `using bar = char; bar b;` to the plugin's loader (plugin-implementation defined). +//! +//! \warning +//! To utilize plugin auto-loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void load(const common::PluginPath& path, const common::PluginArgs& args) +{ + ::dfi::common::type:: + Pluggable + type{path, common::PluginSpace{path.parent()}, args}; + + common::Plugin plugin{type}; + plugin.load(); } -//! \brief Wrapper to load plugins by list of pseudo-paths -//! \ingroup cpp_plugin_impl -//! \param paths List must consist of string "repo/file" or "custom/file" where file is -//! a plugin filename that exists in repository plugin path or -//! a client-side end-user's custom plugin path. +//! \brief Convenience wrapper to load a list of single plugins with argument +//! +//! \ref load(const common::PluginPath& path, const common::PluginArgs& args) +//! +//! \param plugins List of single plugins +//! //! \details +//! //! Example: -//!
  root [0] dfi::plugin::load({"repo/example.cc", "custom/example.cc"})
+//! +//!   root [0] dfi::plugin::load({{"repo/example/example.cc", "using foo = int; foo f;"}, {"custom/example/example.cc", "using bar = char; bar b;"}}) //! //! Will load: -//!
  ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example.cc -//! and ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example.cc
//! -//! \note Parent directory for all plugins are outside of repository's `root` directory +//!   `${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc`\n +//!   `${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc` +//! +//! And pass plugin-implementation defined auto-loader arguments: +//! +//!   `using foo = int; foo f;` for `${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc`\n +//!   `using bar = char; bar b;` for `${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc` +//! +//! \warning +//! To utilize plugin auto-loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void load( + const std::initializer_list< + std::pair>& plugins) +{ + for (const auto& plugin : plugins) + load(plugin.first, plugin.second); +} + +//! \brief Convenience wrapper to load a single plugin with optional argument +//! +//! \ref load(const common::PluginPath& path, const common::PluginArgs& args) +//! +//! \param path Must be of string "repo/" or "custom/" to plugin location, as described by common::PluginPath +//! +//! \param arg Optional string argument to pass to plugin's auto-loader (plugin-implementation defined) +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::load("repo/example.cc", "using foo = int; foo f;") +//! +//! Will load: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc +//! +//! and pass `using foo = int; foo f;` to the plugin's loader (plugin-implementation defined). +//! +//! +//! Example 2: +//! +//!   root [0] dfi::plugin::load("custom/example/example.cc", "using bar = char; bar b;") +//! +//! Will load: +//! +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! and pass `using bar = char; bar b;` to the plugin's loader (plugin-implementation defined). +//! +//! \warning +//! To utilize plugin auto-loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void load(const std::string& path, const std::string& arg = {}) +{ + load(common::PluginPath{path}, common::PluginArgs{arg, ""}); +} + +//! \brief Convenience wrapper to load a list of plugins with no arguments +//! +//! \ref load(const std::string& path, const std::string& arg) +//! +//! \param path Must be of string "repo/" or "custom/" to plugin location, as described by common::PluginPath +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::load({"repo/example/example.cc", "custom/example/example.cc"}) +//! +//! Will load both: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc\n +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! \warning +//! To utilize plugin auto-loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.0.0 void load(const std::initializer_list& paths) { for (const auto& path : paths) - ::dfi::plugin::common::load(path); + load(path); } -} // namespace common -//! \brief Convenience wrapper to inner common loader +// +// Unloader +// + +//! \brief Unloads a single plugin +//! +//! \param path common::PluginPath +//! \param args common::PluginArgs +//! +//! \ref dfi::plugin::common::Plugin +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::unload({"repo/example/example.cc"}, {"using foo = int; foo f;"}) +//! +//! Will unload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc +//! +//! and pass `using foo = int; foo f;` to the plugin's unloader (plugin-implementation defined). +//! +//! +//! Example 2: +//! +//!   root [0] dfi::plugin::unload({"custom/example/example.cc"}, {"using bar = char; bar b;"}) +//! +//! Will unload: +//! +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! and pass `using bar = char; bar b;` to the plugin's unloader (plugin-implementation defined). +//! +//! \warning +//! To utilize plugin auto-unloader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-unloader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \ingroup cpp_plugin //! \since docker-finance 1.1.0 -void load(const std::string& path) +void unload(const common::PluginPath& path, const common::PluginArgs& args) { - common::load(path); + ::dfi::common::type:: + Pluggable + type{path, common::PluginSpace{path.parent()}, args}; + + common::Plugin plugin{type}; + plugin.unload(); } -//! \brief Convenience wrapper to inner common loader + +//! \brief Convenience wrapper to unload a list of single plugins with arguments +//! +//! \ref unload(const common::PluginPath& path, const common::PluginArgs& args) +//! +//! \param plugins List of single plugins +//! +//! \details +//! +//! Example: +//! +//!   root [0] dfi::plugin::unload({{"repo/example/example.cc", "using foo = int; foo f;"}, {"custom/example/example.cc", "using bar = char; bar b;"}}) +//! +//! Will unload: +//! +//!   `${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc`\n +//!   `${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc` +//! +//! And pass plugin-implementation defined auto-unloader arguments: +//! +//!   `using foo = int; foo f;` for `${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc`\n +//!   `using bar = char; bar b;` for `${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc` +//! +//! \warning +//! To utilize plugin auto-unloader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-unloader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \ingroup cpp_plugin //! \since docker-finance 1.1.0 -void load(const std::initializer_list& paths) +void unload( + const std::initializer_list< + std::pair>& plugins) { - common::load(paths); + for (const auto& plugin : plugins) + unload(plugin.first, plugin.second); } -// TODO(afiore): unload +//! \brief Convenience wrapper to unload a single plugin with optional argument +//! +//! \ref unload(const common::PluginPath& path, const common::PluginArgs& args) +//! +//! \param path Must be of string "repo/" or "custom/" to plugin location, as described by common::PluginPath +//! +//! \param arg Optional string argument to pass to plugin's auto-unloader (plugin-implementation defined) +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::unload("repo/example/example.cc", "using foo = int; foo f;") +//! +//! Will unload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc +//! +//! and pass `using foo = int; foo f;` to the plugin's unloader (plugin-implementation defined). +//! +//! +//! Example 2: +//! +//!   root [0] dfi::plugin::unload("custom/example/example.cc", "using bar = char; bar b;") +//! +//! Will unload: +//! +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! and pass `using bar = char; bar b;` to the plugin's unloader (plugin-implementation defined). +//! +//! \warning +//! To utilize plugin auto-unloader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-unloader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void unload(const std::string& path, const std::string& arg = {}) +{ + unload(common::PluginPath{path}, common::PluginArgs{"", arg}); +} +//! \brief Convenience wrapper to unload a list of plugins with no arguments +//! +//! \ref unload(const std::string& path, const std::string& arg) +//! +//! \param path Must be of string "repo/" or "custom/" to plugin location, as described by common::PluginPath +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::unload({"repo/example/example.cc", "custom/example/example.cc"}) +//! +//! Will unload both: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc\n +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! \warning +//! To utilize plugin auto-unloader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-unloader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void unload(const std::initializer_list& paths) +{ + for (const auto& path : paths) + unload(path); +} + +// +// Reloader +// + +//! \brief Reloads a single plugin +//! +//! \param path common::PluginPath +//! \param args common::PluginArgs +//! +//! \ref dfi::plugin::common::Plugin +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::reload({"repo/example/example.cc"}, {"using foo = int; foo f;", "using bar = char; bar b;"}) +//! +//! Will reload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc +//! +//! and pass `using bar = char; bar b;` to the plugin's unloader (plugin-implementation defined) +//! and pass `using foo = int; foo f;` to the plugin's loader (plugin-implementation defined) +//! +//! \warning +//! To utilize plugin auto-(un)loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-(un)loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void reload(const common::PluginPath& path, const common::PluginArgs& args) +{ + ::dfi::common::type:: + Pluggable + type{path, common::PluginSpace{path.parent()}, args}; + + common::Plugin plugin{type}; + plugin.reload(); +} + +//! \brief Convenience wrapper to unload a list of single plugins with arguments +//! +//! \ref reload(const common::PluginPath& path, const common::PluginArgs& args) +//! +//! \param plugins List of populated pair of common::PluginPath and common::PluginArgs +//! +//! \details +//! +//! Example: +//!   root [0] dfi::plugin::reload({{"repo/example/example.cc", "using foo = int; foo f;"}, {"custom/example/example.cc", "using bar = char; bar b;"}}) +//! +//! Will reload: +//! +//!   `${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc`\n +//!   `${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc` +//! +//! And pass plugin-implementation defined auto-loader arguments: +//! +//!   `using foo = int; foo f;` for `${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc`\n +//!   `using bar = char; bar b;` for `${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc` +//! +//! \warning +//! To utilize plugin auto-(un)loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-(un)loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void reload( + const std::initializer_list< + std::pair>& plugins) +{ + for (const auto& plugin : plugins) + reload(plugin.first, plugin.second); +} + +//! \brief Convenience wrapper to reload a single plugin with optional arguments +//! +//! \ref reload(const common::PluginPath& path, const common::PluginArgs& args) +//! +//! \param path Must be of string "repo/" or "custom/" to plugin location, as described by common::PluginPath +//! +//! \param args Optional pair of arguments to pass to plugin's auto-loader/unloader (plugin-implementation defined) +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::reload("repo/example/example.cc", {"using foo = int; foo f;", ""}) +//! +//! Will reload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc +//! +//! and pass `using foo = int; foo f;` to the plugin's auto-loader (plugin-implementation defined). +//! +//! +//! Example 2: +//! +//!   root [0] dfi::plugin::reload("custom/example/example.cc", {"", "using bar = char; bar b;"}) +//! +//! Will reload: +//! +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! and pass `using bar = char; bar b;` to the plugin's auto-unloader (plugin-implementation defined). +//! +//! \warning +//! To utilize plugin auto-(un)loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-(un)loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void reload( + const std::string& path, + const std::pair& args = {}) +{ + reload(common::PluginPath{path}, common::PluginArgs{args.first, args.second}); +} + +//! \brief Convenience wrapper to reload a list of plugins with no arguments +//! +//! \ref reload(const std::string& path, const std::string& arg) +//! +//! \param path Must be of string "repo/" or "custom/" to plugin location, as described by common::PluginPath +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::plugin::reload({"repo/example/example.cc", "custom/example/example.cc"}) +//! +//! Will reload both: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/plugins/root/example/example.cc\n +//!   ${DOCKER_FINANCE_CLIENT_PLUGINS}/root/example/example.cc +//! +//! \warning +//! To utilize plugin auto-(un)loader functionality, the plugin's parent directory *MUST* align with the plugin's namespace;\n +//! e.g., "repo/example/example.cc" requires the plugin's auto-(un)loader to exist in the namespace `dfi::plugin::example`.\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_plugin +//! \since docker-finance 1.1.0 +void reload(const std::initializer_list& paths) +{ + for (const auto& path : paths) + reload(path); +} } // namespace plugin } // namespace dfi