change error system & function signatures

Signed-off-by: Folling <mail@folling.io>
This commit is contained in:
Folling 2024-01-02 15:14:39 +01:00 committed by Folling
parent 1367373819
commit e1bf97704a
No known key found for this signature in database
28 changed files with 633 additions and 651 deletions

View file

@ -67,7 +67,7 @@ BreakInheritanceList: AfterColon
BreakStringLiterals: false
ColumnLimit: 140
CommentPragmas: '^\\.+'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4

View file

@ -1,7 +1,7 @@
Checks: >-
-*,
bugprone-*, -bugprone-lambda-function-name,
cppcoreguidelines-*, -cppcoreguidelines-owning-memory,
cppcoreguidelines-*, -cppcoreguidelines-owning-memory, -cppcoreguidelines-non-private-member-variables-in-classes,
clang-analyzer-*,
google-*, -google-readability-todo,
modernize-*, -modernize-use-trailing-return-type,

View file

@ -5,7 +5,7 @@ A snapshot if you will.
One must not rely on it representing the actual state of the project at any given time. The data is simply copied
from the underlying data sources and returned to the caller.
No mechanisms are provided to avoid race conditions. LibIkarus itself should only be used in a single-threaded context.
No mechanisms are provided to avoid race conditions. libikarus itself should only be used in a single-threaded context.
However, nothing breaks if you do use it in a multithreaded context, that is, libikarus is threadsafe.
You just cannot rely on the data being consistent.
This goes especially for inter-process access to the same project.

View file

@ -7,57 +7,111 @@
/// \addtogroup errors Errors
/// \brief Error handling within libikarus
/// \details Errors are stored for each project, akin to the errno handling in C.
/// We store multiple pieces of information about the error occurring. For more information see
/// #ikarus_project_get_error_message.
/// \details Functions in Ikarus may fail, in which case they have an out parameter for the error.
/// Upon erring the function will store relevant information about the error in the out parameter.
/// If the out parameter is null nothing will be stored. This is not recommended as it essentially ignores errors.
/// For the sake of simplicity we have avoided mechanisms that "force" clients to handle errors.
/// Note that Ikarus does not check for null pointers. Passing null pointers to functions that do not explicitly state that they accept null
/// pointers is undefined behaviour. This decision is done for the sake of brevity and readability. `project_get_name(project)` is also
/// synonymous to `project->get_name()` in OOP languages, which shares the same semantics.
/// @{
IKARUS_BEGIN_HEADER
/// \brief Delineates what caused an error.
/// \details First 2 bytes delineate the major type, next 2 bytes delineate the minor type, next 4 bytes delineate the
/// detail type.
/// \remark Note that this doesn't show responsibility. An error with source "SubSystem" could still be the
/// fault of libikarus.
/// fault of libikarus, it just indicates where the error occurred.
enum IkarusErrorInfo {
/// \brief No error occurred.
IkarusErrorInfo_Source_None = 0x0001000000000000,
IkarusErrorInfo_None = 0x0,
/// \brief The error was caused by the client.
IkarusErrorInfo_Source_Client = 0x0001000000000001,
/// \brief The error was caused by a sub-system of libikarus.
IkarusErrorInfo_Source_SubSystem = 0x0001000000000002,
IkarusErrorInfo_Client = 0x10100000,
/// \brief The error was caused by a dependency (e.g. boost) of libikarus.
IkarusErrorInfo_Dependency = 0x10200000,
/// \brief The error was caused by the filesystem.
IkarusErrorInfo_Filesystem = 0x10300000,
/// \brief The error was caused by the database.
IkarusErrorInfo_Database = 0x10400000,
/// \brief The error was caused by the underlying OS.
IkarusErrorInfo_OS = 0x10500000,
/// \brief The error was caused by libikarus itself.
IkarusErrorInfo_Source_LibIkarus = 0x0001000000000003,
/// \brief The error was caused by an unknown source.
IkarusErrorInfo_Source_Unknown = 0x00010000FFFFFFFF,
/// \brief No error occurred.
IkarusErrorInfo_Type_None = 0x0002000000000000,
/// \brief The user misused the API.
IkarusErrorInfo_LibIkarus = 0x10600000,
/// \brief The client misused the API.
/// Example: Accessing a resource that does not exist.
IkarusErrorInfo_Type_Client_Misuse = 0x0002000100000001,
/// \brief The user provided invalid input.
/// Example: Passing null for a pointer that must not be null.
IkarusErrorInfo_Type_Client_Input = 0x0002000100000002,
/// \brief An error occurred while interacting with a dependency from ikarus.
/// Example: An error occurred in the underlying OS library.
IkarusErrorInfo_Type_SubSystem_Dependency = 0x0002000200000001,
/// \brief An error occurred while interacting with the database.
/// Example: An error occurred while executing a query.
IkarusErrorInfo_Type_SubSystem_Database = 0x0002000200000002,
/// \brief An error occurred while interacting with the filesystem.
/// Example: An error occurred while reading a file.
IkarusErrorInfo_Type_SubSystem_Filesystem = 0x0002000200000003,
IkarusErrorInfo_Client_Misuse = 0x10100001,
/// \brief The client provided a null value for a parameter that must not be null.
/// Example: Passing null for `ikarus_project_get_name`
IkarusErrorInfo_Client_InvalidNull = 0x10100002,
/// \brief The client provided an index that was out of bounds for some array.
/// Example: Passing the index 3 for an `IkarusToggleValue` with size 3.
IkarusErrorInfo_Client_IndexOutOfBounds = 0x10100003,
/// \brief The client provided a numeric value that was out of bounds
/// Example: Passing the value 2^32 to an i32 (might be passed as a string).
IkarusErrorInfo_Client_ValueOutOfBounds = 0x10100004,
/// \brief The client provided invalid input that doesn't fit in any of the other categories.
/// Example: Passing an empty/blank string for a string that must be non-empty/-blank.
IkarusErrorInfo_Client_InvalidInput = 0x10100005,
/// \brief The client provided valid data in an invalid format.
/// Example: Passing a malformed JSON string.
IkarusErrorInfo_Client_InvalidFormat = 0x10100006,
/// \brief The client violated a constraint.
/// \details This error is most likely caused by endusers.
/// Example: A user tries to set the age of a character to an value outside of their specified range.
IkarusErrorInfo_Client_ConstraintViolated = 0x10100007,
/// \brief A file was not found.
IkarusErrorInfo_Filesystem_NotFound = 0x10300001,
/// \brief A file or directory already exists.
IkarusErrorInfo_Filesystem_AlreadyExists = 0x10300002,
/// \brief Missing permissions to access a file or directory.
IkarusErrorInfo_Filesystem_MissingPermissions = 0x10300003,
/// \brief Insufficient space to perform an operation.
IkarusErrorInfo_Filesystem_InsufficientSpace = 0x10300004,
/// \brief A path is invalid.
IkarusErrorInfo_Filesystem_InvalidPath = 0x10300005,
/// \brief A database connection failed.
IkarusErrorInfo_Database_ConnectionFailed = 0x10400001,
/// \brief A database query failed.
IkarusErrorInfo_Database_QueryFailed = 0x10400002,
/// \brief A database migration failed.
IkarusErrorInfo_Database_MigrationFailed = 0x10400003,
/// \brief A database is in an invalid state. This indicates a corrupt project.
/// Example: An entity is linked to a non-existant blueprint.
IkarusErrorInfo_Database_InvalidState = 0x10400004,
/// \brief A system call failed.
IkarusErrorInfo_OS_SystemCallFailed = 0x10500001,
/// \brief A system call returned an invalid value.
IkarusErrorInfo_OS_InvalidReturnValue = 0x10500002,
/// \brief An OOM error occurred.
IkarusErrorInfo_OS_InsufficientMemory = 0x10500003,
/// \brief A datapoint within ikarus is invalid for the current state of the system.
/// Example: The name of an object is found to be invalid UTF8.
IkarusErrorInfo_Type_LibIkarus_InvalidState = 0x0002000300000001,
/// \brief LibIkarus is unable to perform a certain operation that should succeed.
/// Example: Migrating a project fails
IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation = 0x0002000300000002,
/// \brief LibIkarus is unable to perform a certain operation within a given timeframe.
IkarusErrorInfo_LibIkarus_InvalidState = 0x20030001,
/// \brief libikarus is unable to perform a certain operation within a given timeframe.
/// Example: A query takes longer than the timeout.
IkarusErrorInfo_Type_LibIkarus_Timeout = 0x0002000300000003,
/// \brief The type of error is unknown.
IkarusErrorInfo_Type_Unknown = 0xFFFFFFFFFFFFFFFF,
IkarusErrorInfo_LibIkarus_Timeout = 0x20030003,
};
/// \brief The data limits for an error.
enum IkarusErrorDataLimit {
/// \brief The maximum number of error infos that can be stored in an error.
IkarusErrorDataLimit_MaxErrorInfos = 8,
/// \brief The maximum length of an error message.
IkarusErrorDataLimit_MaxMessageLength = 128,
};
/// \brief The data stored for an error
struct IkarusErrorData {
/// \brief The scope of the error.
/// \details This array may at most hold #IkarusErrorDataLimit_MaxErrorInfos elements.
/// The first occurrence of #IkarusErrorInfo_None signifies the end of the array. If this happens at idx x== 0, no error occurred.
IkarusErrorInfo infos[IkarusErrorDataLimit_MaxErrorInfos];
char message[IkarusErrorDataLimit_MaxMessageLength];
};
/// \brief Gets the name of an error info.
@ -66,6 +120,20 @@ enum IkarusErrorInfo {
/// \remark The returned pointer is valid for the lifetime of the program and must not be freed.
IKA_API char const * get_error_info_name(IkarusErrorInfo info);
/// \brief Checks if an error data is a success.
/// \param data The error data to check.
/// \return True if the error data is a success, false otherwise.
IKA_API bool ikarus_error_data_is_success(IkarusErrorData const * data);
/// \brief Checks if an error data is an error.
/// \param data The error data to check.
/// \return True if the error data is an error, false otherwise.
IKA_API bool ikarus_error_data_is_error(IkarusErrorData const * data);
/// \brief Formats the error data in a reasonable but unspecified way.
/// \param data The error data to format.
/// \return The formatted error data.
/// \remark Ownership of the returned pointer is passed to the user and must be freed at their leisure using ikarus_free.
IKA_API char const * ikarus_error_data_pretty_format(IkarusErrorData const * data);
IKARUS_END_HEADER
/// @}

View file

@ -28,8 +28,12 @@ IKARUS_BEGIN_HEADER
/// To avoid ordering fiascos and potential index performance degradation we just skip the first bit.
/// - next 7 bits: #IkarusObjectType
/// - last 56 bits: incremented counter generated by the database
using IkarusId = int64_t;
typedef int64_t IkarusId;
/// \brief Creates an id from the given data and type.
/// \param data The data to use for the id.
/// \param type The type to use for the id.
/// \return The created id.
IKA_API IkarusId ikarus_id_from_data_and_type(int64_t data, IkarusObjectType type);
/// \brief Fetches the object type of the given id.

View file

@ -3,6 +3,7 @@
/// \file blueprint.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
@ -24,16 +25,18 @@ struct IkarusBlueprint;
/// \param name The name of the blueprint.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param error_out \see errors.h
/// \return The created blueprint or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name);
IKA_API IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out);
/// \brief Deletes & frees a blueprint.
/// \param blueprint The blueprint to delete.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \remark The blueprint must not be accessed after deletion.
IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint);
IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint, IkarusErrorData * error_out);
/// \brief Gets the properties of a blueprint.
/// \param blueprint The blueprint to get the properties of.
@ -42,16 +45,22 @@ IKA_API void ikarus_blueprint_delete(IkarusBlueprint * blueprint);
/// \param properties_out The buffer to write the properties to.
/// \pre \li Must not be null.
/// \param properties_out_size The size of the buffer.
/// \param error_out \see errors.h
/// \see ikarus_blueprint_get_property_count
IKA_API void
ikarus_blueprint_get_properties(IkarusBlueprint const * blueprint, struct IkarusProperty ** properties_out, size_t properties_out_size);
IKA_API void ikarus_blueprint_get_properties(
IkarusBlueprint const * blueprint,
struct IkarusProperty ** properties_out,
size_t properties_out_size,
IkarusErrorData * error_out
);
/// \brief Gets the number of properties of a blueprint.
/// \param blueprint The blueprint to get the number of properties of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The number of properties or undefined if an error occurs.
IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint);
IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out);
/// \brief Gets the entities linked to a blueprint.
/// \param blueprint The blueprint to get the linked entities of.
@ -60,27 +69,34 @@ IKA_API size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * bluep
/// \param entities_out The buffer to write the entities to.
/// \pre \li Must not be null.
/// \param entities_out_size The size of the buffer.
/// \param error_out \see errors.h
/// \see ikarus_blueprint_get_linked_entity_count
IKA_API void
ikarus_blueprint_get_linked_entities(IkarusBlueprint const * blueprint, struct IkarusEntity ** entities_out, size_t entities_out_size);
IKA_API void ikarus_blueprint_get_linked_entities(
IkarusBlueprint const * blueprint,
struct IkarusEntity ** entities_out,
size_t entities_out_size,
IkarusErrorData * error_out
);
/// \brief Gets the number of entities linked to a blueprint.
/// \param blueprint The blueprint to get the number of linked entities of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The number of linked entities or undefined if an error occurs.
IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint);
IKA_API size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint, IkarusErrorData * error_out);
/// \brief Casts a blueprint to an object.
/// \param blueprint The blueprint to cast.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The blueprint represented as an object or null if an error occurs.
/// \remark This operation is guaranteed to be very fast and is intended to be used frequently.
IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint);
IKA_API struct IkarusObject * ikarus_blueprint_to_object(IkarusBlueprint * blueprint, IkarusErrorData * error_out);
/// \see ikarus_blueprint_to_object
IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint);
IKA_API struct IkarusObject const * ikarus_blueprint_to_object_const(IkarusBlueprint const * blueprint, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -1,5 +1,6 @@
#pragma once
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
@ -37,24 +38,21 @@ struct IkarusEntity;
/// \param project The project the entity is part of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param parent The parent folder of the entity.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param position The position of the entity in the parent folder. \see #FolderPosition
/// \pre \li Must be within bounds for the parent folder.
/// \param name The name of the entity.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param error_out \see errors.h
/// \return The created entity or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name);
IKA_API IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name, IkarusErrorData * error_out);
/// \brief Deletes an entity.
/// \param entity The entity to delete.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \remark The entity must not be accessed after deletion.
IKA_API void ikarus_entity_delete(IkarusEntity * entity);
IKA_API void ikarus_entity_delete(IkarusEntity * entity, IkarusErrorData * error_out);
/// \brief Checks if an entity is linked to a blueprint.
/// \param entity The entity to check.
@ -63,8 +61,10 @@ IKA_API void ikarus_entity_delete(IkarusEntity * entity);
/// \param blueprint The blueprint to check.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return True if the entity is linked to the blueprint, false otherwise.
IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint);
IKA_API bool
ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, struct IkarusBlueprint const * blueprint, IkarusErrorData * error_out);
/// \brief Links an entity to a blueprint.
/// \param entity The entity to link.
@ -73,8 +73,9 @@ IKA_API bool ikarus_entity_is_linked_to_blueprint(IkarusEntity const * entity, s
/// \param blueprint The blueprint to link the entity to.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \remark No-op if the entity is already linked to the blueprint.
IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint);
IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out);
/// \brief Unlinks an entity from a blueprint. All values of the properties of the blueprint the entity is linked with
/// will be deleted.
@ -84,8 +85,9 @@ IKA_API void ikarus_entity_link_to_blueprint(IkarusEntity * entity, struct Ikaru
/// \param blueprint The blueprint to unlink the entity from.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \remark No-op if the entity is not linked to the blueprint.
IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint);
IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct IkarusBlueprint * blueprint, IkarusErrorData * error_out);
/// \brief Checks if an entity has a specific property.
/// \param entity The entity to check.
@ -94,15 +96,17 @@ IKA_API void ikarus_entity_unlink_from_blueprint(IkarusEntity * entity, struct I
/// \param property The property to check.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return True if the entity has the property, false otherwise.
IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property);
IKA_API bool ikarus_entity_has_property(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Gets the number of properties of an entity.
/// \param entity The entity to get the number of properties of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The number of properties or undefined if an error occurs.
IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity);
IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity, IkarusErrorData * error_out);
/// \brief Gets the properties of an entity.
/// \param entity The entity to get the properties of.
@ -110,8 +114,14 @@ IKA_API size_t ikarus_entity_get_property_count(IkarusEntity const * entity);
/// \pre \li Must exist.
/// \param properties_out The buffer to write the properties to.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \param properties_out_size The size of the buffer.
IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct IkarusProperty ** properties_out, size_t properties_out_size);
IKA_API void ikarus_entity_get_properties(
IkarusEntity const * entity,
struct IkarusProperty ** properties_out,
size_t properties_out_size,
IkarusErrorData * error_out
);
/// \brief Gets the value of a property of an entity.
/// \details If the entity has never set the value of the property, the default value is returned (which may be
@ -122,10 +132,12 @@ IKA_API void ikarus_entity_get_properties(IkarusEntity const * entity, struct Ik
/// \param property The property to get the value of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The value of the property or null if the entity does not have the property or an error occurs.
/// \remark Must be freed using
/// #ikarus_free.
IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property);
IKA_API struct IkarusEntityValue *
ikarus_entity_get_value(IkarusEntity const * entity, struct IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Sets the value of a property of an entity.
/// \param entity The entity to set the value of.
@ -138,19 +150,26 @@ IKA_API struct IkarusEntityValue * ikarus_entity_get_value(IkarusEntity const *
/// \pre \li Must not be null.
/// \pre \li Must be of the same type as the property.
/// \pre \li Must be valid for the property's settings.
/// \param error_out \see errors.h
/// \remark If the entity does not have the property, this function fails.
IKA_API void ikarus_entity_set_value(IkarusEntity * entity, struct IkarusProperty const * property, struct IkarusValue const * value);
IKA_API void ikarus_entity_set_value(
IkarusEntity * entity,
struct IkarusProperty const * property,
struct IkarusValue const * value,
IkarusErrorData * error_out
);
/// \brief Casts an entity to an object.
/// \param entity The entity to cast.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The entity represented as an object or null if an error occurs.
/// \remark This operation is guaranteed to be very fast and is intended to be used frequently.
IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity);
IKA_API struct IkarusObject * ikarus_entity_to_object(IkarusEntity * entity, IkarusErrorData * error_out);
/// \see ikarus_entity_to_object
IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity);
IKA_API struct IkarusObject const * ikarus_entity_to_object_const(IkarusEntity const * entity, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file object.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \defgroup object Objects
@ -20,10 +21,14 @@ struct IkarusObject;
/// \brief Compares two objects for equality.
/// \details This neither compares the pointers nor does a deep copy. Instead it figures out if the objects _are_ the
/// same object. \param lhs The left hand side object. \pre \li Must not be null. \param rhs The right hand side object.
/// same object.
/// \param lhs The left hand side object.
/// \pre \li Must not be null.
/// \param rhs The right hand side object.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the objects are equal, false otherwise.
IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs);
IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const * rhs, IkarusErrorData * error_out);
/// \brief Visits an object. Calling the appropriate function for the object's type.
/// \param object The object to visit.
@ -34,6 +39,7 @@ IKA_API bool ikarus_object_is_equal(IkarusObject const * lhs, IkarusObject const
/// \param property_folder_visitor The function to call if the object is a property folder. Skipped if null.
/// \param entity_folder_visitor The function to call if the object is an entity folder. Skipped if null.
/// \param data The data passed to the visitor functions.
/// \param error_out \see errors.h
IKA_API void ikarus_object_visit(
IkarusObject * object,
void (*blueprint_visitor)(struct IkarusBlueprint *, void *),
@ -42,7 +48,8 @@ IKA_API void ikarus_object_visit(
void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder *, void *),
void (*property_folder_visitor)(struct IkarusPropertyFolder *, void *),
void (*entity_folder_visitor)(struct IkarusEntityFolder *, void *),
void * data
void * data,
IkarusErrorData * error_out
);
/// \see ikarus_object_visit
@ -54,7 +61,8 @@ IKA_API void ikarus_object_visit_const(
void (*blueprint_folder_visitor)(struct IkarusBlueprintFolder const *, void *),
void (*property_folder_visitor)(struct IkarusPropertyFolder const *, void *),
void (*entity_folder_visitor)(struct IkarusEntityFolder const *, void *),
void * data
void * data,
IkarusErrorData * error_out
);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file object_type.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \addtogroup objects Objects
@ -24,9 +25,10 @@ enum IkarusObjectType {
/// \brief Converts an IkarusObjectType to a string.
/// \param type The type to convert.
/// \param error_out \see errors.h
/// \return The string representation of the type.
/// \remark The returned string must not be freed.
char const * ikarus_object_type_to_string(IkarusObjectType type);
char const * ikarus_object_type_to_string(IkarusObjectType type, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -3,24 +3,44 @@
/// \file number_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// \brief Number properties store a numeric value. (e.g. "Weight" or "Age")
/// \brief Number properties store a value that can be either true or false. (e.g. "Is the character dead?")
/// @{
IKARUS_BEGIN_HEADER
struct IkarusNumberProperty;
IKA_API IkarusNumberProperty *
ikarus_number_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source);
/// \brief Creates a number property.
/// \param project The project to create the property in.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The name of the property.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param property_source The property source to create the property for.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The created property or null if an error occurs.
IKA_API IkarusNumberProperty * ikarus_number_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertySource * property_source,
IkarusErrorData * error_out
);
/// \brief Sets the default value for a number property.
/// \param property The number property.
/// \pre \li Must not be null.
/// \pre \li Must exist.
IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(struct IkarusNumberProperty * property);
/// \param error_out \see errors.h
/// \return The default value or null if an error occurs.
IKA_API struct IkarusNumberValue *
ikarus_number_property_get_default_value(struct IkarusNumberProperty * property, IkarusErrorData * error_out);
/// \brief Sets the default value for a number property.
/// \param property The number property.
@ -29,9 +49,14 @@ IKA_API struct IkarusNumberValue * ikarus_number_property_get_default_value(stru
/// \param default_value The default value.
/// \pre \li Must not be null.
/// \pre \li Must be a valid value for the property.
/// \param error_out \see errors.h
/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between
/// default values and other settings.
IKA_API void ikarus_number_property_set_default_value(struct IkarusNumberProperty * property, struct IkarusNumberValue * default_value);
IKA_API void ikarus_number_property_set_default_value(
struct IkarusNumberProperty * property,
struct IkarusNumberValue * default_value,
IkarusErrorData * error_out
);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/objects/properties/property_type.h>
#include <ikarus/stdtypes.h>
@ -59,32 +60,36 @@ struct IkarusProperty;
/// \param property The property to delete.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \remark The property must not be accessed after deletion.
IKA_API void ikarus_property_delete(IkarusProperty * property);
IKA_API void ikarus_property_delete(IkarusProperty * property, IkarusErrorData * error_out);
/// \brief Gets the type info of a property.
/// \param property The property to get the type info of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The type info of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property);
IKA_API IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Gets the source of a property.
/// \param property The property to get the source of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The source of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property);
IKA_API struct IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Gets the default value of a property.
/// \param property The property to get the type info of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The default value of the property or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property);
IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusProperty const * property, IkarusErrorData * error_out);
/// \brief Visits a property. Calling the appropriate function for the property's type.
/// \param property The property to visit.
@ -94,12 +99,14 @@ IKA_API struct IkarusValue const * ikarus_property_get_default_value(IkarusPrope
/// \param number_property_visitor The function to call if the property is a number property. Skipped if null.
/// \param text_property_visitor The function to call if the property is a text property. Skipped if null.
/// \param data The data to pass to the functions.
/// \param error_out \see errors.h
IKA_API void ikarus_property_visit(
IkarusProperty * property,
void (*toggle_property_visitor)(struct IkarusToggleProperty *, void *),
void (*number_property_visitor)(struct IkarusNumberProperty *, void *),
void (*text_property_visitor)(struct IkarusTextProperty *, void *),
void * data
void * data,
IkarusErrorData * error_out
);
/// \see ikarus_property_visit
@ -108,19 +115,21 @@ IKA_API void ikarus_property_visit_const(
void (*toggle_property_visitor)(struct IkarusToggleProperty const *, void *),
void (*number_property_visitor)(struct IkarusNumberProperty const *, void *),
void (*text_property_visitor)(struct IkarusTextProperty const *, void *),
void * data
void * data,
IkarusErrorData * error_out
);
/// \brief Casts a property to an object.
/// \param property The property to cast.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The property represented as an object or null if an error occurs.
/// \remark This operation is guaranteed to be very fast and is intended to be used frequently.
IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property);
IKA_API struct IkarusObject * ikarus_property_to_object(IkarusProperty * property, IkarusErrorData * error_out);
/// \see ikarus_property_to_object
IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property);
IKA_API struct IkarusObject const * ikarus_property_to_object_const(IkarusProperty const * property, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file property_source.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
@ -16,17 +17,20 @@ struct IkarusPropertySource;
/// \param blueprint The blueprint to create the property source for.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The created property source or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusPropertySource * ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint);
IKA_API struct IkarusPropertySource *
ikarus_property_source_create_blueprint(struct IkarusBlueprint * blueprint, IkarusErrorData * error_out);
/// \brief Creates an entity property source.
/// \param entity The entity to create the property source for.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The created property source or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity);
IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struct IkarusEntity * entity, IkarusErrorData * error_out);
/// \brief Visits a property source, calling the appropriate callback.
/// \param property_source The property source to visit.
@ -35,11 +39,13 @@ IKA_API struct IkarusPropertySource * ikarus_property_source_create_entity(struc
/// \param blueprint_visitor The callback to call if the source is a blueprint, skipped if null.
/// \param entity_visitor The callback to call if the source is an entity, skipped if null.
/// \param user_data User data to pass to the callbacks.
/// \param error_out \see errors.h
IKA_API void ikarus_property_source_visit(
struct IkarusPropertySource * property_source,
void (*blueprint_visitor)(struct IkarusBlueprint *, void *),
void (*entity_visitor)(struct IkarusEntity *, void *),
void * user_data
void * user_data,
IkarusErrorData * error_out
);
/// \see ikarus_property_source_visit
@ -47,7 +53,8 @@ IKA_API void ikarus_property_source_visit_const(
struct IkarusPropertySource const * property_source,
void (*blueprint_visitor)(struct IkarusBlueprint const *, void *),
void (*entity_visitor)(struct IkarusEntity const *, void *),
void * user_data
void * user_data,
IkarusErrorData * error_out
);
IKARUS_END_HEADER

View file

@ -3,24 +3,43 @@
/// \file text_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
/// \brief Text properties store an arbitrary piece of text. (e.g. "Firstname" or "Description")
/// \brief Text properties store a value that can be either true or false. (e.g. "Is the character dead?")
/// @{
IKARUS_BEGIN_HEADER
struct IkarusTextProperty;
IKA_API IkarusTextProperty *
ikarus_text_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source);
/// \brief Creates a text property.
/// \param project The project to create the property in.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The name of the property.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param property_source The property source to create the property for.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The created property or null if an error occurs.
IKA_API IkarusTextProperty * ikarus_text_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertySource * property_source,
IkarusErrorData * error_out
);
/// \brief Sets the default value for a text property.
/// \param property The text property.
/// \pre \li Must not be null.
/// \pre \li Must exist.
IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property);
/// \param error_out \see errors.h
/// \return The default value or null if an error occurs.
IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct IkarusTextProperty * property, IkarusErrorData * error_out);
/// \brief Sets the default value for a text property.
/// \param property The text property.
@ -29,9 +48,14 @@ IKA_API struct IkarusTextValue * ikarus_text_property_get_default_value(struct I
/// \param default_value The default value.
/// \pre \li Must not be null.
/// \pre \li Must be a valid value for the property.
/// \param error_out \see errors.h
/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between
/// default values and other settings.
IKA_API void ikarus_text_property_set_default_value(struct IkarusTextProperty * property, struct IkarusTextValue * default_value);
IKA_API void ikarus_text_property_set_default_value(
struct IkarusTextProperty * property,
struct IkarusTextValue * default_value,
IkarusErrorData * error_out
);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file toggle_property.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \addtogroup properties Properties
@ -13,14 +14,33 @@ IKARUS_BEGIN_HEADER
struct IkarusToggleProperty;
IKA_API IkarusToggleProperty *
ikarus_toggle_property_create(struct IkarusProject * project, char const * name, struct IkarusPropertySource * property_source);
/// \brief Creates a toggle property.
/// \param project The project to create the property in.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The name of the property.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param property_source The property source to create the property for.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The created property or null if an error occurs.
IKA_API IkarusToggleProperty * ikarus_toggle_property_create(
struct IkarusProject * project,
char const * name,
struct IkarusPropertySource * property_source,
IkarusErrorData * error_out
);
/// \brief Sets the default value for a toggle property.
/// \param property The toggle property.
/// \pre \li Must not be null.
/// \pre \li Must exist.
IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property);
/// \param error_out \see errors.h
/// \return The default value or null if an error occurs.
IKA_API struct IkarusToggleValue *
ikarus_toggle_property_get_default_value(struct IkarusToggleProperty * property, IkarusErrorData * error_out);
/// \brief Sets the default value for a toggle property.
/// \param property The toggle property.
@ -29,9 +49,14 @@ IKA_API struct IkarusToggleValue * ikarus_toggle_property_get_default_value(stru
/// \param default_value The default value.
/// \pre \li Must not be null.
/// \pre \li Must be a valid value for the property.
/// \param error_out \see errors.h
/// \remark Please see \ref property.h "the property documentation" for more information on the interplay between
/// default values and other settings.
IKA_API void ikarus_toggle_property_set_default_value(struct IkarusToggleProperty * property, struct IkarusToggleValue * default_value);
IKA_API void ikarus_toggle_property_set_default_value(
struct IkarusToggleProperty * property,
struct IkarusToggleValue * default_value,
IkarusErrorData * error_out
);
IKARUS_END_HEADER

View file

@ -25,28 +25,31 @@ struct IkarusProject;
/// \param name The name of the project. Must neither be null nor empty.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param error_out \see errors.h
/// \return The created project or null if an error occurs.
/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use
/// ikarus_project_delete
IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name);
IKA_API IkarusProject * ikarus_project_create(char const * path, char const * name, IkarusErrorData * error_out);
/// \brief Creates a project in memory.
/// \param name The name of the project. Must neither be null nor empty.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param error_out \see errors.h
/// \return The created project or null if an error occurs.
/// \remark Must be freed using #ikarus_free. Freeing does not delete the project from the filesystem. For that, use
/// ikarus_project_delete
IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name);
IKA_API IkarusProject * ikarus_project_create_in_memory(char const * name, IkarusErrorData * error_out);
/// \brief Opens an existing project.
/// \param path The path to the project.
/// \pre \li Must not be null.
/// \pre \li Must point to an existing project on the system.
/// \param error_out \see errors.h
/// \return The opened project or null if an error occurs.
/// \remark Must be freed using ikarus_free. Freeing does not delete the project from the filesystem. For that, use
/// ikarus_project_delete
IKA_API IkarusProject * ikarus_project_open(char const * path);
IKA_API IkarusProject * ikarus_project_open(char const * path, IkarusErrorData * error_out);
/// \brief Copies a project to a new location.
/// \details The new project is not opened.
@ -59,25 +62,29 @@ IKA_API IkarusProject * ikarus_project_open(char const * path);
/// \param target_name The name of the new project.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
/// \param error_out \see errors.h
/// \remark If successful the project connection remains intact. The previous location will still exist.
IKA_API void ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name);
IKA_API void
ikarus_project_copy(IkarusProject const * project, char const * target_path, char const * target_name, IkarusErrorData * error_out);
/// \brief Deletes a project and all its associated data from the filesystem.
/// \param project The project to delete.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \remark also frees the project.
/// \remark In-Memory projects will just be freed.
/// \remark If deletion fails, the project pointer remains intact.
IKA_API void ikarus_project_delete(IkarusProject * project);
IKA_API void ikarus_project_delete(IkarusProject * project, IkarusErrorData * error_out);
/// \brief Gets the name of a project.
/// \param project The project to get the name of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The name of the project.
/// \remark Must be freed using #ikarus_free.
IKA_API char const * ikarus_project_get_name(IkarusProject const * project);
IKA_API char const * ikarus_project_get_name(IkarusProject const * project, IkarusErrorData * error_out);
/// \brief Sets the name of a project.
/// \param project The project to set the name of.
@ -86,15 +93,17 @@ IKA_API char const * ikarus_project_get_name(IkarusProject const * project);
/// \param new_name The new name of the project.
/// \pre \li Must not be null.
/// \pre \li Must not be empty.
IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name);
/// \param error_out \see errors.h
IKA_API void ikarus_project_set_name(IkarusProject * project, char const * new_name, IkarusErrorData * error_out);
/// \brief Gets the path of a project.
/// \param project The project to get the path of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The path of the project.
/// \remark Must be freed using #ikarus_free.
IKA_API char const * ikarus_project_get_path(IkarusProject const * project);
IKA_API char const * ikarus_project_get_path(IkarusProject const * project, IkarusErrorData * error_out);
/// \brief Moves a project to a new location.
/// \param project The project to move.
@ -103,39 +112,19 @@ IKA_API char const * ikarus_project_get_path(IkarusProject const * project);
/// \param target_path The new location of the project.
/// \pre \li Must not be null.
/// \pre \li Must point to a valid unused path on the system.
/// \param error_out \see errors.h
/// \remark If successful the project connection remains intact. The previous location will not exist anymore.
/// \remark Due to the nature of filesystems this function may not be atomic.
IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path);
/// \brief Gets the error code of a project.
/// \param project The project to get the error code of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return The error code of the project.
IKA_API int ikarus_project_get_error_code(IkarusProject const * project);
/// \brief Gets the error message of a project.
/// \param project The project to get the error message of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return The error message of the project.
/// \remark The returned pointer is valid until the project is freed but may be altered by other operations.
/// \warning Must not be freed.
IKA_API char const * ikarus_project_get_error_message(IkarusProject const * project);
IKA_API void ikarus_project_move(IkarusProject * project, char const * target_path, IkarusErrorData * error_out);
/// \brief Gets the blueprint root folder of a project.
/// \param project The project to get the blueprint root folder of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The blueprint root folder of the project or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project);
/// \brief Gets the number of blueprints of a project.
/// \param project The project to get the number of blueprints of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return The number of blueprints or undefined if an error occurs.
IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project);
IKA_API struct IkarusBlueprintFolder * ikarus_project_get_blueprint_root_folder(IkarusProject const * project, IkarusErrorData * error_out);
/// \brief Gets the blueprints of a project.
/// \param project The project to get the blueprints of.
@ -144,23 +133,30 @@ IKA_API size_t ikarus_project_get_blueprint_count(IkarusProject const * project)
/// \param blueprints_out The buffer to write the blueprints to.
/// \pre \li Must not be null.
/// \param blueprints_out_size The size of the buffer.
IKA_API void
ikarus_project_get_blueprints(IkarusProject const * project, struct IkarusBlueprint ** blueprints_out, size_t blueprints_out_size);
/// \param error_out \see errors.h
IKA_API void ikarus_project_get_blueprints(
IkarusProject const * project,
struct IkarusBlueprint ** blueprints_out,
size_t blueprints_out_size,
IkarusErrorData * error_out
);
/// \brief Gets the number of blueprints of a project.
/// \param project The project to get the number of blueprints of.
/// \pre \li Must not be null.
/// \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);
/// \brief Gets the entity root folder of a project.
/// \param project The project to get the entity root folder of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The entity root folder of the project or null if an error occurs.
/// \remark Must be freed using #ikarus_free.
IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project);
/// \brief Gets the number of entities of a project.
/// \param project The project to get the number of entities of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \return The number of entities or undefined if an error occurs.
IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project);
IKA_API struct IkarusEntityFolder * ikarus_project_get_entity_root_folder(IkarusProject const * project, IkarusErrorData * error_out);
/// \brief Gets the entities of a project.
/// \param project The project to get the entities of.
@ -169,7 +165,21 @@ IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project);
/// \param entities_out The buffer to write the entities to.
/// \pre \li Must not be null.
/// \param entities_out_size The size of the buffer.
IKA_API void ikarus_project_get_entities(IkarusProject const * project, struct IkarusEntity ** entities_out, size_t entities_out_size);
/// \param error_out \see errors.h
IKA_API void ikarus_project_get_entities(
IkarusProject const * project,
struct IkarusEntity ** entities_out,
size_t entities_out_size,
IkarusErrorData * error_out
);
/// \brief Gets the number of entities of a project.
/// \param project The project to get the number of entities of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param error_out \see errors.h
/// \return The number of entities or undefined if an error occurs.
IKA_API size_t ikarus_project_get_entity_count(IkarusProject const * project, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -3,8 +3,8 @@
/// \file number_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
/// \addtogroup values Values
/// @{
@ -14,24 +14,27 @@ IKARUS_BEGIN_HEADER
/// \brief A numeric value. For example "Age" or "Height".
struct IkarusNumberValue;
/// \brief Creates a number value.
/// \brief Creates an empty number value.
/// \param error_out \see errors.h
/// \return The value or null if an error occurs.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusNumberValue * ikarus_number_value_create();
IKA_API IkarusNumberValue * ikarus_number_value_create(IkarusErrorData * error_out);
/// \brief Fetches the underlying data of a number value at a specific index.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param idx The index of the data to fetch.
/// \pre \li Must be less than the size of the value.
/// \return The underlying data or null if an error occurs or the value is undefined.
IKA_API double const * ikarus_number_value_get(IkarusNumberValue * value, size_t idx);
/// \param error_out \see errors.h
/// \return The underlying data or NaN if an error occurs or the value is undefined.
IKA_API double ikarus_number_value_get(IkarusNumberValue const * value, size_t idx, IkarusErrorData * error_out);
/// \brief Fetches the size of the underlying data of a number value.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The size of the underlying data or 0 if an error occurs or the value is undefined.
IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value);
IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value, IkarusErrorData * error_out);
/// \brief Sets the data of a number value at a specific index.
/// \param value The number value.
@ -39,14 +42,16 @@ IKA_API size_t ikarus_number_value_get_size(IkarusNumberValue const * value);
/// \param idx The index of the data to set.
/// \pre \li Must be less than the size of the value.
/// \param new_data The new data.
IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double const * new_data);
/// \param error_out \see errors.h
IKA_API void ikarus_number_value_set(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out);
/// \brief Removes a data from a number value.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param idx The index of the data to remove.
/// \pre \li Must be less than the size of the value.
IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx);
/// \param error_out \see errors.h
IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx, IkarusErrorData * error_out);
/// \brief Inserts a data into a number value.
/// \param value The number value.
@ -54,60 +59,68 @@ IKA_API void ikarus_number_value_remove(IkarusNumberValue * value, size_t idx);
/// \param idx The index of the data to insert.
/// \pre \li Must be less than or equal to the size of the value.
/// \param new_data The new data.
IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double const * new_data);
/// \param error_out \see errors.h
IKA_API void ikarus_number_value_insert(IkarusNumberValue * value, size_t idx, double new_data, IkarusErrorData * error_out);
/// \brief Clears a number value.
/// \param value The number value.
/// \param error_out \see errors.h
/// \remark Noop if the value is undefined.
IKA_API void ikarus_number_value_clear(IkarusNumberValue * value);
IKA_API void ikarus_number_value_clear(IkarusNumberValue * value, IkarusErrorData * error_out);
/// \brief Checks if a number value is undefined.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the value is undefined, false otherwise.
IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value);
IKA_API bool ikarus_number_value_is_undefined(IkarusNumberValue const * value, IkarusErrorData * error_out);
/// \brief Changes a number value's undefined state.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param undefined The new undefined state.
/// \param error_out \see errors.h
/// \remark Noop if the value is already undefined.
/// \remark If the value is set to undefined, all data will be cleared.
/// \remark If the value is set to not undefined, the value is as if newly created.
IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined);
IKA_API void ikarus_number_value_set_undefined(IkarusNumberValue * value, bool undefined, IkarusErrorData * error_out);
/// \brief Converts a number value to a string.
/// \param value The number value to convert.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The converted string.
/// \remark Must be freed with #ikarus_free.
/// \remark Undefined if the value is undefined.
IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value);
IKA_API char const * ikarus_number_value_to_string(IkarusNumberValue const * value, IkarusErrorData * error_out);
/// \brief Checks if two values are equal.
/// \param lhs The left hand side value.
/// \pre \li Must not be null.
/// \param rhs The right hand side value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the values' data are equal, false otherwise.
IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs);
IKA_API bool ikarus_number_value_is_equal(IkarusNumberValue const * lhs, IkarusNumberValue const * rhs, IkarusErrorData * error_out);
/// \brief Creates a copy of a number value.
/// \param value The value to copy.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The copied value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value);
IKA_API IkarusNumberValue * ikarus_number_value_copy(IkarusNumberValue const * value, IkarusErrorData * error_out);
/// \brief Converts a number value to an entity value.
/// \param value The number value to convert.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The converted entity value.
/// \remark This is the same pointer, so freeing it implies freeing the original value.
IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value);
IKA_API struct IkarusValue * ikarus_number_value_to_value(IkarusNumberValue * value, IkarusErrorData * error_out);
/// \see ikarus_toggle_value_to_value
IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value);
IKA_API struct IkarusValue const * ikarus_number_value_to_value_const(IkarusNumberValue const * value, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file text_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
@ -14,101 +15,115 @@ IKARUS_BEGIN_HEADER
/// \brief A textual value. For example "Surname" or "Description"
struct IkarusTextValue;
/// \brief Creates a text value.
/// \brief Creates an empty text value.
/// \param error_out \see errors.h
/// \return The value or null if an error occurs.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTextValue * ikarus_text_value_create();
IKA_API IkarusTextValue * ikarus_text_value_create(IkarusErrorData * error_out);
/// \brief Fetches the underlying data of a number value at a specific index.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param idx The index of the data to fetch.
/// \pre \li Must be less than the size of the value.
/// \param error_out \see errors.h
/// \return The underlying data or null if an error occurs or the value is undefined.
/// \remark This value is owned by LibIkarus and must not be freed.
IKA_API char const * ikarus_text_value_get(IkarusTextValue * value, size_t idx);
IKA_API char const * const * ikarus_text_value_get(IkarusTextValue const * value, size_t idx, IkarusErrorData * error_out);
/// \brief Fetches the size of the underlying data of a text value.
/// \param value The text value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The size of the underlying data or 0 if an error occurs or the value is undefined.
IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value);
IKA_API size_t ikarus_text_value_get_size(IkarusTextValue const * value, IkarusErrorData * error_out);
/// \brief Sets the data of a text value at a specific index.
/// \param value The text value.
/// \pre \li Must not be null.
/// \param idx The index of the data to set.
/// \pre \li Must be less than the size of the value.
/// \param new_data The new data. LibIkarus assumes ownership of this pointer.
IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data);
/// \param new_data The new data. Ownership remains with the caller.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
IKA_API void ikarus_text_value_set(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out);
/// \brief Removes a data from a text value.
/// \param value The text value.
/// \pre \li Must not be null.
/// \param idx The index of the data to remove.
/// \pre \li Must be less than the size of the value.
IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx);
/// \param error_out \see errors.h
IKA_API void ikarus_text_value_remove(IkarusTextValue * value, size_t idx, IkarusErrorData * error_out);
/// \brief Inserts a data into a text value.
/// \param value The text value.
/// \pre \li Must not be null.
/// \param idx The index of the data to insert.
/// \pre \li Must be less than or equal to the size of the value.
/// \param new_data The new data.
IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data);
/// \param new_data The new data. Ownership remains with the caller.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
IKA_API void ikarus_text_value_insert(IkarusTextValue * value, size_t idx, char const * new_data, IkarusErrorData * error_out);
/// \brief Clears a text value.
/// \param value The text value.
/// \param error_out \see errors.h
/// \remark Noop if the value is undefined.
IKA_API void ikarus_text_value_clear(IkarusTextValue * value);
IKA_API void ikarus_text_value_clear(IkarusTextValue * value, IkarusErrorData * error_out);
/// \brief Checks if a text value is undefined.
/// \param value The text value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the value is undefined, false otherwise.
IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value);
IKA_API bool ikarus_text_value_is_undefined(IkarusTextValue const * value, IkarusErrorData * error_out);
/// \brief Changes a text value's undefined state.
/// \param value The text value.
/// \pre \li Must not be null.
/// \param undefined The new undefined state.
/// \param error_out \see errors.h
/// \remark Noop if the value is already undefined.
/// \remark If the value is set to undefined, all data will be cleared.
/// \remark If the value is set to not undefined, the value is as if newly created.
IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined);
IKA_API void ikarus_text_value_set_undefined(IkarusTextValue * value, bool undefined, IkarusErrorData * error_out);
/// \brief Converts a text value to a string.
/// \param value The text value to convert.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The converted string.
/// \remark Must be freed with #ikarus_free.
/// \remark Undefined if the value is undefined.
IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value);
IKA_API char const * ikarus_text_value_to_string(IkarusTextValue const * value, IkarusErrorData * error_out);
/// \brief Checks if two values are equal.
/// \param lhs The left hand side value.
/// \pre \li Must not be null.
/// \param rhs The right hand side value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the values' data are equal, false otherwise.
IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs);
IKA_API bool ikarus_text_value_is_equal(IkarusTextValue const * lhs, IkarusTextValue const * rhs, IkarusErrorData * error_out);
/// \brief Creates a copy of a text value.
/// \param value The value to copy.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The copied value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value);
IKA_API IkarusTextValue * ikarus_text_value_copy(IkarusTextValue const * value, IkarusErrorData * error_out);
/// \brief Converts a text value to an entity value.
/// \param value The text value to convert.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The converted entity value.
/// \remark This is the same pointer, so freeing it implies freeing the original value.
IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value);
IKA_API struct IkarusValue * ikarus_text_value_to_value(IkarusTextValue * value, IkarusErrorData * error_out);
/// \see ikarus_toggle_value_to_value
IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value);
/// \see ikarus_text_value_to_value
IKA_API struct IkarusValue const * ikarus_text_value_to_value_const(IkarusTextValue const * value, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file toggle_value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
#include <ikarus/stdtypes.h>
@ -14,24 +15,27 @@ IKARUS_BEGIN_HEADER
/// \brief A true/false boolean-esque value. For example "Is Dead".
struct IkarusToggleValue;
/// \brief Creates a toggle.
/// \brief Creates an empty toggle value.
/// \param error_out \see errors.h
/// \return The value or null if an error occurs.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusToggleValue * ikarus_toggle_value_create();
IKA_API IkarusToggleValue * ikarus_toggle_value_create(IkarusErrorData * error_out);
/// \brief Fetches the underlying data of a number value at a specific index.
/// \param value The number value.
/// \pre \li Must not be null.
/// \param idx The index of the data to fetch.
/// \pre \li Must be less than the size of the value.
/// \param error_out \see errors.h
/// \return The underlying data or null if an error occurs or the value is undefined.
IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue * value, size_t idx);
IKA_API bool const * ikarus_toggle_value_get(IkarusToggleValue const * value, size_t idx, IkarusErrorData * error_out);
/// \brief Fetches the size of the underlying data of a toggle value.
/// \param value The toggle value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The size of the underlying data or 0 if an error occurs or the value is undefined.
IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value);
IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value, IkarusErrorData * error_out);
/// \brief Sets the data of a toggle value at a specific index.
/// \param value The toggle value.
@ -39,14 +43,16 @@ IKA_API size_t ikarus_toggle_value_get_size(IkarusToggleValue const * value);
/// \param idx The index of the data to set.
/// \pre \li Must be less than the size of the value.
/// \param new_data The new data.
IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data);
/// \param error_out \see errors.h
IKA_API void ikarus_toggle_value_set(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out);
/// \brief Removes a data from a toggle value.
/// \param value The toggle value.
/// \pre \li Must not be null.
/// \param idx The index of the data to remove.
/// \pre \li Must be less than the size of the value.
IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx);
/// \param error_out \see errors.h
IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx, IkarusErrorData * error_out);
/// \brief Inserts a data into a toggle value.
/// \param value The toggle value.
@ -54,60 +60,68 @@ IKA_API void ikarus_toggle_value_remove(IkarusToggleValue * value, size_t idx);
/// \param idx The index of the data to insert.
/// \pre \li Must be less than or equal to the size of the value.
/// \param new_data The new data.
IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data);
/// \param error_out \see errors.h
IKA_API void ikarus_toggle_value_insert(IkarusToggleValue * value, size_t idx, bool new_data, IkarusErrorData * error_out);
/// \brief Clears a toggle value.
/// \param value The toggle value.
/// \param error_out \see errors.h
/// \remark Noop if the value is undefined.
IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value);
IKA_API void ikarus_toggle_value_clear(IkarusToggleValue * value, IkarusErrorData * error_out);
/// \brief Checks if a toggle value is undefined.
/// \param value The toggle value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the value is undefined, false otherwise.
IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value);
IKA_API bool ikarus_toggle_value_is_undefined(IkarusToggleValue const * value, IkarusErrorData * error_out);
/// \brief Changes a toggle value's undefined state.
/// \param value The toggle value.
/// \pre \li Must not be null.
/// \param undefined The new undefined state.
/// \param error_out \see errors.h
/// \remark Noop if the value is already undefined.
/// \remark If the value is set to undefined, all data will be cleared.
/// \remark If the value is set to not undefined, the value is as if newly created.
IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined);
IKA_API void ikarus_toggle_value_set_undefined(IkarusToggleValue * value, bool undefined, IkarusErrorData * error_out);
/// \brief Converts a toggle value to a string.
/// \param value The toggle value to convert.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The converted string.
/// \remark Must be freed with #ikarus_free.
/// \remark Undefined if the value is undefined.
IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value);
IKA_API char const * ikarus_toggle_value_to_string(IkarusToggleValue const * value, IkarusErrorData * error_out);
/// \brief Checks if two values are equal.
/// \param lhs The left hand side value.
/// \pre \li Must not be null.
/// \param rhs The right hand side value.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the values' data are equal, false otherwise.
IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs);
IKA_API bool ikarus_toggle_value_is_equal(IkarusToggleValue const * lhs, IkarusToggleValue const * rhs, IkarusErrorData * error_out);
/// \brief Creates a copy of a toggle value.
/// \param value The value to copy.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The copied value.
/// \remark Must be freed with #ikarus_free.
IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value);
IKA_API IkarusToggleValue * ikarus_toggle_value_copy(IkarusToggleValue const * value, IkarusErrorData * error_out);
/// \brief Converts a toggle value to an entity value.
/// \param value The toggle value to convert.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return The converted entity value.
/// \remark This is the same pointer, so freeing it implies freeing the original value.
IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value);
IKA_API struct IkarusValue * ikarus_toggle_value_to_value(IkarusToggleValue * value, IkarusErrorData * error_out);
/// \see ikarus_toggle_value_to_value
IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value);
IKA_API struct IkarusValue const * ikarus_toggle_value_to_value_const(IkarusToggleValue const * value, IkarusErrorData * error_out);
IKARUS_END_HEADER

View file

@ -3,6 +3,7 @@
/// \file value.h
/// \author Folling <folling@ikarus.world>
#include <ikarus/errors.h>
#include <ikarus/macros.h>
/// \defgroup values Values
@ -11,7 +12,7 @@
/// These value classes represent plain objects. They are not associated with any entity.
/// Each value may be undefined. \see IkarusProperty
/// Values are stored as lists. If a property is "singular" then its value is a list of size 1.
/// Values are typed, with types existing for each of the corresponding property types.
/// Values are typed, with types existing for each of the corresponding property types. The data of values starts with the index 0.
/// When setting values for a property the type must match the property type and the value must be valid under the
/// property's settings. \see PropertyType
/// @{
@ -28,12 +29,14 @@ struct IkarusValue;
/// \param number_visitor The function to call if the value is a number value. Skipped if null.
/// \param text_visitor The function to call if the value is a text value. Skipped if null.
/// \param data The data passed to the visitor functions.
/// \param error_out \see errors.h
IKA_API void ikarus_value_visit(
IkarusValue * value,
void (*toggle_visitor)(struct IkarusToggleValue *, void *),
void (*number_visitor)(struct IkarusNumberValue *, void *),
void (*text_visitor)(struct IkarusTextValue *, void *),
void * data
void * data,
IkarusErrorData * error_out
);
/// \see ikarus_value_visit
@ -42,7 +45,8 @@ IKA_API void ikarus_value_visit_const(
void (*toggle_visitor)(struct IkarusToggleValue const *, void *),
void (*number_visitor)(struct IkarusNumberValue const *, void *),
void (*text_visitor)(struct IkarusTextValue const *, void *),
void * data
void * data,
IkarusErrorData * error_out
);
IKARUS_END_HEADER

View file

@ -1,22 +1,71 @@
#include "ikarus/errors.h"
#include "cppbase/functional.hpp"
#include <string.h>
#include <ranges>
#include <fmt/format.h>
char const * get_error_info_name(IkarusErrorInfo info) {
switch (info) {
case IkarusErrorInfo_Source_None: return "IkarusErrorInfo_Source_None";
case IkarusErrorInfo_Source_Client: return "IkarusErrorInfo_Source_Client";
case IkarusErrorInfo_Source_SubSystem: return "IkarusErrorInfo_Source_SubSystem";
case IkarusErrorInfo_Source_LibIkarus: return "IkarusErrorInfo_Source_LibIkarus";
case IkarusErrorInfo_Source_Unknown: return "IkarusErrorInfo_Source_Unknown";
case IkarusErrorInfo_Type_None: return "IkarusErrorInfo_Type_None";
case IkarusErrorInfo_Type_Client_Misuse: return "IkarusErrorInfo_Type_Client_Misuse";
case IkarusErrorInfo_Type_Client_Input: return "IkarusErrorInfo_Type_Client_Input";
case IkarusErrorInfo_Type_SubSystem_Dependency: return "IkarusErrorInfo_Type_SubSystem_Dependency";
case IkarusErrorInfo_Type_SubSystem_Database: return "IkarusErrorInfo_Type_SubSystem_Database";
case IkarusErrorInfo_Type_SubSystem_Filesystem: return "IkarusErrorInfo_Type_SubSystem_Filesystem";
case IkarusErrorInfo_Type_LibIkarus_InvalidState: return "IkarusErrorInfo_Type_LibIkarus_InvalidState";
case IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation: return "IkarusErrorInfo_Type_LibIkarus_CannotPerformOperation";
case IkarusErrorInfo_Type_LibIkarus_Timeout: return "IkarusErrorInfo_Type_LibIkarus_Timeout";
case IkarusErrorInfo_Type_Unknown: return "IkarusErrorInfo_Type_Unknown";
default: return "Unknown";
case IkarusErrorInfo_None: return "None";
case IkarusErrorInfo_Client: return "Client";
case IkarusErrorInfo_Dependency: return "Dependency";
case IkarusErrorInfo_Filesystem: return "Filesystem";
case IkarusErrorInfo_Database: return "Database";
case IkarusErrorInfo_OS: return "OS";
case IkarusErrorInfo_LibIkarus: return "libikarus";
case IkarusErrorInfo_Client_Misuse: return "Misuse";
case IkarusErrorInfo_Client_InvalidInput: return "InvalidInput";
case IkarusErrorInfo_Client_InvalidFormat: return "InvalidFormat";
case IkarusErrorInfo_Client_ConstraintViolated: return "ConstraintViolated";
case IkarusErrorInfo_Filesystem_NotFound: return "NotFound";
case IkarusErrorInfo_Filesystem_AlreadyExists: return "AlreadyExists";
case IkarusErrorInfo_Filesystem_MissingPermissions: return "MissingPermissions";
case IkarusErrorInfo_Filesystem_InsufficientSpace: return "InsufficientSpace";
case IkarusErrorInfo_Filesystem_InvalidPath: return "InvalidPath";
case IkarusErrorInfo_Database_ConnectionFailed: return "ConnectionFailed";
case IkarusErrorInfo_Database_QueryFailed: return "QueryFailed";
case IkarusErrorInfo_Database_MigrationFailed: return "MigrationFailed";
case IkarusErrorInfo_Database_InvalidState: return "InvalidState";
case IkarusErrorInfo_OS_SystemCallFailed: return "SystemCallFailed";
case IkarusErrorInfo_OS_InvalidReturnValue: return "InvalidReturnValue";
case IkarusErrorInfo_OS_InsufficientMemory: return "InsufficientMemory";
case IkarusErrorInfo_LibIkarus_InvalidState: return "InvalidState";
case IkarusErrorInfo_LibIkarus_Timeout: return "Timeout";
default: return "Invalid";
}
}
bool ikarus_error_data_is_success(IkarusErrorData const * data) {
return data->infos[0] == IkarusErrorInfo_None;
}
bool ikarus_error_data_is_error(IkarusErrorData const * data) {
return data->infos[0] != IkarusErrorInfo_None;
}
char const * ikarus_error_data_pretty_format(IkarusErrorData const * data) {
if (ikarus_error_data_is_success(data)) {
return "Success";
}
auto const formatted = fmt::format(
"{} - {}",
fmt::join(
data->infos | std::views::take_while(cppbase::pred_ne(IkarusErrorInfo_None)) | std::views::transform(get_error_info_name),
"->"
),
data->message
);
return strndup(formatted.data(), formatted.size());
}

View file

@ -1,7 +1,6 @@
#include "ikarus/objects/blueprint.h"
#include "objects/blueprint.hpp"
#include "util.hpp"
#include <cppbase/logger.hpp>
#include <cppbase/result.hpp>
@ -9,25 +8,44 @@
#include <objects/entity.hpp>
#include <objects/properties/property.hpp>
#include <objects/util.hpp>
#include <persistence/function_context.hpp>
#include <persistence/project.hpp>
IkarusBlueprint::IkarusBlueprint(IkarusProject * project, IkarusId id):
IkarusObject{project, id} {}
IkarusBlueprint * ikarus_blueprint_create(struct IkarusProject * project, char const * name) {
return ikarus::util::insert_object(
project,
IkarusObjectType_Blueprint,
name,
[](auto * db, IkarusId id) { return db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id); },
[project](IkarusId id) { return project->get_blueprint(id); }
).unwrap_value_or(nullptr);
if (cppbase::is_empty_or_blank(name)) {
project->set_error("blueprint name must not be empty", true, IkarusErrorInfo_Source_Client, IkarusErrorInfo_Type_Client_Input);
return nullptr;
}
project->db
->transact([name](auto * db) {
TRY(db->execute("INSERT INTO `objects`(`type`, `name`) VALUES(?, ?, ?)", IkarusObjectType_Blueprint, name));
auto id = ikarus_id_from_data_and_type(db->last_insert_rowid(), IkarusObjectType_Blueprint);
TRY(db->execute("INSERT INTO `blueprints`(`id`) VALUES(?)", id));
return cppbase::ok();
})
.on_error([project](auto const & err) {
project->set_error(
fmt::format("failed to create blueprint: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
});
}
void ikarus_blueprint_delete(IkarusBlueprint * blueprint) {
ikarus::util::delete_object(blueprint);
blueprint->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", blueprint->id).on_error([blueprint](auto const & err) {
blueprint->project->set_error(
fmt::format("failed to delete blueprint from objects table: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
});
}
void ikarus_blueprint_get_properties(
@ -35,41 +53,34 @@ void ikarus_blueprint_get_properties(
struct IkarusProperty ** properties_out,
size_t properties_out_size
) {
ikarus::util::fetch_multiple_buffered<IkarusId>(
blueprint,
ikarus::util::MultipleBufferQueryData{
.table_name = "properties",
.select_field_name = "id",
.where_field_name = "blueprint",
.relation_desc = "properties"
},
properties_out,
properties_out_size,
[&](IkarusProject * project, IkarusFunctionContext * ctx, IkarusId id) -> cppbase::Result<IkarusProperty *, sqlitecpp::QueryError> {
VTRY(auto const type, IkarusProperty::get_property_type(blueprint->project, id).on_error([ctx, id](auto const & err) {
ctx->set_error(
fmt::format("failed to fetch property {}'s type: {}", id, err),
IkarusId ids[properties_out_size];
TRYRV(
,
blueprint->project->db
->query_many_buffered<IkarusId>("SELECT `id` FROM `properties` WHERE `source` = ?", ids, properties_out_size, blueprint->id)
.on_error([&](auto const & err) {
blueprint->project->set_error(
fmt::format("failed to fetch blueprint properties from database: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
}));
return cppbase::ok(project->get_property(id, type));
}
})
);
// not atomic, could be switched to two loops if necessary
for (size_t i = 0; i < properties_out_size; ++i) {
IkarusId id = ids[i];
VTRYRV(auto const type, , IkarusProperty::get_property_type(blueprint->project, id));
properties_out[i] = blueprint->project->get_property(id, type);
}
}
size_t ikarus_blueprint_get_property_count(IkarusBlueprint const * blueprint) {
return ikarus::util::fetch_count(
blueprint,
ikarus::util::CountQueryData{
.table_name = "blueprint_properties",
.select_field_name = "property",
.where_field_name = "blueprint",
.relation_desc = "properties"
}
)
return blueprint->project->db->query_one<int64_t>("SELECT COUNT(*) FROM `properties` WHERE `source` = ?", blueprint->id)
.unwrap_value_or(0);
}
@ -78,31 +89,34 @@ void ikarus_blueprint_get_linked_entities(
struct IkarusEntity ** entities_out,
size_t entities_out_size
) {
ikarus::util::fetch_multiple_buffered<IkarusId>(
blueprint,
ikarus::util::MultipleBufferQueryData{
.table_name = "entity_blueprint_links",
.select_field_name = "entity",
.where_field_name = "blueprint",
.relation_desc = "linked entities"
},
entities_out,
IkarusId ids[entities_out_size];
TRYRV(
,
blueprint->project->db
->query_many_buffered<IkarusId>(
"SELECT `entity` FROM `entity_blueprint_links` WHERE `blueprint` = ?",
ids,
entities_out_size,
[&](IkarusProject * project, [[maybe_unused]] IkarusFunctionContext * ctx, IkarusId id
) -> cppbase::Result<IkarusEntity *, sqlitecpp::QueryError> { return cppbase::ok(project->get_entity(id)); }
blueprint->id
)
.on_error([&](auto const & err) {
blueprint->project->set_error(
fmt::format("failed to fetch linked entities from database: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
})
);
for (size_t i = 0; i < entities_out_size; ++i) {
entities_out[i] = blueprint->project->get_entity(ids[i]);
}
}
size_t ikarus_blueprint_get_linked_entity_count(IkarusBlueprint const * blueprint) {
return ikarus::util::fetch_count(
blueprint,
ikarus::util::CountQueryData{
.table_name = "entity_blueprint_links",
.select_field_name = "entity",
.where_field_name = "blueprint",
.relation_desc = "linked entities"
}
)
return blueprint->project->db->query_one<int64_t>("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `blueprint` = ?", blueprint->id)
.unwrap_value_or(0);
}

View file

@ -4,7 +4,6 @@
#include <objects/blueprint.hpp>
#include <objects/util.hpp>
#include <persistence/function_context.hpp>
#include <persistence/project.hpp>
IkarusEntity * ikarus_entity_create(struct IkarusProject * project, char const * name) {

View file

@ -5,7 +5,6 @@
#include <ikarus/objects/properties/property_type.h>
#include <objects/properties/property_source.hpp>
#include <persistence/function_context.hpp>
#include <persistence/project.hpp>
#include <sys/stat.h>
#include <values/value.hpp>
@ -23,12 +22,10 @@ IkarusProperty::Data const & IkarusProperty::get_data() const {
}
cppbase::Result<IkarusPropertyType, sqlitecpp::SingleQueryError> IkarusProperty::get_property_type(IkarusProject * project, IkarusId id) {
auto * ctx = project->get_function_context();
VTRY(
auto const type,
project->get_db()->query_one<int>("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([ctx](auto const & err) {
ctx->set_error(
project->db->query_one<int>("SELECT `type` FROM `properties` WHERE `id` = ?", id).on_error([project](auto const & err) {
project->set_error(
fmt::format("failed to fetch unboxed property type: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,
@ -41,16 +38,17 @@ cppbase::Result<IkarusPropertyType, sqlitecpp::SingleQueryError> IkarusProperty:
}
IKA_API void ikarus_property_delete(IkarusProperty * property) {
auto * ctx = property->project->get_function_context();
TRYRV(, property->project->get_db()->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([ctx](auto const & err) {
ctx->set_error(
TRYRV(
,
property->project->db->execute("DELETE FROM `objects` WHERE `id` = ?", property->id).on_error([property](auto const & err) {
property->project->set_error(
fmt::format("failed to delete property from objects table: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
}));
})
);
property->project->uncache(property);
}
@ -60,15 +58,13 @@ IkarusPropertyType ikarus_property_get_type(IkarusProperty const * property) {
}
IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * property) {
auto * ctx = property->project->get_function_context();
VTRYRV(
auto const source,
nullptr,
property->project->get_db()
property->project->db
->query_one<int>("SELECT `source` FROM `properties` WHERE `id` = ?", property->id)
.on_error([ctx](auto const & err) {
ctx->set_error(
.on_error([property](auto const & err) {
property->project->set_error(
fmt::format("failed to fetch property's source: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,
@ -81,7 +77,7 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p
case IkarusObjectType_Blueprint: return new IkarusPropertySource{property->project->get_blueprint(source)};
case IkarusObjectType_Entity: return new IkarusPropertySource{property->project->get_entity(source)};
default: {
ctx->set_error(
property->project->set_error(
fmt::format("PropertySource is neither blueprint nor entity"),
true,
IkarusErrorInfo_Source_LibIkarus,
@ -94,15 +90,13 @@ IkarusPropertySource const * ikarus_property_get_source(IkarusProperty const * p
}
IkarusValue * ikarus_property_get_default_value(IkarusProperty const * property) {
auto * ctx = property->project->get_function_context();
VTRYRV(
auto const value,
nullptr,
property->project->get_db()
property->project->db
->query_one<int>("SELECT `default_value` FROM `properties` WHERE `id` = ?", property->id)
.on_error([ctx](auto const & err) {
ctx->set_error(
.on_error([property](auto const & err) {
property->project->set_error(
fmt::format("failed to fetch property's default value: {}", err),
true,
IkarusErrorInfo_Source_SubSystem,

View file

@ -1,241 +0,0 @@
#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);
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> {
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);
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
);
})
);
return cppbase::ok(object_factory(id));
}
template<typename Object>
requires std::derived_from<Object, IkarusObject>
void delete_object(Object * object) {
auto object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id));
auto * ctx = object->project->get_function_context();
TRYRV(, object->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
);
}));
object->project->uncache(object);
}
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));
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));
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
);
})
);
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));
auto * ctx = object->project->get_function_context();
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
);
})
);
return cppbase::ok(static_cast<size_t>(count));
}
template<typename T>
struct ExistsQueryData {
std::string_view table_name;
std::string_view where_field_name;
T where_field_value;
std::string_view relation_desc;
};
template<typename Object, typename T>
requires std::derived_from<Object, IkarusObject>
cppbase::Result<bool, sqlitecpp::QueryError> check_exists(Object const * object, ExistsQueryData<T> const & query_data) {
auto * object_type_str = ikarus_object_type_to_string(ikarus_id_get_object_type(object->id));
auto * ctx = object->project->get_function_context();
VTRY(
auto exists,
object->project->get_db()
->template query_one<int>(
fmt::format("SELECT EXISTS(SELECT 1 FROM `{}` WHERE `{}` = ?);", query_data.table_name, query_data.where_field_name),
query_data.where_field_value
)
.on_error([&](auto const & err) {
ctx->set_error(
fmt::format("failed to check whether {} {} exists: {}", object_type_str, query_data.relation_desc, err),
true,
IkarusErrorInfo_Source_SubSystem,
IkarusErrorInfo_Type_SubSystem_Database
);
})
);
return cppbase::ok(static_cast<bool>(exists));
}
} // namespace ikarus::util

View file

@ -1,18 +0,0 @@
#include "function_context.hpp"
IkarusFunctionContext::IkarusFunctionContext(IkarusProject * project):
_project{project} {}
IkarusFunctionContext::~IkarusFunctionContext() {
if (_project->_function_contexts.size() == 1) {
if (_project->error_message_buffer.empty()) {
_project->error_message_buffer.push_back('\0');
} else {
_project->error_message_buffer[0] = '\0';
}
_project->error_infos = {};
}
_project->_function_contexts.pop_back();
}

View file

@ -1,49 +0,0 @@
#pragma once
#include <ranges>
#include <string_view>
#include <type_traits>
#include <fmt/format.h>
#include <cppbase/logger.hpp>
#include <ikarus/errors.h>
#include <persistence/project.hpp>
struct IkarusFunctionContext {
public:
explicit IkarusFunctionContext(struct IkarusProject * project);
IkarusFunctionContext(IkarusFunctionContext const &) noexcept = default;
IkarusFunctionContext(IkarusFunctionContext &&) noexcept = default;
auto operator=(IkarusFunctionContext const &) noexcept -> IkarusFunctionContext & = default;
auto operator=(IkarusFunctionContext &&) noexcept -> IkarusFunctionContext & = default;
~IkarusFunctionContext();
public:
template<typename... Infos>
requires(std::is_same_v<IkarusErrorInfo, Infos> && ...) && (sizeof...(Infos) <= MAXIMUM_ERROR_INFOS)
auto set_error(std::string_view error_message, bool log_error, Infos... infos) -> void {
if (error_message.size() > _project->error_message_buffer.size()) {
_project->error_message_buffer.resize(error_message.size() + 1);
}
for (int i = 0; i < error_message.size(); ++i) {
_project->error_message_buffer[i] = error_message[i];
}
_project->error_message_buffer[error_message.size()] = '\0';
_project->error_infos = {infos...};
if (log_error) {
LOG_ERROR("Error({}): {}", fmt::join(_project->error_infos | std::views::transform(get_error_info_name), ", "), error_message);
}
}
private:
struct IkarusProject * _project;
};

View file

@ -8,27 +8,14 @@
#include <objects/properties/property.hpp>
#include <objects/properties/text_property.hpp>
#include <objects/properties/toggle_property.hpp>
#include <persistence/function_context.hpp>
auto IkarusProject::get_name() const -> std::string_view {
return _name;
}
auto IkarusProject::get_path() const -> std::filesystem::path const & {
return _path;
}
auto IkarusProject::get_db() -> sqlitecpp::Connection * {
return _db.get();
}
auto IkarusProject::get_db() const -> sqlitecpp::Connection const * {
return _db.get();
}
auto IkarusProject::get_function_context() -> IkarusFunctionContext * {
return &_function_contexts.emplace_back(this);
}
IkarusProject::IkarusProject(std::string_view name, std::filesystem::path path):
name{name},
path{path},
db{nullptr},
_blueprints{},
_properties{},
_entities{} {}
IkarusBlueprint * IkarusProject::get_blueprint(IkarusId id) {
return get_cached_object<IkarusBlueprint>(id, this->_blueprints);

View file

@ -11,20 +11,10 @@
#include <ikarus/id.h>
#include <ikarus/objects/properties/property_type.h>
constexpr inline auto MAXIMUM_ERROR_INFOS = 8;
/// \private
struct IkarusProject {
public:
[[nodiscard]] auto get_name() const -> std::string_view;
[[nodiscard]] auto get_path() const -> std::filesystem::path const &;
[[nodiscard]] auto get_db() -> sqlitecpp::Connection *;
[[nodiscard]] auto get_db() const -> sqlitecpp::Connection const *;
public:
[[nodiscard]] auto get_function_context() -> struct IkarusFunctionContext *;
IkarusProject(std::string_view name, std::filesystem::path path);
public:
[[nodiscard]] auto get_blueprint(IkarusId id) -> struct IkarusBlueprint *;
@ -53,19 +43,13 @@ private:
cache.erase(object->id);
}
public:
std::string name;
std::filesystem::path path;
std::unique_ptr<sqlitecpp::Connection> db;
private:
friend struct IkarusFunctionContext;
std::string _name;
std::filesystem::path _path;
std::unique_ptr<sqlitecpp::Connection> _db;
std::array<IkarusErrorInfo, MAXIMUM_ERROR_INFOS> error_infos;
std::string error_message_buffer;
std::unordered_map<IkarusId, std::unique_ptr<IkarusBlueprint>> _blueprints;
std::unordered_map<IkarusId, std::unique_ptr<IkarusProperty>> _properties;
std::unordered_map<IkarusId, std::unique_ptr<IkarusEntity>> _entities;
std::vector<IkarusFunctionContext> _function_contexts;
std::unordered_map<IkarusId, std::unique_ptr<struct IkarusBlueprint>> _blueprints;
std::unordered_map<IkarusId, std::unique_ptr<struct IkarusProperty>> _properties;
std::unordered_map<IkarusId, std::unique_ptr<struct IkarusEntity>> _entities;
};