container: root: new pluggable framework
- Implements pluggable auto-(un|re)loading - Refactors, adds/updates documentation
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<type::RuntimeError>(
|
||||
"must be of type 'repo/<relative>' or 'custom/<relative>'");
|
||||
}
|
||||
m_absolute = std::move(absolute);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
//! \brief Sets pluggable absolute path from given pseudo path
|
||||
//! \param pseudo Pseudo-path ('repo/<relative>' or 'custom/<relative>')
|
||||
//! \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 <typename t_path>
|
||||
concept PPath = std::derived_from<t_path, PluggablePath>;
|
||||
|
||||
//! \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 <typename t_space>
|
||||
concept PSpace = std::derived_from<t_space, PluggableSpace>;
|
||||
|
||||
//! \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 <typename t_args>
|
||||
concept PArgs = std::derived_from<t_args, PluggableArgs>;
|
||||
|
||||
//! \brief Base pluggable handler
|
||||
//! \ingroup cpp_plugin_impl cpp_macro_impl
|
||||
//! \since docker-finance 1.1.0
|
||||
template <PPath t_path, PSpace t_space, PArgs t_args>
|
||||
class Pluggable
|
||||
{
|
||||
protected:
|
||||
explicit Pluggable(const type::Pluggable<t_path, t_space, t_args>& 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<t_path, t_space, t_args> m_plug;
|
||||
};
|
||||
|
||||
} // namespace common
|
||||
} // namespace dfi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user