#ifndef REPOSITORY_HPP #define REPOSITORY_HPP #include "matador/object/basic_repository.hpp" #include "matador/object/foreign_node_completer.hpp" #include "matador/object/observer.hpp" #include "matador/object/relation_completer.hpp" #include "matador/utils/result.hpp" #include "matador/utils/error.hpp" #include #include #include namespace matador::object { class repository : public basic_repository { public: using basic_repository::basic_repository; template typename... Observers> [[nodiscard]] utils::result attach(const std::string &name, Observers&&... observers) { return attach_type(name, std::string{}, std::forward>(observers)...); } template typename... Observers> [[nodiscard]] utils::result attach(const std::string &name, Observers&&... observers) { const auto ti = std::type_index(typeid(SuperType)); const auto it = find_node(ti); if (it == end()) { return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + std::string(ti.name()) + "' not found")); } return attach_type(name, it->name(), std::forward>(observers)...); } template typename... Observers> [[nodiscard]] utils::result attach(const std::string &name, const std::string &parent, Observers&&... observers) { return attach_type(name, parent, std::forward>(observers)...); } template typename... Observers> [[nodiscard]] utils::result attach_type(const std::string &name, const std::string &parent, Observers&&... observers) { if (const auto it = nodes_by_type_.find(typeid(Type)); it == nodes_by_type_.end() ) { std::vector>> obs; obs.reserve(sizeof...(Observers)); (obs.push_back(std::unique_ptr>(new Observers(std::forward>(observers)))), ...); // if the type was not found std::unique_ptr node; if (is_node_announced(typeid(Type))) { node = pop_announce_node(typeid(Type)); node->update_name(name); } else { node = repository_node::make_node(*this, name, []{ return std::make_unique(); }, std::move(obs)); } auto result = attach_node(node.release(), parent); if (!result) { return utils::failure(result.err()); } repository_node* attached_node = result.value(); const auto info = attached_node->template info(); foreign_node_completer::complete(attached_node, info.get().observers()); relation_completer::complete(attached_node, info.get().observers()); } else if (!has_node(name)) { it->second->update_name(name); nodes_by_name_[name] = it->second; if (const auto i = nodes_by_name_.find(""); i != nodes_by_name_.end()) { nodes_by_name_.erase(i); } const auto info = it->second->info(); relation_completer::complete(it->second, info.get().observers()); log_.info("attach: update node name to '%s' (type: %s)", it->second->name().c_str(), it->second->type_index().name()); } else { return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists")); } return utils::ok(); } template [[nodiscard]] utils::result, utils::error> info() const { const auto it = find_node(std::type_index(typeid(Type))); if (it == end()) { return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + std::string(typeid(Type).name()) + "' not found")); } return utils::ok(it->info()); } }; } #endif //REPOSITORY_HPP