688 lines
18 KiB
C++
688 lines
18 KiB
C++
#include "entity.hpp"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <ikarus/errors.h>
|
|
#include <ikarus/errors.hpp>
|
|
#include <ikarus/objects/blueprint.h>
|
|
#include <ikarus/objects/entity.h>
|
|
#include <ikarus/objects/property.h>
|
|
#include <ikarus/persistence/project.hpp>
|
|
|
|
IkarusEntity::IkarusEntity(struct IkarusProject * project, int64_t id):
|
|
project{project},
|
|
id{id} {}
|
|
|
|
bool ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out) {
|
|
IKARUS_FAIL_IF_NULL(entity, false);
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto exists,
|
|
false,
|
|
"failed to check whether entity exists: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_one<bool>("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `id` = ?)", entity->id)
|
|
);
|
|
|
|
return exists;
|
|
}
|
|
|
|
IkarusEntity * ikarus_entity_create(
|
|
struct IkarusProject * project,
|
|
char const * name,
|
|
IkarusEntityCreateFlags flags,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_FAIL_IF_NULL(project, nullptr);
|
|
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
nullptr,
|
|
"failed to create entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
project->db->execute("INSERT INTO `entities`(`name`) VALUES(?)", name)
|
|
);
|
|
|
|
auto const id = project->db->last_insert_rowid();
|
|
return new IkarusEntity{project, id};
|
|
}
|
|
|
|
void ikarus_entity_delete(IkarusEntity * entity, IkarusEntityDeleteFlags flags, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to delete entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id)
|
|
);
|
|
|
|
delete entity;
|
|
}
|
|
|
|
IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags flags, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto id,
|
|
nullptr,
|
|
"failed to copy entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->transact([entity](auto * db) -> cppbase::Result<int64_t, sqlitecpp::TransactionError> {
|
|
CPPBASE_TRY(entity->project->db->execute(
|
|
"INSERT INTO `entities`(`name`) "
|
|
"SELECT `name` FROM `entities` WHERE `id` = ?",
|
|
entity->id
|
|
));
|
|
|
|
auto const id = entity->project->db->last_insert_rowid();
|
|
|
|
CPPBASE_TRY(entity->project->db->execute(
|
|
"INSERT INTO `entity_values`(`entity`, `name`, `value`) "
|
|
"SELECT ?1, `name`, `value` FROM `entity_values` WHERE "
|
|
"`entity` = ?1",
|
|
entity->id
|
|
))
|
|
|
|
CPPBASE_TRY(entity->project->db->execute(
|
|
"INSERT INTO `entity_property_values`(`entity`, `property`, `value`) "
|
|
"SELECT ?1, `property`, `value` FROM `entity_property_values` "
|
|
"WHERE `entity` = ?1",
|
|
entity->id
|
|
))
|
|
|
|
CPPBASE_TRY(entity->project->db->execute(
|
|
"INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) "
|
|
"SELECT ?1, `property`, `value` FROM `entity_property_values` "
|
|
"WHERE `entity` = ?1",
|
|
entity->id
|
|
))
|
|
|
|
return cppbase::ok(id);
|
|
})
|
|
);
|
|
|
|
return new IkarusEntity{entity->project, id};
|
|
}
|
|
|
|
IkarusProject * ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
return entity->project;
|
|
}
|
|
|
|
char const * ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto name,
|
|
nullptr,
|
|
"failed to get name for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_one<char const *>("SELECT `name` FROM `entities` WHERE `id` = ?", entity->id)
|
|
);
|
|
|
|
return name;
|
|
}
|
|
|
|
void ikarus_entity_set_name(
|
|
IkarusEntity * entity,
|
|
char const * name,
|
|
IkarusEntitySetNameFlags flags,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to set name for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute("UPDATE `entities` SET `name` = ? WHERE `id` = ?", name, entity->id)
|
|
);
|
|
}
|
|
|
|
bool ikarus_entity_is_linked_to_blueprint(
|
|
IkarusEntity * entity,
|
|
struct IkarusBlueprint * blueprint,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
IKARUS_ASCERTAIN(
|
|
false,
|
|
"blueprint doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_blueprint_exists,
|
|
blueprint
|
|
);
|
|
|
|
IKARUS_FAIL_IF(
|
|
entity->project != blueprint->project,
|
|
false,
|
|
"blueprint does not belong to entity's project",
|
|
IkarusErrorInfo_Client_NotLinked
|
|
);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto exists,
|
|
false,
|
|
"failed to check if entity is linked to blueprint: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_one<bool>(
|
|
"SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)",
|
|
entity->id,
|
|
blueprint->id
|
|
)
|
|
);
|
|
|
|
return exists;
|
|
}
|
|
|
|
struct IkarusBlueprint **
|
|
ikarus_entity_get_linked_blueprints(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
IKARUS_VCALL(auto count, nullptr, ikarus_entity_get_linked_blueprints_count, entity);
|
|
|
|
std::int64_t ids[count];
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
nullptr,
|
|
"failed to get linked blueprints for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_many_buffered<std::int64_t>(
|
|
"SELECT `blueprint` FROM `entity_blueprint_links` WHERE `entity` = ?",
|
|
ids,
|
|
count,
|
|
entity->id
|
|
)
|
|
);
|
|
|
|
auto blueprints = new IkarusBlueprint *[count];
|
|
|
|
std::transform(ids, ids + count, blueprints, [entity](auto id) {
|
|
return new IkarusBlueprint{entity->project, id};
|
|
});
|
|
|
|
if (size_out) {
|
|
*size_out = count;
|
|
}
|
|
|
|
return blueprints;
|
|
}
|
|
|
|
size_t ikarus_entity_get_linked_blueprints_count(IkarusEntity * entity, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto count,
|
|
0,
|
|
"failed to get linked blueprints count for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db
|
|
->query_one<std::int64_t>("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id)
|
|
);
|
|
|
|
return count;
|
|
}
|
|
|
|
void ikarus_entity_link_blueprint(
|
|
IkarusEntity * entity,
|
|
struct IkarusBlueprint * blueprint,
|
|
IkarusEntityLinkBlueprintFlags flags,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"blueprint doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_blueprint_exists,
|
|
blueprint
|
|
);
|
|
|
|
IKARUS_FAIL_IF(
|
|
entity->project != blueprint->project,
|
|
IKARUS_VOID_RETURN,
|
|
"blueprint does not belong to entity's project",
|
|
IkarusErrorInfo_Client_NotLinked
|
|
);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to link blueprint to entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute(
|
|
"INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) VALUES(?, ?)",
|
|
entity->id,
|
|
blueprint->id
|
|
)
|
|
);
|
|
}
|
|
|
|
void ikarus_entity_unlink_blueprint(
|
|
IkarusEntity * entity,
|
|
struct IkarusBlueprint * blueprint,
|
|
IkarusEntityUnlinkBlueprintFlags flags,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"blueprint doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_blueprint_exists,
|
|
blueprint
|
|
);
|
|
|
|
IKARUS_FAIL_IF(
|
|
entity->project != blueprint->project,
|
|
IKARUS_VOID_RETURN,
|
|
"blueprint does not belong to entity's project",
|
|
IkarusErrorInfo_Client_NotLinked
|
|
);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to unlink blueprint from entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute(
|
|
"DELETE FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?",
|
|
entity->id,
|
|
blueprint->id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ikarus_entity_has_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) {
|
|
IKARUS_FAIL_IF_NULL(name, false);
|
|
|
|
IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto exists,
|
|
false,
|
|
"failed to check if entity has value: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_one<bool>(
|
|
"SELECT EXISTS(SELECT 1 FROM `entity_values` WHERE `entity` = ? AND `name` = ?)",
|
|
entity->id,
|
|
name
|
|
)
|
|
);
|
|
|
|
return exists;
|
|
}
|
|
|
|
IkarusEntityValue * ikarus_entity_get_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto values_plain,
|
|
nullptr,
|
|
"failed to get values for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_many<char const *, char const *>(
|
|
"SELECT `name`, `value` FROM `entity_values` WHERE `entity` = ?",
|
|
entity->id
|
|
)
|
|
);
|
|
|
|
IkarusEntityValue * values = new IkarusEntityValue[values_plain.size()];
|
|
|
|
std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [](auto const & tuple) {
|
|
return IkarusEntityValue{tuple.template get<0>(), tuple.template get<1>()};
|
|
});
|
|
|
|
if (size_out) {
|
|
*size_out = values_plain.size();
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
char const * ikarus_entity_get_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) {
|
|
IKARUS_FAIL_IF_NULL(name, nullptr);
|
|
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
IKARUS_ASCERTAIN(
|
|
nullptr,
|
|
"entity doesn't have value",
|
|
IkarusErrorInfo_Client_NotLinked,
|
|
ikarus_entity_has_value,
|
|
entity,
|
|
name
|
|
);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto value,
|
|
nullptr,
|
|
"failed to get value for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_one<char const *>(
|
|
"SELECT `value` FROM `entity_values` WHERE `entity` = ? AND `name` = ?",
|
|
entity->id,
|
|
name
|
|
)
|
|
);
|
|
|
|
return value;
|
|
}
|
|
|
|
void ikarus_entity_set_value(
|
|
IkarusEntity * entity,
|
|
char const * name,
|
|
char const * value,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN);
|
|
IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
|
|
// parsing from & to here to ensure values are valid JSON & formatted
|
|
// uniformly
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto value_parsed,
|
|
IKARUS_VOID_RETURN,
|
|
"cannot parse value as JSON: {}",
|
|
IkarusErrorInfo_Client_InvalidInput,
|
|
IkarusValue::from_json_str(value)
|
|
);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to set value for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute(
|
|
"INSERT INTO `entity_values`(`entity`, `name`, `value`) VALUES(?, ?, ?) "
|
|
"ON CONFLICT(`entity`, `name`) DO UPDATE SET `value` = excluded.`value`",
|
|
entity->id,
|
|
name,
|
|
IkarusValue::to_json(value_parsed).dump()
|
|
)
|
|
);
|
|
}
|
|
|
|
void ikarus_entity_delete_value(
|
|
IkarusEntity * entity,
|
|
char const * name,
|
|
IkarusEntityDeleteValueFlags flags,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to delete value for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute("DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", entity->id, name)
|
|
);
|
|
}
|
|
|
|
bool ikarus_entity_has_property_value(
|
|
IkarusEntity * entity,
|
|
struct IkarusProperty * property,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
false,
|
|
"property doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_property_exists,
|
|
property
|
|
);
|
|
|
|
IKARUS_FAIL_IF(
|
|
entity->project != property->project,
|
|
false,
|
|
"property does not belong to entity's project",
|
|
IkarusErrorInfo_Client_NotLinked
|
|
);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto exists,
|
|
false,
|
|
"failed to check if entity has property value: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_one<bool>(
|
|
"SELECT EXISTS(SELECT 1 FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?)",
|
|
entity->id,
|
|
property->id
|
|
)
|
|
);
|
|
|
|
return exists;
|
|
}
|
|
|
|
IkarusEntityPropertyValue *
|
|
ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto values_plain,
|
|
nullptr,
|
|
"failed to get property values for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_many<int64_t, char const *>(
|
|
"SELECT `p`.`id`, IFNULL(`v`.`value`, ikarus_default_value(`p`.`schema`)) "
|
|
"FROM `entities` AS `e` "
|
|
"INNER JOIN `entity_blueprint_links` as `l` ON `l`.`entity` = `e`.`id` "
|
|
"INNER JOIN `properties` AS `p` ON `p`.`blueprint` = `l`.`blueprint` "
|
|
"LEFT JOIN `entity_property_values` AS `v` ON `e`.`entity` = `e`.`id` "
|
|
"WHERE `e`.`entity` = ?",
|
|
entity->id
|
|
)
|
|
);
|
|
|
|
IkarusEntityPropertyValue * values = new IkarusEntityPropertyValue[values_plain.size()];
|
|
std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [entity](auto const & tuple) {
|
|
return IkarusEntityPropertyValue{
|
|
new IkarusProperty{entity->project, tuple.template get<0>()},
|
|
tuple.template get<1>()
|
|
};
|
|
});
|
|
|
|
if (size_out) {
|
|
*size_out = values_plain.size();
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
char const *
|
|
ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * property, IkarusErrorData * error_out) {
|
|
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
|
|
IKARUS_ASCERTAIN(
|
|
nullptr,
|
|
"property doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_property_exists,
|
|
property
|
|
);
|
|
|
|
IKARUS_FAIL_IF(
|
|
entity->project != property->project,
|
|
nullptr,
|
|
"property does not belong to entity's project",
|
|
IkarusErrorInfo_Client_NotLinked
|
|
);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
nullptr,
|
|
"entity doesn't have property value",
|
|
IkarusErrorInfo_Client_NotLinked,
|
|
ikarus_entity_has_property_value,
|
|
entity,
|
|
property
|
|
);
|
|
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto value,
|
|
nullptr,
|
|
"failed to get property value for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->query_one<char const *>(
|
|
"SELECT IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` "
|
|
"INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` "
|
|
"WHERE `e`.`entity` = ? AND `e`.`property` = ?",
|
|
entity->id,
|
|
property->id
|
|
)
|
|
);
|
|
|
|
return value;
|
|
}
|
|
|
|
void ikarus_entity_set_property_value(
|
|
IkarusEntity * entity,
|
|
struct IkarusProperty * property,
|
|
char const * value,
|
|
IkarusEntitySetPropertyValueFlags flags,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"property doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_property_exists,
|
|
property
|
|
);
|
|
|
|
IKARUS_FAIL_IF(
|
|
entity->project != property->project,
|
|
IKARUS_VOID_RETURN,
|
|
"property does not belong to entity's project",
|
|
IkarusErrorInfo_Client_NotLinked
|
|
);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity does not have property value",
|
|
IkarusErrorInfo_Client_NotLinked,
|
|
ikarus_entity_has_property_value,
|
|
entity,
|
|
property
|
|
);
|
|
|
|
// parsing from & to here to ensure values are valid JSON & formatted
|
|
// uniformly
|
|
IKARUS_VTRYRV_OR_FAIL(
|
|
auto value_parsed,
|
|
IKARUS_VOID_RETURN,
|
|
"cannot parse value as JSON: {}",
|
|
IkarusErrorInfo_Client_InvalidInput,
|
|
IkarusValue::from_json_str(value)
|
|
);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to set property value for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute(
|
|
"INSERT INTO `entity_property_values`(`entity`, `property`, `value`) VALUES(?, ?, ?) "
|
|
"ON CONFLICT(`entity`, `property`) DO UPDATE SET `value` = excluded.`value`",
|
|
entity->id,
|
|
property->id,
|
|
IkarusValue::to_json(value_parsed).dump()
|
|
)
|
|
);
|
|
}
|
|
|
|
void ikarus_entity_clear_property_value(
|
|
IkarusEntity * entity,
|
|
struct IkarusProperty * property,
|
|
IkarusEntitySetPropertyValueFlags flags,
|
|
IkarusErrorData * error_out
|
|
) {
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_entity_exists,
|
|
entity
|
|
);
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"property doesn't exist",
|
|
IkarusErrorInfo_Client_NonExistent,
|
|
ikarus_property_exists,
|
|
property
|
|
);
|
|
|
|
IKARUS_FAIL_IF(
|
|
entity->project != property->project,
|
|
IKARUS_VOID_RETURN,
|
|
"property does not belong to entity's project",
|
|
IkarusErrorInfo_Client_NotLinked
|
|
);
|
|
|
|
IKARUS_ASCERTAIN(
|
|
IKARUS_VOID_RETURN,
|
|
"entity doesn't have property",
|
|
IkarusErrorInfo_Client_NotLinked,
|
|
ikarus_entity_has_property_value,
|
|
entity,
|
|
property
|
|
);
|
|
|
|
IKARUS_TRYRV_OR_FAIL(
|
|
IKARUS_VOID_RETURN,
|
|
"failed to clear property value for entity: {}",
|
|
IkarusErrorInfo_Database_QueryFailed,
|
|
entity->project->db->execute(
|
|
"DELETE FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?",
|
|
entity->id,
|
|
property->id
|
|
)
|
|
);
|
|
}
|