diff --git a/container/src/root/macro/common/utility.hh b/container/src/root/macro/common/utility.hh index c61dc8c..37dcf4f 100644 --- a/container/src/root/macro/common/utility.hh +++ b/container/src/root/macro/common/utility.hh @@ -1,6 +1,6 @@ // docker-finance | modern accounting for the power-user // -// Copyright (C) 2021-2025 Aaron Fiore (Founder, Evergreen Crypto LLC) +// Copyright (C) 2021-2026 Aaron Fiore (Founder, Evergreen Crypto LLC) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -25,7 +25,9 @@ #include #include +#include +#include "../../common/type.hh" #include "../../common/utility.hh" //! \namespace dfi @@ -42,65 +44,91 @@ namespace macro //! \since docker-finance 1.0.0 namespace common { -//! \brief Load macro file by path -//! \details -//! Example: -//!
  root [0] dfi::macro::load("macro/test/unit.C")
-//! -//! Will load: -//!
  root/macro/test/unit.C
-//! -//! \note Parent directory is `root` by default but accepts absolute paths outside of `root` -//! \todo Refactor with dfi::common::Pluggable -//! \ingroup cpp_macro_impl +//! \brief Macro's pseudo-path handler +//! \note Parent directory for all macros are outside of repository's `root` directory +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +class MacroPath final : public ::dfi::common::PluggablePath +{ + public: + //! \param pseudo Must be string "repo/" or "custom/" + //! where "repo" is a repository macro, "custom" is a custom macro, + //! and where is a pseudo-path relative to macro's + //! operating system location. + explicit MacroPath(const std::string& pseudo) + : ::dfi::common::PluggablePath( + pseudo, + ::dfi::common::type::PluggablePath({ + ::dfi::common::get_root_path() + "macro/", + "" /* TODO(afiore): currently no-op */ + })) + { + } +}; + +//! \brief Macro's pluggable namespace and entrypoint handler +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +class MacroSpace final : public ::dfi::common::PluggableSpace +{ + public: + explicit MacroSpace(const std::string& inner, const std::string& entry) + : ::dfi::common::PluggableSpace( + ::dfi::common::type::PluggableSpace({"macro", inner, entry})) + { + } +}; + +//! \brief Macro's arguments handler +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +class MacroArgs final : public ::dfi::common::PluggableArgs +{ + public: + explicit MacroArgs(const std::string& load, const std::string& unload) + : ::dfi::common::PluggableArgs( + ::dfi::common::type::PluggableArgs({load, unload})) + { + } +}; + +//! \brief Macro handler +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +class Macro final + : public ::dfi::common::Pluggable +{ + public: + explicit Macro( + ::dfi::common::type::Pluggable macro) + : ::dfi::common::Pluggable(macro) + { + } +}; + +//! \deprecated This will be removed in the v2 API; use the `dfi::macro` free function instead +//! \todo Remove in 2.0.0 void load(const std::string& path) { ::dfi::common::load(path); } -//! \brief Load macro files by list of paths -//! \details -//! Example: -//!
  root [0] dfi::macro::load({"macro/test/unit.C", "macro/test/benchmark.C"})
-//! -//! Will load: -//!
  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 +//! \deprecated This will be removed in the v2 API; use the `dfi::macro` free function instead +//! \todo Remove in 2.0.0 void load(const std::initializer_list& paths) { ::dfi::common::load(paths); } -//! \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 +//! \deprecated This will be removed in the v2 API; use the `dfi::macro` free function instead +//! \todo Remove in 2.0.0 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 +//! \deprecated This will be removed in the v2 API; use the `dfi::macro` free function instead +//! \todo Remove in 2.0.0 void unload(const std::initializer_list& paths) { ::dfi::common::unload(paths); @@ -113,33 +141,15 @@ class Command : public ::dfi::common::Command { }; -//! \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 +//! \deprecated This will be removed in the v2 API; use the `dfi::macro` free function instead +//! \todo Remove in 2.0.0 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 +//! \deprecated This will be removed in the v2 API; use the `dfi::macro` free function instead +//! \todo Remove in 2.0.0 void reload(const std::initializer_list& paths) { ::dfi::common::reload(paths); @@ -170,63 +180,436 @@ std::string make_timestamp() } } // namespace common -//! \brief Convenience wrapper to inner common loader -//! \ref dfi::macro::common::load(const std::string& path) +// +// Loader +// + +//! \brief Loads a single macro +//! +//! \param path common::MacroPath +//! \param args common::MacroArgs +//! +//! \ref dfi::macro::common::Macro +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::load({"repo/example/example1.C"}, {"using foo = int; foo f;"}) +//! +//! Will load: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//! +//! and pass `using foo = int; foo f;` to the macro's loader (macro-implementation defined). +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example_C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \ingroup cpp_macro //! \since docker-finance 1.1.0 -//! \todo Refactor with dfi::common::Pluggable -void load(const std::string& path) +void load(const common::MacroPath& path, const common::MacroArgs& args) { - common::load(path); + ::dfi::common::type:: + Pluggable + type{path, common::MacroSpace{path.parent(), path.child()}, args}; + + common::Macro macro{type}; + macro.load(); } -//! \brief Convenience wrapper to inner common loader -//! \ref dfi::macro::common::load(const std::initializer_list& paths) + +//! \brief Convenience wrapper to load a list of single macros with argument +//! +//! \ref load(const common::MacroPath& path, const common::MacroArgs& args) +//! +//! \param macros List of single macros +//! +//! \details +//! +//! Example: +//! +//!   root [0] dfi::macro::load({{"repo/example/example1.C", "using foo = int; foo f;"}, {"repo/example/example2.C", "using bar = char; bar b;"}}) +//! +//! Will load: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C +//! +//! And pass macro-implementation defined auto-loader arguments: +//! +//!   `using foo = int; foo f;` for `${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C`\n +//!   `using bar = char; bar b;` for `${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C` +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example_C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \ingroup cpp_macro //! \since docker-finance 1.1.0 -//! \todo Refactor with dfi::common::Pluggable +void load( + const std::initializer_list< + std::pair>& macros) +{ + for (const auto& macro : macros) + load(macro.first, macro.second); +} + +//! \brief Convenience wrapper to load a single macro with optional argument +//! +//! \ref load(const common::MacroPath& path, const common::MacroArgs& args) +//! +//! \param path Must be of string "repo/" or "custom/" to macro location, as described by common::MacroPath +//! +//! \param arg Optional string argument to pass to macro's auto-loader (macro-implementation defined) +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::load("repo/example/example1.C", "using foo = int; foo f;") +//! +//! Will load: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//! +//! and pass `using foo = int; foo f;` to the macro's loader (macro-implementation defined). +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example_C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +void load(const std::string& path, const std::string& arg = {}) +{ + load(common::MacroPath{path}, common::MacroArgs{arg, ""}); +} + +//! \brief Convenience wrapper to load a list of macros with no arguments +//! +//! \ref load(const std::string& path, const std::string& arg) +//! +//! \param path Must be of string "repo/" or "custom/" to macro location, as described by common::MacroPath +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::load({"repo/example/example1.C", "repo/example/example2.C"}) +//! +//! Will load both: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example_C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_macro +//! \since docker-finance 1.0.0 void load(const std::initializer_list& paths) { - common::load(paths); + for (const auto& path : paths) + load(path); } -//! \brief Convenience wrapper to inner common unloader -//! \ref dfi::macro::common::unload(const std::string& path) +// +// Unloader +// + +//! \brief Unloads a single macro +//! +//! \param path common::MacroPath +//! \param args common::MacroArgs +//! +//! \ref dfi::macro::common::Macro +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::unload({"repo/example/example1.C"}, {"using foo = int; foo f;"}) +//! +//! Will unload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//! +//! and pass `using foo = int; foo f;` to the macro's unloader (macro-implementation defined). +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example_C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \ingroup cpp_macro //! \since docker-finance 1.1.0 -//! \todo Refactor with dfi::common::Pluggable -void unload(const std::string& path) +void unload(const common::MacroPath& path, const common::MacroArgs& args) { - common::unload(path); + ::dfi::common::type:: + Pluggable + type{path, common::MacroSpace{path.parent(), path.child()}, args}; + + common::Macro macro{type}; + macro.unload(); } -//! \brief Convenience wrapper to inner common unloader -//! \ref dfi::macro::common::unload(const std::initializer_list& paths) + +//! \brief Convenience wrapper to unload a list of single macros with arguments +//! +//! \ref unload(const common::MacroPath& path, const common::MacroArgs& args) +//! +//! \param macros List of single macros +//! +//! \details +//! +//! Example: +//! +//!   root [0] dfi::macro::unload({{"repo/example/example1.C", "using foo = int; foo f;"}, {"repo/example/example2.C", "using bar = char; bar b;"}}) +//! +//! Will unload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C +//! +//! And pass macro-implementation defined auto-unloader arguments: +//! +//!   `using foo = int; foo f;` for `${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C`\n +//!   `using bar = char; bar b;` for `${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C` +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example_C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +void unload( + const std::initializer_list< + std::pair>& macros) +{ + for (const auto& macro : macros) + unload(macro.first, macro.second); +} + +//! \brief Convenience wrapper to unload a single macro with optional argument +//! +//! \ref unload(const common::MacroPath& path, const common::MacroArgs& args) +//! +//! \param path Must be of string "repo/" or "custom/" to macro location, as described by common::MacroPath +//! +//! \param arg Optional string argument to pass to macro's auto-unloader (macro-implementation defined) +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::unload("repo/example/example1.C", "using foo = int; foo f;") +//! +//! Will unload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//! +//! and pass `using foo = int; foo f;` to the macro's unloader (macro-implementation defined). +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example1.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example1.C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +void unload(const std::string& path, const std::string& arg = {}) +{ + unload(common::MacroPath{path}, common::MacroArgs{"", arg}); +} + +//! \brief Convenience wrapper to unload a list of macros with no arguments +//! +//! \ref unload(const std::string& path, const std::string& arg) +//! +//! \param path Must be of string "repo/" or "custom/" to macro location, as described by common::MacroPath +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::unload({"repo/example/example1.C", "repo/example/example2.C"}) +//! +//! Will unload both: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example1.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example1.C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \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); + for (const auto& path : paths) + unload(path); } -//! \brief Convenience wrapper to inner common reloader -//! \ref dfi::macro::common::reload(const std::string& path) +// +// Reloader +// + +//! \brief Reloads a single macro +//! +//! \param path common::MacroPath +//! \param args common::MacroArgs +//! +//! \ref dfi::macro::common::Macro +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::reload({"repo/example/example1.C"}, {"using foo = int; foo f;", "using bar = char; bar b;"}) +//! +//! Will reload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//! +//! and pass `using bar = char; bar b;` to the macro's unloader (macro-implementation defined) +//! and pass `using foo = int; foo f;` to the macro's loader (macro-implementation defined) +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example1.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example1.C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \ingroup cpp_macro //! \since docker-finance 1.1.0 -//! \todo Refactor with dfi::common::Pluggable -void reload(const std::string& path) +void reload(const common::MacroPath& path, const common::MacroArgs& args) { - common::reload(path); + ::dfi::common::type:: + Pluggable + type{path, common::MacroSpace{path.parent(), path.child()}, args}; + + common::Macro macro{type}; + macro.reload(); } -//! \brief Convenience wrapper to inner common reloader -//! \ref dfi::macro::common::reload(const std::initializer_list& paths) + +//! \brief Convenience wrapper to reload a list of single macros with arguments +//! +//! \ref reload(const common::MacroPath& path, const common::MacroArgs& args) +//! +//! \param macros List of populated pair of common::MacroPath and common::MacroArgs +//! +//! \details +//! +//! Example: +//!   root [0] dfi::macro::reload({{"repo/example/example1.C", "using foo = int; foo f;"}, {"repo/example/example2.C", "using bar = char; bar b;"}}) +//! +//! Will reload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C +//! +//! And pass macro-implementation defined auto-loader arguments: +//! +//!   `using foo = int; foo f;` for `${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C`\n +//!   `using bar = char; bar b;` for `${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C` +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example1.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example1.C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +void reload( + const std::initializer_list< + std::pair>& macros) +{ + for (const auto& macro : macros) + reload(macro.first, macro.second); +} + +//! \brief Convenience wrapper to reload a single macro with optional arguments +//! +//! \ref reload(const common::MacroPath& path, const common::MacroArgs& args) +//! +//! \param path Must be of string "repo/" or "custom/" to macro location, as described by common::MacroPath +//! +//! \param args Optional pair of arguments to pass to macro's auto-loader/unloader (macro-implementation defined) +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::reload("repo/example/example1.C", {"using foo = int; foo f;", ""}) +//! +//! Will reload: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//! +//! and pass `using foo = int; foo f;` to the macro's auto-loader (macro-implementation defined). +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example1.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example1.C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! +//! \ingroup cpp_macro +//! \since docker-finance 1.1.0 +void reload( + const std::string& path, + const std::pair& args = {}) +{ + reload(common::MacroPath{path}, common::MacroArgs{args.first, args.second}); +} + +//! \brief Convenience wrapper to reload a list of macros with no arguments +//! +//! \ref reload(const std::string& path, const std::string& arg) +//! +//! \param path Must be of string "repo/" or "custom/" to macro location, as described by common::MacroPath +//! +//! \details +//! +//! Example 1: +//! +//!   root [0] dfi::macro::reload({"repo/example/example1.C", "repo/example/example2.C"}) +//! +//! Will reload both: +//! +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example1.C +//!   ${DOCKER_FINANCE_CONTAINER_REPO}/src/root/macro/example/example2.C +//! +//! \warning +//! To utilize macro auto-(un)loader functionality, the macro's parent directory *MUST* align with the macro's namespace\n +//! and the macro's entrypoint class *MUST* align with the macro's filename; e.g., "repo/example/example1.C" requires that\n +//! the auto-(un)loader exist within the entrypoint class name `example1.C` within the namespace `dfi::macro::example`.\n\n +//! To avoid this requisite, though ill-advised, use a `dfi::common` file loader instead. +//! //! \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); + for (const auto& path : paths) + reload(path); } - } // namespace macro } // namespace dfi