diff --git a/include/ikarus/persistence/project.h b/include/ikarus/persistence/project.h index 7847a78..d0116a4 100644 --- a/include/ikarus/persistence/project.h +++ b/include/ikarus/persistence/project.h @@ -89,7 +89,7 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project, Ikar /// \param entities_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_entities( - IkarusProject const * project, + IkarusProject * project, struct IkarusEntity ** entities_out, size_t entities_out_size, IkarusErrorData * error_out @@ -112,7 +112,7 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, Ik /// \param blueprints_out_size The size of the buffer. /// \param error_out \see errors.h IKA_API void ikarus_project_get_blueprints( - IkarusProject const * project, + IkarusProject * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size, IkarusErrorData * error_out @@ -124,7 +124,7 @@ IKA_API void ikarus_project_get_blueprints( /// \pre \li Must exist. /// \param error_out \see errors.h /// \return The number of blueprints or undefined if an error occurs. -IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, IkarusErrorData * error_out); +IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject * project, IkarusErrorData * error_out); /// \brief Finds an entity by a given name. /// \param project The project to search. @@ -135,7 +135,7 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project, /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The entity with the given name or null if none was found. -IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); /// \brief Finds a property by a given name. /// \param project The project to search. @@ -151,7 +151,7 @@ IKA_API struct IkarusEntity * get_entity_by_name(IkarusProject const * project, /// \param error_out \see errors.h /// \return The property with the given name or null if none was found. IKA_API struct IkarusProperty * -get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); +get_property_by_name_and_scope(IkarusProject * project, struct IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out); /// \brief Finds a blueprint by a given name. /// \param project The project to search. @@ -162,7 +162,7 @@ get_property_by_name(IkarusProject const * project, struct IkarusPropertyScope * /// \pre \li Must not be empty. /// \param error_out \see errors.h /// \return The blueprint with the given name or null if none was found. -IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject const * project, char const * name, IkarusErrorData * error_out); +IKA_API struct IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out); IKARUS_END_HEADER diff --git a/src/ikarus/objects/blueprint.cpp b/src/ikarus/objects/blueprint.cpp index 326129d..dde1193 100644 --- a/src/ikarus/objects/blueprint.cpp +++ b/src/ikarus/objects/blueprint.cpp @@ -17,7 +17,7 @@ IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id): IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/entity.cpp b/src/ikarus/objects/entity.cpp index 94e8b75..0b3da7f 100644 --- a/src/ikarus/objects/entity.cpp +++ b/src/ikarus/objects/entity.cpp @@ -12,7 +12,7 @@ IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out) { IKARUS_FAIL_IF_NULL(project, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, nullptr, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, nullptr, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/properties/util.hpp b/src/ikarus/objects/properties/util.hpp index 092296e..2af0e32 100644 --- a/src/ikarus/objects/properties/util.hpp +++ b/src/ikarus/objects/properties/util.hpp @@ -23,7 +23,7 @@ T * create_property( IKARUS_FAIL_IF_NULL(project, nullptr); IKARUS_FAIL_IF_NULL(property_scope, nullptr); IKARUS_FAIL_IF_PROPERTY_SCOPE_INVALID(property_scope, nullptr); - IKARUS_FAIL_IF_NAME_INVALID(name, project, property_scope, nullptr); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, property_scope, nullptr); IKARUS_VTRYRV_OR_FAIL( IkarusId const id, diff --git a/src/ikarus/objects/util.hpp b/src/ikarus/objects/util.hpp index 29857cf..d0b9fa2 100644 --- a/src/ikarus/objects/util.hpp +++ b/src/ikarus/objects/util.hpp @@ -96,15 +96,18 @@ name_is_unique(IkarusProject * project, std::string_view name, IkarusProperty co return name_is_unique(project, name, scope.get(), error_out); } -#define IKARUS_FAIL_IF_NAME_INVALID(name, project, object, ret, ...) \ - IKARUS_FAIL_IF_NULL(name, ret); \ - IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); \ - IKARUS_FAIL_IF( \ - !ikarus::util::name_is_unique(project, name, object, error_out), \ - ret, \ - "name must be unique", \ - IkarusErrorInfo_Client_InvalidInput \ - ); \ +#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \ + IKARUS_FAIL_IF_NULL(name, ret); \ + IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput); + +#define IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, project, object, ret, ...) \ + IKARUS_FAIL_IF_NAME_INVALID(name, ret); \ + IKARUS_FAIL_IF( \ + !ikarus::util::name_is_unique(project, name, object, error_out), \ + ret, \ + "name must be unique", \ + IkarusErrorInfo_Client_InvalidInput \ + ); \ IKARUS_FAIL_IF_ERROR(ret); template @@ -112,7 +115,7 @@ void object_set_name(O * object, char const * name, IkarusErrorData * error_out) IKARUS_FAIL_IF_NULL(object, ); IKARUS_FAIL_IF_OBJECT_MISSING(object, ); IKARUS_FAIL_IF_NULL(name, ); - IKARUS_FAIL_IF_NAME_INVALID(name, object->project, object, ); + IKARUS_FAIL_IF_NAME_INVALID_OR_DUPLICATE(name, object->project, object, ); IKARUS_TRYRV_OR_FAIL( , diff --git a/src/ikarus/persistence/project.cpp b/src/ikarus/persistence/project.cpp index 6c460f7..b7c3e89 100644 --- a/src/ikarus/persistence/project.cpp +++ b/src/ikarus/persistence/project.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -275,3 +276,60 @@ size_t ikarus_project_get_entity_count(IkarusProject * project, IkarusErrorData return ret; } + +struct IkarusEntity * get_entity_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + // TODO, 'InvalidInput' doesn't really make sense here, we'd need to adjust the macros to support distinguishing between different + // errors. In this case `sqlitecpp::MissingRow` and database related errors. Same for the other functions. + IKARUS_VTRYRV_OR_FAIL( + auto const id, + nullptr, + "unable to find entity in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one("SELECT `id` FROM `entities` WHERE `name` = ?", name) + ); + + return project->get_entity(id); +} + +struct IkarusProperty * +get_property_by_name(IkarusProject * project, IkarusPropertyScope * scope, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto const id_and_type, + nullptr, + "unable to find property in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one( + "SELECT `id`, `type` FROM `properties` WHERE `name` = ? AND `scope` = ?", + name, + scope->get_id() + ) + ); + + auto const [id, type] = id_and_type; + + return project->get_property(id, type); +} + +IkarusBlueprint * get_blueprints_by_name(IkarusProject * project, char const * name, IkarusErrorData * error_out) { + IKARUS_FAIL_IF_NULL(project, nullptr); + IKARUS_FAIL_IF_NULL(name, nullptr); + IKARUS_FAIL_IF_NAME_INVALID(name, nullptr); + + IKARUS_VTRYRV_OR_FAIL( + auto const id, + nullptr, + "unable to find blueprint in database: {}", + IkarusErrorInfo_Client_InvalidInput, + project->db->query_one("SELECT `id` FROM `blueprints` WHERE `name` = ?", name) + ); + + return project->get_blueprint(id); +}