libikarus/src/objects/util.hpp
Folling 910f337d02
change clang-format
Signed-off-by: Folling <mail@folling.io>
2025-01-01 14:07:47 +01:00

242 lines
8.2 KiB
C++

#pragma once
#include "util.hpp"
#include <concepts>
#include <cppbase/result.hpp>
#include <cppbase/strings.hpp>
#include <ikarus/id.h>
#include <ikarus/objects/object_type.h>
#include <persistence/function_context.hpp>
namespace ikarus::util {
struct EmptyNameError {};
COMPOUND_ERROR(InsertObjectError, EmptyNameError, sqlitecpp::TransactionError);
template<typename InsertFunction, typename ObjectFactory>
[[nodiscard]] cppbase::Result<std::invoke_result_t<ObjectFactory, IkarusId>, InsertObjectError> insert_object(
IkarusProject * project,
IkarusObjectType type,
std::string_view name,
InsertFunction insert_function,
ObjectFactory object_factory
) {
auto const * object_type_str = ikarus_object_type_to_string(type);
LOG_INFO("creating new {}", object_type_str);
LOG_DEBUG("project={}; name={}", project->get_path().c_str(), name);
auto * ctx = project->get_function_context();
if (cppbase::is_empty_or_blank(name)) {
ctx->set_error("name is empty or blank", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input);
return cppbase::err(EmptyNameError{});
}
VTRY(
auto const id,
project->get_db()
->transact([&](auto * db) -> cppbase::Result<IkarusId, sqlitecpp::TransactionError> {
LOG_VERBOSE("creating {} in objects table", object_type_str);
TRY(db->execute("INSERT INTO `objects` (`object_type`, `name`) VALUES(?, ?);", static_cast<int>(type), name));
auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint);
LOG_DEBUG("{} is {}", object_type_str, id);
TRY(insert_function(db, id));
return cppbase::ok(id);
})
.on_error([&](auto const & err) {
ctx->set_error(
fmt::format("unable to insert {} into database: {}", object_type_str, err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
})
);
LOG_VERBOSE("successfully created {}", object_type_str);
return cppbase::ok(object_factory(id));
}
template<typename Object>
requires std::derived_from<Object, IkarusObject>
void delete_object(IkarusProject * project, Object * object) {
auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id));
LOG_INFO("deleting {}", object_type_str);
LOG_DEBUG("project={}; {}={}", object_type_str, object->project->get_path().c_str(), object->id);
auto * ctx = object->project->get_function_context();
TRYRV(, project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", object->id).on_error([&](auto const & err) {
ctx->set_error(
fmt::format("failed to delete {} from objects table: {}", object_type_str, err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
}));
LOG_VERBOSE("{} was successfully deleted from database, freeing", object_type_str);
project->uncache(object);
LOG_VERBOSE("successfully deleted {}", object_type_str);
}
struct SingleQueryData {
std::string_view table_name;
std::string_view select_field_name;
};
template<typename T, typename Object>
requires std::derived_from<Object, IkarusObject>
cppbase::Result<T, sqlitecpp::SingleQueryError> fetch_single_field(Object const * object, SingleQueryData const & query_data) {
auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id));
LOG_VERBOSE("fetching property default value");
LOG_VERBOSE("project={};property={}", object->project->get_path().c_str(), object->id);
auto * ctx = object->project->get_function_context();
VTRY(
T value,
object->project->get_db()
->template query_one<T>(
fmt::format("SELECT `{}` FROM `{}` WHERE `id` = ?", query_data.select_field_name, query_data.table_name),
object->id
)
.on_error([&](auto const & err) {
ctx->set_error(
fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.select_field_name, err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
})
);
return value;
}
struct MultipleBufferQueryData {
std::string_view table_name;
std::string_view select_field_name;
std::string_view where_field_name;
std::string_view relation_desc;
};
template<typename Selected, typename Mapped, typename Object, typename F>
requires std::derived_from<Object, IkarusObject>
void fetch_multiple_buffered(
Object const * object,
MultipleBufferQueryData const & query_data,
Mapped * mapped_buffer,
size_t buffer_size,
F transformer
)
requires cppbase::is_result_with_value_type_v<Mapped, std::invoke_result_t<F, IkarusProject *, IkarusFunctionContext *, Selected>>
{
auto * ctx = object->project->get_function_context();
auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id));
LOG_VERBOSE("fetching {} {}", object_type_str, query_data.relation_desc);
LOG_VERBOSE("project={};{}={}", object->project->get_path().c_str(), object_type_str, object->id);
Selected select_buffer[buffer_size];
TRYRV(
,
object->project->get_db()
->template query_many_buffered<Selected>(
fmt::format(
"SELECT `{}` FROM `{}` WHERE `{}` = ?",
query_data.select_field_name,
query_data.table_name,
query_data.where_field_name
),
select_buffer,
buffer_size,
object->id
)
.on_error([&](auto const & err) {
ctx->set_error(
fmt::format("failed to fetch {} {} from database: {}", object_type_str, query_data.relation_desc, err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
})
);
LOG_DEBUG("{} {}: [{}]", object_type_str, query_data.relation_desc, fmt::join(select_buffer, select_buffer + buffer_size, ", "));
for (size_t i = 0; i < buffer_size; ++i) {
VTRYRV(mapped_buffer[i], , transformer(object->project, ctx, select_buffer[i]));
}
}
struct CountQueryData {
std::string_view table_name;
std::string_view select_field_name;
std::string_view where_field_name;
std::string_view relation_desc;
};
template<typename Object>
requires std::derived_from<Object, IkarusObject>
cppbase::Result<cppbase::usize, sqlitecpp::QueryError> fetch_count(Object const * object, CountQueryData const & query_data) {
auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id));
LOG_VERBOSE("fetching {} {} count", object_type_str, query_data.relation_desc);
auto * ctx = object->project->get_function_context();
LOG_DEBUG("{}={}", object_type_str, object->id);
VTRY(
auto count,
object->project->get_db()
->template query_one<int>(
fmt::format(
"SELECT COUNT(`{}`) FROM `{}` WHERE `{}` = ?;",
query_data.select_field_name,
query_data.table_name,
query_data.where_field_name
),
object->id
)
.on_error([&](auto const & err) {
ctx->set_error(
fmt::format("failed to fetch {} {} count: {}", object_type_str, query_data.relation_desc, err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
})
);
LOG_DEBUG("{} {} count: {}", object_type_str, query_data.relation_desc, count);
LOG_VERBOSE("successfully fetched {} {} count", object_type_str, query_data.relation_desc);
return cppbase::ok(static_cast<size_t>(count));
}
} // namespace ikarus::util