220 lines
7.5 KiB
C++
220 lines
7.5 KiB
C++
#ifndef SCHEMA_HPP
|
|
#define SCHEMA_HPP
|
|
|
|
#include "matador/object/error_code.hpp"
|
|
#include "matador/object/schema_node.hpp"
|
|
#include "matador/object/schema_node_iterator.hpp"
|
|
|
|
#include "matador/utils/result.hpp"
|
|
#include "matador/utils/error.hpp"
|
|
|
|
#include <memory>
|
|
#include <stack>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
|
|
namespace matador::object {
|
|
|
|
utils::error make_error(error_code ec, const std::string& msg);
|
|
|
|
class schema;
|
|
|
|
template<typename Type>
|
|
class type_analyzer final {
|
|
public:
|
|
using value_type = Type;
|
|
|
|
static void analyze(schema &scm) {
|
|
type_analyzer analyzer(scm);
|
|
|
|
Type obj;
|
|
const auto ti = std::type_index(typeid(Type));
|
|
analyzer.known_types_.emplace(ti);
|
|
access::process(analyzer, obj);
|
|
analyzer.known_types_.erase(ti);
|
|
}
|
|
|
|
template < class PrimaryKeyType >
|
|
void on_primary_key(const char * /*id*/, PrimaryKeyType &/*pk*/, std::enable_if_t<std::is_integral_v<PrimaryKeyType> && !std::is_same_v<bool, PrimaryKeyType>>* = nullptr) {}
|
|
void on_primary_key(const char * /*id*/, std::string &/*pk*/, size_t /*size*/) {}
|
|
void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
|
|
|
template<typename AttributeType>
|
|
void on_attribute(const char * /*id*/, AttributeType &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
|
|
|
template<typename AttributeType>
|
|
void on_attribute(const char * /*id*/, std::optional<AttributeType> &/*val*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
|
|
|
template<class ForeignPointerType>
|
|
void on_belongs_to(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/) {
|
|
// const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
|
|
// columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
|
|
}
|
|
|
|
template<class ForeignPointerType>
|
|
void on_has_one(const char * /*id*/, ForeignPointerType &/*obj*/, const utils::foreign_attributes &/*attr*/);
|
|
|
|
template<class CollectionType>
|
|
void on_has_many(const char * /*id*/, CollectionType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
|
template<class CollectionType>
|
|
void on_has_many_to_many(const char * /*id*/, CollectionType &/*col*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
|
template<class ContainerType>
|
|
void on_has_many_to_many(const char * /*id*/, ContainerType &/*col*/, const utils::foreign_attributes &/*attr*/) {}
|
|
|
|
private:
|
|
explicit type_analyzer(schema& schema)
|
|
: schema_(schema) {};
|
|
|
|
|
|
private:
|
|
schema &schema_;
|
|
std::unordered_set<std::type_index> known_types_;
|
|
};
|
|
|
|
|
|
class schema {
|
|
public:
|
|
typedef const_schema_node_iterator const_iterator; /**< Shortcut for the list const iterator. */
|
|
|
|
/**
|
|
* Creates an empty schema
|
|
*/
|
|
explicit schema( const std::string& name = "");
|
|
|
|
template <typename Type>
|
|
[[nodiscard]] utils::result<void, utils::error> attach(const std::string& name, const std::string &parent = "") {
|
|
if (has_node(name)) {
|
|
return utils::failure(make_error(error_code::NodeAlreadyExists, "Node '" + name + "' already exists"));
|
|
}
|
|
|
|
// analyze node (collect unknown types by type index)
|
|
type_analyzer<Type>::analyze(*this);
|
|
|
|
auto node = schema_node::make_node<Type>(*this, name);
|
|
|
|
auto result = attach_node(node, parent);
|
|
if (!result) {
|
|
return utils::failure(result.err());
|
|
}
|
|
|
|
return utils::ok<void>();
|
|
}
|
|
|
|
template <typename Type, typename ParentType>
|
|
[[nodiscard]] utils::result<void, utils::error> attach(const std::string name) {
|
|
const auto ti = std::type_index(typeid(ParentType));
|
|
auto result = find_node(ti);
|
|
if (!result) {
|
|
return utils::failure(make_error(error_code::NodeNotFound, "Parent node '" + std::string(ti.name()) + "' not found"));
|
|
}
|
|
|
|
return attach<Type>(name, (*result)->name());
|
|
}
|
|
|
|
/**
|
|
* Return the first schema node.
|
|
*
|
|
* @return The first schema node iterator.
|
|
*/
|
|
[[nodiscard]] const_iterator begin() const;
|
|
|
|
/**
|
|
* Return the last schema node.
|
|
*
|
|
* @return The last schema node iterator.
|
|
*/
|
|
[[nodiscard]] const_iterator end() const;
|
|
|
|
/**
|
|
* Returns true if the schema contains
|
|
* no schema nodes.
|
|
*
|
|
* @return True if schema is empty
|
|
*/
|
|
[[nodiscard]] bool empty() const;
|
|
|
|
/**
|
|
* Returns the current number of schema node.
|
|
*
|
|
* @return Number of schema nodes
|
|
*/
|
|
[[nodiscard]] size_t size() const;
|
|
|
|
/**
|
|
* Returns the name of the schema.
|
|
*
|
|
* @return The name of the schema
|
|
*/
|
|
[[nodiscard]] std::string name() const;
|
|
|
|
template <typename Type>
|
|
[[nodiscard]] utils::result<object_info_ref<Type>, utils::error> info() const {
|
|
auto result = find_node(std::type_index(typeid(Type)));
|
|
if (!result) {
|
|
return utils::failure(result.err());
|
|
}
|
|
|
|
return utils::ok(result.value()->info<Type>());
|
|
}
|
|
|
|
template <typename Type>
|
|
[[nodiscard]] utils::result<basic_object_info_ref, utils::error> basic_info() const {
|
|
auto result = find_node(std::type_index(typeid(Type)));
|
|
if (!result) {
|
|
return utils::failure(result.err());
|
|
}
|
|
|
|
return utils::ok(basic_object_info_ref{result.value()->basic_info()});
|
|
}
|
|
|
|
[[nodiscard]] utils::result<std::shared_ptr<attribute_definition>, utils::error> reference(const std::type_index &type_index) const;
|
|
|
|
private:
|
|
using node_ptr = std::shared_ptr<schema_node>;
|
|
using t_node_map = std::unordered_map<std::string, node_ptr>;
|
|
using t_type_index_node_map = std::unordered_map<std::type_index, node_ptr>;
|
|
|
|
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
|
|
const std::string &parent);
|
|
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> attach_node(const std::shared_ptr<schema_node> &node,
|
|
const std::type_index &type_index);
|
|
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> find_node(const std::string &name) const;
|
|
[[nodiscard]] utils::result<std::shared_ptr<schema_node>, utils::error> find_node(const std::type_index &type_index) const;
|
|
|
|
[[nodiscard]] bool has_node(const std::string &name) const;
|
|
[[nodiscard]] bool has_node(const std::type_index& index) const;
|
|
[[nodiscard]] bool has_node(const std::type_index& index, const std::string &name) const;
|
|
|
|
static void push_back_child(const node_ptr &parent, const node_ptr &child);
|
|
|
|
private:
|
|
template <typename Type>
|
|
friend class type_analyzer;
|
|
|
|
std::string name_;
|
|
std::shared_ptr<schema_node> root_;
|
|
|
|
t_node_map node_map_;
|
|
t_type_index_node_map type_index_node_map_;
|
|
};
|
|
|
|
template<typename Type>
|
|
template<class ForeignPointerType>
|
|
void type_analyzer<Type>::on_has_one(const char *, ForeignPointerType &, const utils::foreign_attributes &) {
|
|
auto ti = std::type_index(typeid(typename ForeignPointerType::value_type));
|
|
if (schema_.has_node(ti)) {
|
|
return;
|
|
}
|
|
|
|
auto info = std::make_unique<object_info<typename ForeignPointerType::value_type>>();
|
|
|
|
known_types_.insert(ti);
|
|
|
|
// const auto [ref_table, ref_column] = determine_foreign_ref(std::type_index(typeid(typename Pointer::value_type)));
|
|
// columns_.push_back(fk_column_generator_.generate(id, *x, ref_table, ref_column));
|
|
}
|
|
|
|
}
|
|
|
|
#endif //SCHEMA_HPP
|