update dependencies

This commit is contained in:
Folling 2025-01-02 09:39:58 +01:00
parent e6233cf3f2
commit ef27673846
No known key found for this signature in database
21 changed files with 800 additions and 370 deletions

View file

@ -66,7 +66,7 @@ BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon BreakInheritanceList: AfterColon
BreakStringLiterals: false BreakStringLiterals: false
ColumnLimit: 80 ColumnLimit: 120
CommentPragmas: '^\\.+' CommentPragmas: '^\\.+'
CompactNamespaces: false CompactNamespaces: false
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
@ -148,6 +148,7 @@ PPIndentWidth: -1
PackConstructorInitializers: Never PackConstructorInitializers: Never
PointerAlignment: Middle PointerAlignment: Middle
QualifierAlignment: Right QualifierAlignment: Right
# QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ] # QualifierOrder: [ 'friend', 'constexpr', 'inline', 'static', 'type', 'const', 'volatile' ]
ReferenceAlignment: Middle ReferenceAlignment: Middle

View file

@ -31,24 +31,27 @@ enum IkarusErrorInfo {
/// \brief The client provided a non-existent resource. /// \brief The client provided a non-existent resource.
/// Example: Passing an entity to a function after it has been deleted. /// Example: Passing an entity to a function after it has been deleted.
IkarusErrorInfo_Client_NonExistent = 0x01000003, IkarusErrorInfo_Client_NonExistent = 0x01000003,
/// \brief The client provided a resource which exists but is not linked to the current context.
/// Example: Passing a property that isn't linked to the current entity.
IkarusErrorInfo_Client_NotLinked = 0x01000004,
/// \brief The client provided an index that was out of bounds for some array. /// \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. /// Example: Passing the index 3 for an `IkarusToggleValue` with size 3.
IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000004, IkarusErrorInfo_Client_IndexOutOfBounds = 0x01000005,
/// \brief The client provided a numeric value that was out of bounds /// \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). /// Example: Passing the value 2^32 to an i32 (might be passed as a string).
IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000005, IkarusErrorInfo_Client_ValueOutOfBounds = 0x01000006,
/// \brief The client provided invalid input that doesn't fit in any of the other categories. /// \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 /// Example: Passing an empty/blank string for a string that must be
/// non-empty/-blank. /// non-empty/-blank.
IkarusErrorInfo_Client_InvalidInput = 0x01000006, IkarusErrorInfo_Client_InvalidInput = 0x01000007,
/// \brief The client provided valid data in an invalid format. /// \brief The client provided valid data in an invalid format.
/// Example: Passing a malformed JSON string. /// Example: Passing a malformed JSON string.
IkarusErrorInfo_Client_InvalidFormat = 0x01000007, IkarusErrorInfo_Client_InvalidFormat = 0x01000008,
/// \brief The client violated a constraint. /// \brief The client violated a constraint.
/// \details This error is most likely caused by clients. /// \details This error is most likely caused by clients.
/// Example: A user tries to set the age of a character to a value outside /// Example: A user tries to set the age of a character to a value outside
/// its specified range. /// its specified range.
IkarusErrorInfo_Client_ConstraintViolated = 0x10000008, IkarusErrorInfo_Client_ConstraintViolated = 0x10000009,
// 0x02 reserved for dependency errors // 0x02 reserved for dependency errors

View file

@ -20,6 +20,13 @@ IKARUS_BEGIN_HEADER
/// in all linked entities. /// in all linked entities.
struct IkarusBlueprint; struct IkarusBlueprint;
/// \brief Checks whether a blueprint exists.
/// \param blueprint The blueprint to check.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the blueprint exists, false otherwise or if an error occurs.
IKA_API bool ikarus_blueprint_exists(IkarusBlueprint * blueprint, IkarusErrorData * error_out);
/// \brief Flags for creating a blueprint. /// \brief Flags for creating a blueprint.
enum IkarusBlueprintCreateFlags { enum IkarusBlueprintCreateFlags {
/// \brief No flags. /// \brief No flags.
@ -48,14 +55,10 @@ IKA_API IkarusBlueprint * ikarus_blueprint_create(
enum IkarusBlueprintCreateFromEntityFlags { enum IkarusBlueprintCreateFromEntityFlags {
/// \brief No flags. /// \brief No flags.
IkarusBlueprintCreateFromEntityFlags_None = 0, IkarusBlueprintCreateFromEntityFlags_None = 0,
/// \brief The default values of the properties will be set to the values of the source entity.
IkarusBlueprintCreateFromEntityFlags_AdoptDefaultValues = 1 << 0,
/// \brief The entity will be linked to the blueprint, and all values will be turned into properties.
IkarusBlueprintCreateFromEntityFlags_LinkEntity = 1 << 1,
}; };
/// \brief Creates a new blueprint from an entity. /// \brief Creates a new blueprint from an entity.
/// \details Each value of the entity will be copied into the blueprint as a property. /// \details Each value of the entity will be copied into the blueprint as a blueprint.
/// \param entity The entity to create the blueprint from. /// \param entity The entity to create the blueprint from.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
@ -117,10 +120,8 @@ IKA_API void ikarus_blueprint_delete(
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The project the blueprint belongs to. /// \return The project the blueprint belongs to.
/// \remark Ownership remains with libikarus. /// \remark Ownership remains with libikarus.
IKA_API struct IkarusProject * ikarus_blueprint_get_project( IKA_API struct IkarusProject *
struct IkarusBlueprint * blueprint, ikarus_blueprint_get_project(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out);
struct IkarusErrorData * error_out
);
/// \brief Gets the name of a blueprint. /// \brief Gets the name of a blueprint.
/// \param blueprint The blueprint to get the name of. /// \param blueprint The blueprint to get the name of.
@ -129,10 +130,7 @@ IKA_API struct IkarusProject * ikarus_blueprint_get_project(
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The name of the blueprint. /// \return The name of the blueprint.
/// \remark Ownership remains with libikarus. /// \remark Ownership remains with libikarus.
IKA_API char const * ikarus_blueprint_get_name( IKA_API char const * ikarus_blueprint_get_name(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out);
struct IkarusBlueprint * blueprint,
struct IkarusErrorData * error_out
);
/// \brief Flags for setting the name of a blueprint. /// \brief Flags for setting the name of a blueprint.
enum IkarusBlueprintSetNameFlags { enum IkarusBlueprintSetNameFlags {
@ -163,7 +161,7 @@ IKA_API void ikarus_blueprint_set_name(
/// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs. /// \param size_out An out parameter for the number of items in the returned array or undefined if an error occurs.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The properties of the blueprint or null if an error occurs. /// \return The properties of the blueprint or null if an error occurs.
IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties( IKA_API struct IkarusBlueprint ** ikarus_blueprint_get_properties(
struct IkarusBlueprint * blueprint, struct IkarusBlueprint * blueprint,
size_t * size_out, size_t * size_out,
struct IkarusErrorData * error_out struct IkarusErrorData * error_out
@ -175,10 +173,8 @@ IKA_API struct IkarusProperty ** ikarus_blueprint_get_properties(
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The number of properties of the blueprint or 0 if an error occurs. /// \return The number of properties of the blueprint or 0 if an error occurs.
IKA_API size_t ikarus_blueprint_get_properties_count( IKA_API size_t
struct IkarusBlueprint * blueprint, ikarus_blueprint_get_properties_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out);
struct IkarusErrorData * error_out
);
/// \brief Gets all entities linked to a blueprint. /// \brief Gets all entities linked to a blueprint.
/// \param blueprint The blueprint to get the entities of. /// \param blueprint The blueprint to get the entities of.
@ -200,10 +196,8 @@ IKA_API struct IkarusEntity ** ikarus_blueprint_get_entities(
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The number of entities linked to the blueprint or 0 if an error occurs. /// \return The number of entities linked to the blueprint or 0 if an error occurs.
IKA_API size_t ikarus_blueprint_get_entities_count( IKA_API size_t
struct IkarusBlueprint * blueprint, ikarus_blueprint_get_entities_count(struct IkarusBlueprint * blueprint, struct IkarusErrorData * error_out);
struct IkarusErrorData * error_out
);
IKARUS_END_HEADER IKARUS_END_HEADER

View file

@ -33,6 +33,14 @@ enum IkarusEntityCreateFlags {
IkarusEntityCreateFlags_None = 0, IkarusEntityCreateFlags_None = 0,
}; };
/// \brief Checks whether an entity exists.
/// \param entity The entity to check.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the entity exists, false otherwise or if an error occurs.
IKA_API bool
ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out);
/// \brief Creates a new entity. /// \brief Creates a new entity.
/// \param project The project to create the entity in. /// \param project The project to create the entity in.
/// \pre \li Must not be null. /// \pre \li Must not be null.
@ -127,6 +135,22 @@ IKA_API void ikarus_entity_set_name(
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Gets whether an entity is linked to a blueprint.
/// \param entity The entity to check the blueprint of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param blueprint The blueprint to check the entity's link to.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \pre \li Must be in the same project as the entity.
/// \param error_out \see errors.h
/// \return True if the entity is linked to the blueprint, false otherwise or if an error occurs.
IKA_API bool ikarus_entity_is_linked_to_blueprint(
IkarusEntity * entity,
struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out
);
/// \brief Gets the blueprints an entity is linked to. /// \brief Gets the blueprints an entity is linked to.
/// \param entity The entity to get the blueprints of. /// \param entity The entity to get the blueprints of.
/// \pre \li Must not be null. /// \pre \li Must not be null.
@ -191,6 +215,7 @@ enum IkarusEntityUnlinkBlueprintFlags {
/// \param blueprint The blueprint to unlink from. /// \param blueprint The blueprint to unlink from.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \pre \li Must exist. /// \pre \li Must exist.
/// \pre \li Must be in the same project as the entity.
/// \remark If the entity is not linked to the blueprint, nothing happens. /// \remark If the entity is not linked to the blueprint, nothing happens.
/// \param flags Flags for unlinking the entity from the blueprint. /// \param flags Flags for unlinking the entity from the blueprint.
/// \param error_out \see errors.h /// \param error_out \see errors.h
@ -201,6 +226,20 @@ IKA_API void ikarus_entity_unlink_blueprint(
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Gets whether an entity has a value.
/// \param entity The entity to check the value of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param name The value's name.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the entity has a value with the name, false otherwise or if an error occurs.
IKA_API bool ikarus_entity_has_value(
IkarusEntity * entity,
char const * name,
IkarusErrorData * error_out
);
/// \brief Struct for an entity value. /// \brief Struct for an entity value.
struct IkarusEntityValue { struct IkarusEntityValue {
/// \brief The name of the value. /// \brief The name of the value.
@ -229,7 +268,7 @@ IKA_API IkarusEntityValue * ikarus_entity_get_values(
/// \pre \li Must exist. /// \pre \li Must exist.
/// \param name The value's name. /// \param name The value's name.
/// \pre \li Must not be null. /// \pre \li Must not be null.
/// \remark Ownership remains with the client. /// \pre \li Must exist.
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The value, in json format of or null if an error occurs. \see value.h /// \return The value, in json format of or null if an error occurs. \see value.h
IKA_API char const * ikarus_entity_get_value( IKA_API char const * ikarus_entity_get_value(
@ -287,6 +326,22 @@ IKA_API void ikarus_entity_delete_value(
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Gets whether an entity has a property value.
/// \param entity The entity to check the property value of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param property The property to check the value of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \pre \li Must be in the same project as the entity.
/// \param error_out \see errors.h
/// \return True if the entity has a value for the property, false otherwise or if an error occurs.
IKA_API bool ikarus_entity_has_property_value(
IkarusEntity * entity,
struct IkarusProperty * property,
IkarusErrorData * error_out
);
/// \brief Struct for an entity property value. /// \brief Struct for an entity property value.
struct IkarusEntityPropertyValue { struct IkarusEntityPropertyValue {
/// \brief The property. /// \brief The property.
@ -355,6 +410,29 @@ IKA_API void ikarus_entity_set_property_value(
IkarusErrorData * error_out IkarusErrorData * error_out
); );
/// \brief Flags for clearing the value of a property of an entity.
enum IkarusEntityClearPropertyValueFlags {
/// \brief No flags.
IkarusEntityClearPropertyValueFlags_None = 0,
};
/// \brief Clears the value of a property of an entity.
/// \param entity The entity to clear the value of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \param property The property to clear the value of.
/// \pre \li Must not be null.
/// \pre \li Must exist.
/// \pre \li Must be linked to the entity.
/// \param flags Flags for clearing the property value.
/// \param error_out \see errors.h
IKA_API void ikarus_entity_clear_property_value(
IkarusEntity * entity,
struct IkarusProperty * property,
IkarusEntitySetPropertyValueFlags flags,
IkarusErrorData * error_out
);
IKARUS_END_HEADER IKARUS_END_HEADER
/// @} /// @}

View file

@ -36,6 +36,13 @@ IKARUS_BEGIN_HEADER
/// property's default value if none is specified. /// property's default value if none is specified.
struct IkarusProperty; struct IkarusProperty;
/// \brief Checks whether a property exists.
/// \param property The property to check.
/// \pre \li Must not be null.
/// \param error_out \see errors.h
/// \return True if the property exists, false otherwise or if an error occurs.
IKA_API bool ikarus_property_exists(IkarusProperty * property, IkarusErrorData * error_out);
/// \brief Flags for creating a property. /// \brief Flags for creating a property.
enum IkarusPropertyCreateFlags { enum IkarusPropertyCreateFlags {
/// \brief No flags. /// \brief No flags.
@ -73,11 +80,8 @@ enum IkarusPropertyDeleteFlags {
/// \param property The property to delete. /// \param property The property to delete.
/// \param flags Flags for deleting the property. /// \param flags Flags for deleting the property.
/// \param error_out \see errors.h /// \param error_out \see errors.h
IKA_API void ikarus_property_delete( IKA_API void
IkarusProperty * property, ikarus_property_delete(IkarusProperty * property, IkarusPropertyDeleteFlags flags, IkarusErrorData * error_out);
IkarusPropertyDeleteFlags flags,
IkarusErrorData * error_out
);
/// \brief Get the project a property belongs to. /// \brief Get the project a property belongs to.
/// \param property The property to get the project of. /// \param property The property to get the project of.
@ -86,10 +90,7 @@ IKA_API void ikarus_property_delete(
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The project the property belongs to or null if an error occurred. /// \return The project the property belongs to or null if an error occurred.
/// \remark Ownership remains with libikarus. /// \remark Ownership remains with libikarus.
IKA_API struct IkarusProject * ikarus_property_get_project( IKA_API struct IkarusProject * ikarus_property_get_project(IkarusProperty * property, IkarusErrorData * error_out);
IkarusProperty * property,
IkarusErrorData * error_out
);
/// \brief Get the name of a property. /// \brief Get the name of a property.
/// \param property The property to get the name of. /// \param property The property to get the name of.
@ -98,10 +99,7 @@ IKA_API struct IkarusProject * ikarus_property_get_project(
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The name of the property or null if an error occurred. /// \return The name of the property or null if an error occurred.
/// \remark Ownership remains with libikarus. /// \remark Ownership remains with libikarus.
IKA_API char const * ikarus_property_get_name( IKA_API char const * ikarus_property_get_name(IkarusProperty * property, IkarusErrorData * error_out);
IkarusProperty * property,
IkarusErrorData * error_out
);
/// \brief Get the schema of a property. /// \brief Get the schema of a property.
/// \param property The property to get the schema of. /// \param property The property to get the schema of.
@ -110,10 +108,7 @@ IKA_API char const * ikarus_property_get_name(
/// \param error_out \see errors.h /// \param error_out \see errors.h
/// \return The schema of the property or null if an error occurred. /// \return The schema of the property or null if an error occurred.
/// \remark Ownership remains with libikarus. /// \remark Ownership remains with libikarus.
IKA_API struct IkarusValueSchema * ikarus_property_get_schema( IKA_API struct IkarusValueSchema * ikarus_property_get_schema(IkarusProperty * property, IkarusErrorData * error_out);
IkarusProperty * property,
IkarusErrorData * error_out
);
/// \brief Flags for setting the name of a property. /// \brief Flags for setting the name of a property.
enum IkarusPropertySetNameFlags { enum IkarusPropertySetNameFlags {

View file

@ -64,9 +64,9 @@ char const * ikarus_get_error_info_name(IkarusErrorInfo info) {
} }
bool ikarus_error_data_is_success(IkarusErrorData const * data) { bool ikarus_error_data_is_success(IkarusErrorData const * data) {
return data->info == IkarusErrorInfo_None; return data && data->info == IkarusErrorInfo_None;
} }
bool ikarus_error_data_is_error(IkarusErrorData const * data) { bool ikarus_error_data_is_error(IkarusErrorData const * data) {
return data->info != IkarusErrorInfo_None; return data && data->info != IkarusErrorInfo_None;
} }

View file

@ -3,24 +3,33 @@
#include <string_view> #include <string_view>
#include <type_traits> #include <type_traits>
#include <cppbase/result.hpp>
#include <cppbase/strings.hpp>
#include <cppbase/templates.hpp>
#include <ikarus/errors.h> #include <ikarus/errors.h>
void safe_strcpy( void safe_strcpy(std::string_view const src, char * dest, size_t const dest_size);
std::string_view const src,
char * dest, template<>
size_t const dest_size struct fmt::formatter<IkarusErrorData> : formatter<string_view> {
); constexpr static auto format([[maybe_unused]] IkarusErrorData const & error, fmt::format_context & ctx) {
return fmt::format_to(
ctx.out(),
"ERROR {}({}): {}",
ikarus_error_info_get_name(error.info),
static_cast<std::underlying_type_t<IkarusErrorInfo>>(error.info),
error.message
);
}
};
#define IKARUS_VOID_RETURN #define IKARUS_VOID_RETURN
#define IKARUS_SET_ERROR(msg, err_info) \ #define IKARUS_SET_ERROR(msg, err_info) \
if (error_out != nullptr) { \ if (error_out != nullptr) { \
safe_strcpy( \ safe_strcpy(msg, static_cast<char *>(error_out->message), IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT); \
msg, \ error_out->info = err_info; \
static_cast<char *>(error_out->message), \
IKARUS_ERROR_DATA_MAX_MESSAGE_LIMIT \
); \
error_out->info = err_info; \
} }
#define IKARUS_FAIL(ret, msg, err_info) \ #define IKARUS_FAIL(ret, msg, err_info) \
@ -33,142 +42,75 @@ void safe_strcpy(
return ret; \ return ret; \
} }
#define IKARUS_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \ #define IKARUS_TRY_OR_FAIL_IMPL(var_name, msg, err_info, ...) \
auto var_name = __VA_ARGS__; \ auto var_name = __VA_ARGS__; \
if (var_name.is_error()) { \ if (var_name.is_error()) { \
IKARUS_SET_ERROR( \ IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), std::move(var_name).unwrap_error()), err_info); \
fmt::format( \ return var_name; \
fmt::runtime(msg), \
std::move(var_name).unwrap_error() \
), \
err_info \
); \
return var_name; \
} }
#define IKARUS_TRY_OR_FAIL(msg, err_info, ...) \ #define IKARUS_TRY_OR_FAIL(msg, err_info, ...) \
IKARUS_TRY_OR_FAIL_IMPL( \ IKARUS_TRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), msg, err_info, __VA_ARGS__);
CPPBASE_UNIQUE_NAME(result), \
msg, \
err_info, \
__VA_ARGS__ \
);
#define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \ #define IKARUS_TRYRV_OR_FAIL_IMPL(var_name, ret, msg, err_info, ...) \
auto var_name = __VA_ARGS__; \ auto var_name = __VA_ARGS__; \
if (var_name.is_error()) { \ if (var_name.is_error()) { \
IKARUS_SET_ERROR( \ IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), std::move(var_name).unwrap_error()), err_info); \
fmt::format( \ return ret; \
fmt::runtime(msg), \
std::move(var_name).unwrap_error() \
), \
err_info \
); \
return ret; \
} }
#define IKARUS_TRYRV_OR_FAIL(ret, msg, err_info, ...) \ #define IKARUS_TRYRV_OR_FAIL(ret, msg, err_info, ...) \
IKARUS_TRYRV_OR_FAIL_IMPL( \ IKARUS_TRYRV_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), ret, msg, err_info, __VA_ARGS__);
CPPBASE_UNIQUE_NAME(result), \
ret, \
msg, \
err_info, \
__VA_ARGS__ \
);
#define IKARUS_VTRY_OR_FAIL_IMPL(var_name, value, msg, err_info, ...) \ #define IKARUS_VTRY_OR_FAIL_IMPL(var_name, value, msg, err_info, ...) \
auto var_name = __VA_ARGS__; \ auto var_name = __VA_ARGS__; \
if (var_name.is_error()) { \ if (var_name.is_error()) { \
IKARUS_SET_ERROR( \ IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), std::move(var_name).unwrap_error()), err_info); \
fmt::format( \ return var_name; \
fmt::runtime(msg), \ } \
std::move(var_name).unwrap_error() \
), \
err_info \
); \
return var_name; \
} \
value = std::move(var_name).unwrap_value() value = std::move(var_name).unwrap_value()
#define IKARUS_VTRY_OR_FAIL(value, msg, err_info, ...) \ #define IKARUS_VTRY_OR_FAIL(value, msg, err_info, ...) \
IKARUS_VTRY_OR_FAIL_IMPL( \ IKARUS_VTRY_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), value, msg, err_info, __VA_ARGS__);
CPPBASE_UNIQUE_NAME(result), \
value, \
msg, \
err_info, \
__VA_ARGS__ \
);
#define IKARUS_VTRYRV_OR_FAIL_IMPL(var_name, value, ret, msg, err_info, ...) \ #define IKARUS_VTRYRV_OR_FAIL_IMPL(var_name, value, ret, msg, err_info, ...) \
auto var_name = __VA_ARGS__; \ auto var_name = __VA_ARGS__; \
if (var_name.is_error()) { \ if (var_name.is_error()) { \
IKARUS_SET_ERROR( \ IKARUS_SET_ERROR(fmt::format(fmt::runtime(msg), var_name.unwrap_error()), err_info); \
fmt::format(fmt::runtime(msg), var_name.unwrap_error()), \ return ret; \
err_info \ } \
); \
return ret; \
} \
value = std::move(var_name).unwrap_value() value = std::move(var_name).unwrap_value()
#define IKARUS_VTRYRV_OR_FAIL(value, ret, msg, err_info, ...) \ #define IKARUS_VTRYRV_OR_FAIL(value, ret, msg, err_info, ...) \
IKARUS_VTRYRV_OR_FAIL_IMPL( \ IKARUS_VTRYRV_OR_FAIL_IMPL(CPPBASE_UNIQUE_NAME(result), value, ret, msg, err_info, __VA_ARGS__);
CPPBASE_UNIQUE_NAME(result), \
value, \
ret, \
msg, \
err_info, \
__VA_ARGS__ \
);
#define IKARUS_FAIL_IF_ERROR(ret) \ #define IKARUS_FAIL_IF_ERROR(ret, error) \
if (ikarus_error_data_is_error(error_out)) { \ if (ikarus_error_data_is_error(error)) { \
return ret; \ if (error_out) { \
*error_out = *error; \
} \
return ret; \
} }
#define IKARUS_FAIL_IF_NULL(ptr, ret) \ #define IKARUS_FAIL_IF_NULL(ptr, ret) \
IKARUS_FAIL_IF( \ IKARUS_FAIL_IF(((ptr) == nullptr), ret, #ptr " must not be null", IkarusErrorInfo_Client_InvalidNull)
((ptr) == nullptr), \
ret, \
#ptr " must not be null", \
IkarusErrorInfo_Client_InvalidNull \
)
#define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \ #define IKARUS_FAIL_IF_NAME_INVALID(name, ret) \
IKARUS_FAIL_IF_NULL(name, ret); \ IKARUS_FAIL_IF_NULL(name, ret); \
IKARUS_FAIL_IF( \ IKARUS_FAIL_IF(cppbase::is_empty_or_blank(name), ret, "name must not be empty", IkarusErrorInfo_Client_InvalidInput)
cppbase::is_empty_or_blank(name), \
ret, \
#name " must not be empty", \
IkarusErrorInfo_Client_InvalidInput \
);
#define IKARUS_FAIL_IF_NOT_EXIST_IMPL(exists_name, object, ret) \ #define IKARUS_ASCERTAIN_IMPL(cond_name, ret, msg, err_info, func, ...) \
IKARUS_VTRYRV_OR_FAIL( \ auto cond_name = std::invoke(func, __VA_ARGS__, error_out); \
auto exists_name, \ IKARUS_FAIL_IF_ERROR(ret, error_out) \
ret, \ IKARUS_FAIL_IF(!cond_name, ret, msg, err_info)
fmt::format( \
"failed to check if {} exists", \
std::remove_cvref_t<decltype(*object)>::object_name \
), \
IkarusErrorInfo_Database_QueryFailed, \
object->project->db->query_one<bool>( \
fmt::format( \
"SELECT EXISTS(SELECT 1 FROM `{}` WHERE `id` = ?)", \
std::remove_cvref_t<decltype(*object)>::table_name \
), \
object->id \
) \
); \
\
IKARUS_FAIL_IF( \
!exists_name, \
ret, \
fmt::format( \
"{} doesn't exist", \
std::remove_cvref_t<decltype(*object)>::object_name \
), \
IkarusErrorInfo_Client_NonExistent \
)
#define IKARUS_FAIL_IF_NOT_EXIST(object, ret) \ #define IKARUS_ASCERTAIN(ret, msg, err_info, func, ...) \
IKARUS_FAIL_IF_NOT_EXIST_IMPL(CPPBASE_UNIQUE_NAME(exists), object, ret) IKARUS_ASCERTAIN_IMPL(CPPBASE_UNIQUE_NAME(cond), ret, msg, err_info, func, __VA_ARGS__);
#define IKARUS_CALL(ret, func, ...) \
std::invoke(func, __VA_ARGS__, error_out); \
IKARUS_FAIL_IF_ERROR(ret, error_out);
#define IKARUS_VCALL(value, ret, func, ...) \
value = std::invoke(func, __VA_ARGS__, error_out); \
IKARUS_FAIL_IF_ERROR(ret, error_out);

View file

@ -2,8 +2,72 @@
#include <ikarus/errors.hpp> #include <ikarus/errors.hpp>
#include <ikarus/objects/blueprint.hpp> #include <ikarus/objects/blueprint.hpp>
#include <ikarus/objects/entity.h>
#include <ikarus/persistence/project.hpp> #include <ikarus/persistence/project.hpp>
IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id): IkarusBlueprint::IkarusBlueprint(struct IkarusProject * project, int64_t id):
project{project}, project{project},
id{id} {} id{id} {}
IkarusBlueprint * ikarus_blueprint_create(
struct IkarusProject * project,
char const * name,
IkarusBlueprintCreateFlags flags,
struct IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(project, nullptr);
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
IKARUS_TRYRV_OR_FAIL(
nullptr,
"failed to create entity: {}",
IkarusErrorInfo_Database_QueryFailed,
project->db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name)
);
auto const id = project->db->last_insert_rowid();
return new IkarusBlueprint{project, id};
}
IkarusBlueprint * ikarus_blueprint_create_from_entity(
struct IkarusEntity * entity,
char const * name,
IkarusBlueprintCreateFromEntityFlags flags,
struct IkarusErrorData * error_out
) {
IKARUS_TRYRV_OR_FAIL(
nullptr,
"{}",
IkarusErrorInfo_Database_QueryFailed,
ikarus_libikarus_func_call_to_result(
error_out,
ikarus_must_return_true(ikarus_entity_exists, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent),
entity
)
);
IKARUS_FAIL_IF_NAME_INVALID(name, nullptr);
IKARUS_VTRYRV_OR_FAIL(
auto id,
nullptr,
"failed to create blueprint from entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->transact([&](auto * db) -> cppbase::Result<int64_t, sqlitecpp::TransactionError> {
CPPBASE_TRY(db->execute("INSERT INTO `blueprints`(`name`) VALUES(?)", name));
auto const id = db->last_insert_rowid();
CPPBASE_TRY(entity->project->db->execute(
"INSERT INTO `properties`(`blueprint`, `name`, `schema`) "
"SELECT ?, `name`, json_extract(`value`, '$.schema') FROM `entity_values` "
"WHERE `entity` = ?",
id,
entity->id
))
return cppbase::ok(entity->project->db->last_insert_rowid());
})
);
return new IkarusBlueprint{entity->project, id};
}

View file

@ -4,13 +4,28 @@
#include <ikarus/errors.h> #include <ikarus/errors.h>
#include <ikarus/errors.hpp> #include <ikarus/errors.hpp>
#include <ikarus/objects/blueprint.h>
#include <ikarus/objects/entity.h> #include <ikarus/objects/entity.h>
#include <ikarus/objects/property.h>
#include <ikarus/persistence/project.hpp> #include <ikarus/persistence/project.hpp>
IkarusEntity::IkarusEntity(struct IkarusProject * project, int64_t id): IkarusEntity::IkarusEntity(struct IkarusProject * project, int64_t id):
project{project}, project{project},
id{id} {} id{id} {}
bool ikarus_entity_exists(IkarusEntity * entity, IkarusErrorData * error_out) {
IKARUS_FAIL_IF_NULL(entity, false);
IKARUS_VTRYRV_OR_FAIL(
auto exists,
false,
"failed to check whether entity exists: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<bool>("SELECT EXISTS(SELECT 1 FROM `entities` WHERE `id` = ?)", entity->id)
);
return exists;
}
IkarusEntity * ikarus_entity_create( IkarusEntity * ikarus_entity_create(
struct IkarusProject * project, struct IkarusProject * project,
char const * name, char const * name,
@ -31,98 +46,83 @@ IkarusEntity * ikarus_entity_create(
return new IkarusEntity{project, id}; return new IkarusEntity{project, id};
} }
void ikarus_entity_delete( void ikarus_entity_delete(IkarusEntity * entity, IkarusEntityDeleteFlags flags, IkarusErrorData * error_out) {
IkarusEntity * entity, IKARUS_ASCERTAIN(
IkarusEntityDeleteFlags flags, IKARUS_VOID_RETURN,
IkarusErrorData * error_out "entity doesn't exist",
) { IkarusErrorInfo_Client_NonExistent,
IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); ikarus_entity_exists,
IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); entity
);
IKARUS_TRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
IKARUS_VOID_RETURN, IKARUS_VOID_RETURN,
"failed to delete entity: {}", "failed to delete entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db entity->project->db->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id)
->execute("DELETE FROM `entities` WHERE `id` = ?", entity->id)
); );
delete entity; delete entity;
} }
IkarusEntity * ikarus_entity_copy( IkarusEntity * ikarus_entity_copy(IkarusEntity * entity, IkarusEntityCopyFlags flags, IkarusErrorData * error_out) {
IkarusEntity * entity, IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IkarusEntityCopyFlags flags,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto id, auto id,
nullptr, nullptr,
"failed to copy entity: {}", "failed to copy entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db->transact( entity->project->db->transact([entity](auto * db) -> cppbase::Result<int64_t, sqlitecpp::TransactionError> {
[entity](auto * db) CPPBASE_TRY(entity->project->db->execute(
-> cppbase::Result<int64_t, sqlitecpp::TransactionError> { "INSERT INTO `entities`(`name`) "
TRY(entity->project->db->execute( "SELECT `name` FROM `entities` WHERE `id` = ?",
"INSERT INTO `entities`(`name`) " entity->id
"SELECT `name` FROM `entities` WHERE `id` = ?", ));
entity->id
));
TRY(entity->project->db->execute( CPPBASE_TRY(entity->project->db->execute(
"INSERT INTO `entity_values`(`entity`, `name`, `value`) " "INSERT INTO `entity_values`(`entity`, `name`, `value`) "
"SELECT ?1, `name`, `value` FROM `entity_values` WHERE " "SELECT ?1, `name`, `value` FROM `entity_values` WHERE "
"`entity` = ?1", "`entity` = ?1",
entity->id entity->id
)) ))
TRY(entity->project->db->execute( CPPBASE_TRY(entity->project->db->execute(
"INSERT INTO `entity_property_values`(`entity`, `property`, `value`) " "INSERT INTO `entity_property_values`(`entity`, `property`, `value`) "
"SELECT ?1, `property`, `value` FROM `entity_property_values` " "SELECT ?1, `property`, `value` FROM `entity_property_values` "
"WHERE `entity` = ?1", "WHERE `entity` = ?1",
entity->id entity->id
)) ))
TRY(entity->project->db->execute( CPPBASE_TRY(entity->project->db->execute(
"INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) " "INSERT INTO `entity_blueprint_links`(`entity`, `blueprint`) "
"SELECT ?1, `property`, `value` FROM `entity_property_values` " "SELECT ?1, `property`, `value` FROM `entity_property_values` "
"WHERE `entity` = ?1", "WHERE `entity` = ?1",
entity->id entity->id
)) ))
return cppbase::ok(entity->project->db->last_insert_rowid()); return cppbase::ok(entity->project->db->last_insert_rowid());
} })
)
); );
return new IkarusEntity{entity->project, id}; return new IkarusEntity{entity->project, id};
} }
IkarusProject * IkarusProject * ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) {
ikarus_entity_get_project(IkarusEntity * entity, IkarusErrorData * error_out) { IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr);
return entity->project; return entity->project;
} }
char const * char const * ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) {
ikarus_entity_get_name(IkarusEntity * entity, IkarusErrorData * error_out) { IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto name, auto name,
nullptr, nullptr,
"failed to get name for entity: {}", "failed to get name for entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<char const *>( entity->project->db->query_one<char const *>("SELECT `name` FROM `entities` WHERE `id` = ?", entity->id)
"SELECT `name` FROM `entities` WHERE `id` = ?",
entity->id
)
); );
return name; return name;
@ -134,33 +134,63 @@ void ikarus_entity_set_name(
IkarusEntitySetNameFlags flags, IkarusEntitySetNameFlags flags,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); IKARUS_ASCERTAIN(
IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_VOID_RETURN,
"entity doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_entity_exists,
entity
);
IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN);
IKARUS_TRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
IKARUS_VOID_RETURN, IKARUS_VOID_RETURN,
"failed to set name for entity: {}", "failed to set name for entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute( entity->project->db->execute("UPDATE `entities` SET `name` = ? WHERE `id` = ?", name, entity->id)
"UPDATE `entities` SET `name` = ? WHERE `id` = ?",
name,
entity->id
)
); );
} }
struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints( bool ikarus_entity_is_linked_to_blueprint(
IkarusEntity * entity, IkarusEntity * entity,
size_t * size_out, struct IkarusBlueprint * blueprint,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, nullptr); IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); IKARUS_ASCERTAIN(
IKARUS_FAIL_IF_NULL(size_out, nullptr); false,
"blueprint doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_blueprint_exists,
blueprint
);
auto count = ikarus_entity_get_linked_blueprints_count(entity, error_out); IKARUS_FAIL_IF(
IKARUS_FAIL_IF_ERROR(nullptr); entity->project != blueprint->project,
false,
"blueprint does not belong to entity's project",
IkarusErrorInfo_Client_NotLinked
);
IKARUS_VTRYRV_OR_FAIL(
auto exists,
false,
"failed to check if entity is linked to blueprint: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<bool>(
"SELECT EXISTS(SELECT 1 FROM `entity_blueprint_links` WHERE `entity` = ? AND `blueprint` = ?)",
entity->id,
blueprint->id
)
);
return exists;
}
struct IkarusBlueprint **
ikarus_entity_get_linked_blueprints(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) {
IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_VCALL(auto count, false, ikarus_entity_get_linked_blueprints_count, entity);
std::int64_t ids[count]; std::int64_t ids[count];
@ -189,22 +219,16 @@ struct IkarusBlueprint ** ikarus_entity_get_linked_blueprints(
return blueprints; return blueprints;
} }
size_t ikarus_entity_get_linked_blueprints_count( size_t ikarus_entity_get_linked_blueprints_count(IkarusEntity * entity, IkarusErrorData * error_out) {
IkarusEntity * entity, IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, 0);
IKARUS_FAIL_IF_NOT_EXIST(entity, 0);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto count, auto count,
0, 0,
"failed to get linked blueprints count for entity: {}", "failed to get linked blueprints count for entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<std::int64_t>( entity->project->db
"SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", ->query_one<std::int64_t>("SELECT COUNT(*) FROM `entity_blueprint_links` WHERE `entity` = ?", entity->id)
entity->id
)
); );
return count; return count;
@ -216,10 +240,28 @@ void ikarus_entity_link_blueprint(
IkarusEntityLinkBlueprintFlags flags, IkarusEntityLinkBlueprintFlags flags,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); IKARUS_ASCERTAIN(
IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_VOID_RETURN,
IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); "entity doesn't exist",
IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); IkarusErrorInfo_Client_NonExistent,
ikarus_entity_exists,
entity
);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"blueprint doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_blueprint_exists,
blueprint
);
IKARUS_FAIL_IF(
entity->project != blueprint->project,
IKARUS_VOID_RETURN,
"blueprint does not belong to entity's project",
IkarusErrorInfo_Client_NotLinked
);
IKARUS_TRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
IKARUS_VOID_RETURN, IKARUS_VOID_RETURN,
@ -239,10 +281,28 @@ void ikarus_entity_unlink_blueprint(
IkarusEntityUnlinkBlueprintFlags flags, IkarusEntityUnlinkBlueprintFlags flags,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); IKARUS_ASCERTAIN(
IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_VOID_RETURN,
IKARUS_FAIL_IF_NULL(blueprint, IKARUS_VOID_RETURN); "entity doesn't exist",
IKARUS_FAIL_IF_NOT_EXIST(blueprint, IKARUS_VOID_RETURN); IkarusErrorInfo_Client_NonExistent,
ikarus_entity_exists,
entity
);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"blueprint doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_blueprint_exists,
blueprint
);
IKARUS_FAIL_IF(
entity->project != blueprint->project,
IKARUS_VOID_RETURN,
"blueprint does not belong to entity's project",
IkarusErrorInfo_Client_NotLinked
);
IKARUS_TRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
IKARUS_VOID_RETURN, IKARUS_VOID_RETURN,
@ -256,13 +316,28 @@ void ikarus_entity_unlink_blueprint(
); );
} }
IkarusEntityValue * ikarus_entity_get_values( bool ikarus_entity_has_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) {
IkarusEntity * entity, IKARUS_FAIL_IF_NULL(name, false);
size_t * size_out,
IkarusErrorData * error_out IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
) {
IKARUS_FAIL_IF_NULL(entity, nullptr); IKARUS_VTRYRV_OR_FAIL(
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); auto exists,
false,
"failed to check if entity has value: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<bool>(
"SELECT EXISTS(SELECT 1 FROM `entity_values` WHERE `entity` = ? AND `name` = ?)",
entity->id,
name
)
);
return exists;
}
IkarusEntityValue * ikarus_entity_get_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) {
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto values_plain, auto values_plain,
@ -277,17 +352,9 @@ IkarusEntityValue * ikarus_entity_get_values(
IkarusEntityValue * values = new IkarusEntityValue[values_plain.size()]; IkarusEntityValue * values = new IkarusEntityValue[values_plain.size()];
std::transform( std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [](auto const & tuple) {
std::cbegin(values_plain), return IkarusEntityValue{tuple.template get<0>(), tuple.template get<1>()};
std::cend(values_plain), });
values,
[](auto const & tuple) {
return IkarusEntityValue{
tuple.template get<0>(),
tuple.template get<1>()
};
}
);
if (size_out) { if (size_out) {
*size_out = values_plain.size(); *size_out = values_plain.size();
@ -296,15 +363,19 @@ IkarusEntityValue * ikarus_entity_get_values(
return values; return values;
} }
char const * ikarus_entity_get_value( char const * ikarus_entity_get_value(IkarusEntity * entity, char const * name, IkarusErrorData * error_out) {
IkarusEntity * entity,
char const * name,
IkarusErrorData * error_out
) {
IKARUS_FAIL_IF_NULL(entity, nullptr);
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr);
IKARUS_FAIL_IF_NULL(name, nullptr); IKARUS_FAIL_IF_NULL(name, nullptr);
IKARUS_ASCERTAIN(nullptr, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_ASCERTAIN(
nullptr,
"entity doesn't have value",
IkarusErrorInfo_Client_NotLinked,
ikarus_entity_has_value,
entity,
name
);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto value, auto value,
nullptr, nullptr,
@ -326,10 +397,16 @@ void ikarus_entity_set_value(
char const * value, char const * value,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN);
IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN);
IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN);
IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN);
IKARUS_FAIL_IF_NAME_INVALID(name, IKARUS_VOID_RETURN);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"entity doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_entity_exists,
entity
);
// parsing from & to here to ensure values are valid JSON & formatted // parsing from & to here to ensure values are valid JSON & formatted
// uniformly // uniformly
@ -338,7 +415,7 @@ void ikarus_entity_set_value(
IKARUS_VOID_RETURN, IKARUS_VOID_RETURN,
"cannot parse value as JSON: {}", "cannot parse value as JSON: {}",
IkarusErrorInfo_Client_InvalidInput, IkarusErrorInfo_Client_InvalidInput,
IkarusValue::from_json(value) IkarusValue::from_json_str(value)
); );
IKARUS_TRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
@ -361,29 +438,64 @@ void ikarus_entity_delete_value(
IkarusEntityDeleteValueFlags flags, IkarusEntityDeleteValueFlags flags,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN);
IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN);
IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(name, IKARUS_VOID_RETURN);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"entity doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_entity_exists,
entity
);
IKARUS_TRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
IKARUS_VOID_RETURN, IKARUS_VOID_RETURN,
"failed to delete value for entity: {}", "failed to delete value for entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute( entity->project->db->execute("DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?", entity->id, name)
"DELETE FROM `entity_values` WHERE `entity` = ? AND `name` = ?",
entity->id,
name
)
); );
} }
IkarusEntityPropertyValue * ikarus_entity_get_property_values( bool ikarus_entity_has_property_value(
IkarusEntity * entity, IkarusEntity * entity,
size_t * size_out, struct IkarusProperty * property,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, nullptr); IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr);
IKARUS_ASCERTAIN(
false,
"property doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_property_exists,
property
);
IKARUS_FAIL_IF(
entity->project != property->project,
false,
"property does not belong to entity's project",
IkarusErrorInfo_Client_NotLinked
);
IKARUS_VTRYRV_OR_FAIL(
auto exists,
false,
"failed to check if entity has property value: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<bool>(
"SELECT EXISTS(SELECT 1 FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?)",
entity->id,
property->id
)
);
return exists;
}
IkarusEntityPropertyValue *
ikarus_entity_get_property_values(IkarusEntity * entity, size_t * size_out, IkarusErrorData * error_out) {
IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto values_plain, auto values_plain,
@ -391,24 +503,20 @@ IkarusEntityPropertyValue * ikarus_entity_get_property_values(
"failed to get property values for entity: {}", "failed to get property values for entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_many<int64_t, char const *>( entity->project->db->query_many<int64_t, char const *>(
"SELECT `property`, `value` FROM `entity_property_values` WHERE `entity` = ?", "SELECT `e`.`property`, IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` "
"INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` "
" WHERE `e`.`entity` = ?",
entity->id entity->id
) )
); );
IkarusEntityPropertyValue * values = IkarusEntityPropertyValue * values = new IkarusEntityPropertyValue[values_plain.size()];
new IkarusEntityPropertyValue[values_plain.size()]; std::transform(std::cbegin(values_plain), std::cend(values_plain), values, [entity](auto const & tuple) {
std::transform( return IkarusEntityPropertyValue{
std::cbegin(values_plain), new IkarusProperty{entity->project, tuple.template get<0>()},
std::cend(values_plain), tuple.template get<1>()
values, };
[entity](auto const & tuple) { });
return IkarusEntityPropertyValue{
new IkarusProperty{entity->project, tuple.template get<0>()},
tuple.template get<1>()
};
}
);
if (size_out) { if (size_out) {
*size_out = values_plain.size(); *size_out = values_plain.size();
@ -417,15 +525,23 @@ IkarusEntityPropertyValue * ikarus_entity_get_property_values(
return values; return values;
} }
char const * ikarus_entity_get_property_value( char const *
IkarusEntity * entity, ikarus_entity_get_property_value(IkarusEntity * entity, struct IkarusProperty * property, IkarusErrorData * error_out) {
struct IkarusProperty * property, IKARUS_ASCERTAIN(false, "entity doesn't exist", IkarusErrorInfo_Client_NonExistent, ikarus_entity_exists, entity);
IkarusErrorData * error_out IKARUS_ASCERTAIN(
) { false,
IKARUS_FAIL_IF_NULL(entity, nullptr); "property doesn't exist",
IKARUS_FAIL_IF_NOT_EXIST(entity, nullptr); IkarusErrorInfo_Client_NonExistent,
IKARUS_FAIL_IF_NULL(property, nullptr); ikarus_property_exists,
IKARUS_FAIL_IF_NOT_EXIST(property, nullptr); property
);
IKARUS_FAIL_IF(
entity->project != property->project,
nullptr,
"property does not belong to entity's project",
IkarusErrorInfo_Client_NotLinked
);
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
auto value, auto value,
@ -433,7 +549,9 @@ char const * ikarus_entity_get_property_value(
"failed to get property value for entity: {}", "failed to get property value for entity: {}",
IkarusErrorInfo_Database_QueryFailed, IkarusErrorInfo_Database_QueryFailed,
entity->project->db->query_one<char const *>( entity->project->db->query_one<char const *>(
"SELECT `value` FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?", "SELECT IFNULL(`e`.`value`, ikarus_default_value(`p`.`schema`)) FROM `entity_property_values` AS `e` "
"INNER JOIN `properties` AS `p` ON `p`.`id` = `e`.`property` "
"WHERE `e`.`entity` = ? AND `e`.`property` = ?",
entity->id, entity->id,
property->id property->id
) )
@ -449,12 +567,38 @@ void ikarus_entity_set_property_value(
IkarusEntitySetPropertyValueFlags flags, IkarusEntitySetPropertyValueFlags flags,
IkarusErrorData * error_out IkarusErrorData * error_out
) { ) {
IKARUS_FAIL_IF_NULL(entity, IKARUS_VOID_RETURN); IKARUS_ASCERTAIN(
IKARUS_FAIL_IF_NOT_EXIST(entity, IKARUS_VOID_RETURN); IKARUS_VOID_RETURN,
IKARUS_FAIL_IF_NULL(property, IKARUS_VOID_RETURN); "entity doesn't exist",
IKARUS_FAIL_IF_NOT_EXIST(property, IKARUS_VOID_RETURN); IkarusErrorInfo_Client_NonExistent,
ikarus_entity_exists,
entity
);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"property doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_property_exists,
property
);
IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN); IKARUS_FAIL_IF_NULL(value, IKARUS_VOID_RETURN);
IKARUS_FAIL_IF(
entity->project != property->project,
IKARUS_VOID_RETURN,
"property does not belong to entity's project",
IkarusErrorInfo_Client_NotLinked
);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"entity does not have property value",
IkarusErrorInfo_Client_NotLinked,
ikarus_entity_has_property_value,
entity,
property
);
// parsing from & to here to ensure values are valid JSON & formatted // parsing from & to here to ensure values are valid JSON & formatted
// uniformly // uniformly
IKARUS_VTRYRV_OR_FAIL( IKARUS_VTRYRV_OR_FAIL(
@ -462,7 +606,7 @@ void ikarus_entity_set_property_value(
IKARUS_VOID_RETURN, IKARUS_VOID_RETURN,
"cannot parse value as JSON: {}", "cannot parse value as JSON: {}",
IkarusErrorInfo_Client_InvalidInput, IkarusErrorInfo_Client_InvalidInput,
IkarusValue::from_json(value) IkarusValue::from_json_str(value)
); );
IKARUS_TRYRV_OR_FAIL( IKARUS_TRYRV_OR_FAIL(
@ -478,3 +622,51 @@ void ikarus_entity_set_property_value(
) )
); );
} }
void ikarus_entity_clear_property_value(
IkarusEntity * entity,
struct IkarusProperty * property,
IkarusEntitySetPropertyValueFlags flags,
IkarusErrorData * error_out
) {
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"entity doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_entity_exists,
entity
);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"property doesn't exist",
IkarusErrorInfo_Client_NonExistent,
ikarus_property_exists,
property
);
IKARUS_FAIL_IF(
entity->project != property->project,
IKARUS_VOID_RETURN,
"property does not belong to entity's project",
IkarusErrorInfo_Client_NotLinked
);
IKARUS_ASCERTAIN(
IKARUS_VOID_RETURN,
"entity doesn't have property",
IkarusErrorInfo_Client_NotLinked,
ikarus_entity_has_property_value,
entity,
property
);
IKARUS_TRYRV_OR_FAIL(
IKARUS_VOID_RETURN,
"failed to clear property value for entity: {}",
IkarusErrorInfo_Database_QueryFailed,
entity->project->db->execute(
"DELETE FROM `entity_property_values` WHERE `entity` = ? AND `property` = ?",
entity->id,
property->id
)
);
}

View file

@ -26,8 +26,7 @@ CREATE TABLE `properties`
`blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE, `blueprint` INTEGER NOT NULL REFERENCES `blueprints` (`id`) ON DELETE CASCADE,
`name` TEXT NOT NULL, `name` TEXT NOT NULL,
`schema` TEXT NOT NULL, `schema` TEXT NOT NULL,
`default_value` TEXT NOT NULL, `default_value` TEXT
`settings` TEXT NOT NULL
) STRICT; ) STRICT;
CREATE TABLE `entity_blueprint_links` CREATE TABLE `entity_blueprint_links`

View file

@ -73,6 +73,49 @@ auto create_impl(
.on_error(close_db) .on_error(close_db)
); );
db->create_function(
"ikarus_default_value",
1,
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
nullptr,
[](sqlite3_context * ctx, int argc, sqlite3_value ** argv) {
if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) {
sqlite3_result_error(
ctx,
"expected the 'schema' parameter to be of type text",
SQLITE_MISMATCH
);
return;
}
auto const schema_json =
reinterpret_cast<char const *>(sqlite3_value_text(argv[0]));
auto schema_res = IkarusValueSchema::from_json_str(schema_json);
if (schema_res.is_error()) {
sqlite3_result_error(
ctx,
"failed to parse schema",
SQLITE_ERROR
);
return;
}
auto schema = std::move(schema_res).unwrap_value();
auto default_value_json =
IkarusValue::to_json(schema.default_value()).dump();
sqlite3_result_text(
ctx,
default_value_json.data(),
default_value_json.size(),
SQLITE_TRANSIENT
);
}
);
return std::move(db); return std::move(db);
} }

View file

@ -20,7 +20,7 @@ auto get_primitive_type(IkarusValueDataPrimitive const & primitive)
[](IkarusValueDataPrimitiveNumber const &) { [](IkarusValueDataPrimitiveNumber const &) {
return IkarusValuePrimitiveType_Number; return IkarusValuePrimitiveType_Number;
}, },
[](IkarusValueDataPrimitiveString const &) { [](IkarusValueDataPrimitiveText const &) {
return IkarusValuePrimitiveType_Text; return IkarusValuePrimitiveType_Text;
} }
}, },
@ -36,7 +36,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
IkarusValueData value{}; IkarusValueData value{};
VTRY( CPPBASE_VTRY(
auto type, auto type,
deserialize_enum<IkarusValueDataType>( deserialize_enum<IkarusValueDataType>(
json, json,
@ -48,7 +48,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
switch (type) { switch (type) {
case IkarusValueDataType_Primitive: { case IkarusValueDataType_Primitive: {
VTRY( CPPBASE_VTRY(
auto primitive, auto primitive,
deserialize_enum<IkarusValuePrimitiveType>( deserialize_enum<IkarusValuePrimitiveType>(
json, json,
@ -60,21 +60,21 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
switch (primitive) { switch (primitive) {
case IkarusValuePrimitiveType_Toggle: { case IkarusValuePrimitiveType_Toggle: {
VTRY(auto data, deserialize_any<bool>(json, "data")); CPPBASE_VTRY(auto data, deserialize_any<bool>(json, "data"));
value.variant = IkarusValueDataPrimitiveToggle{data}; value.variant = IkarusValueDataPrimitiveToggle{data};
break; break;
} }
case IkarusValuePrimitiveType_Number: { case IkarusValuePrimitiveType_Number: {
VTRY(auto data, deserialize_any<double>(json, "data")); CPPBASE_VTRY(auto data, deserialize_any<double>(json, "data"));
value.variant = IkarusValueDataPrimitiveNumber{data}; value.variant = IkarusValueDataPrimitiveNumber{data};
break; break;
} }
case IkarusValuePrimitiveType_Text: { case IkarusValuePrimitiveType_Text: {
VTRY(auto data, deserialize_any<std::string>(json, "data")); CPPBASE_VTRY(auto data, deserialize_any<std::string>(json, "data"));
value.variant = IkarusValueDataPrimitiveString{data}; value.variant = IkarusValueDataPrimitiveText{data};
break; break;
} }
} }
@ -83,14 +83,17 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
} }
case IkarusValueDataType_List: { case IkarusValueDataType_List: {
std::vector<cppbase::owning_ptr<IkarusValueData>> values_data{}; std::vector<cppbase::owning_ptr<IkarusValueData>> values_data{};
VTRY( CPPBASE_VTRY(
auto data_json, auto data_json,
deserialize_any<std::vector<nlohmann::json>>(json, "data") deserialize_any<std::vector<nlohmann::json>>(json, "data")
); );
values_data.reserve(data_json.size()); values_data.reserve(data_json.size());
for (auto const & data_json : data_json) { for (auto const & data_json : data_json) {
VTRY(auto value_data, IkarusValueData::from_json(data_json)); CPPBASE_VTRY(
auto value_data,
IkarusValueData::from_json(data_json)
);
values_data.emplace_back( values_data.emplace_back(
cppbase::make_owning<IkarusValueData>(std::move(value_data)) cppbase::make_owning<IkarusValueData>(std::move(value_data))
); );
@ -105,7 +108,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
cppbase::owning_ptr<IkarusValueData>>> cppbase::owning_ptr<IkarusValueData>>>
map_data{}; map_data{};
VTRY( CPPBASE_VTRY(
auto map_data_json, auto map_data_json,
deserialize_any<std::vector<nlohmann::json>>(json, "data") deserialize_any<std::vector<nlohmann::json>>(json, "data")
); );
@ -113,10 +116,10 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
map_data.reserve(map_data_json.size()); map_data.reserve(map_data_json.size());
for (auto const & pair_json : map_data_json) { for (auto const & pair_json : map_data_json) {
VTRY(auto key_json, get_key(pair_json, "key")); CPPBASE_VTRY(auto key_json, get_key(pair_json, "key"));
VTRY(auto value_json, get_key(pair_json, "value")); CPPBASE_VTRY(auto value_json, get_key(pair_json, "value"));
VTRY(auto key, IkarusValueData::from_json(*key_json)); CPPBASE_VTRY(auto key, IkarusValueData::from_json(*key_json));
VTRY(auto value, IkarusValueData::from_json(*value_json)); CPPBASE_VTRY(auto value, IkarusValueData::from_json(*value_json));
map_data.emplace_back( map_data.emplace_back(
cppbase::make_owning<IkarusValueData>(key), cppbase::make_owning<IkarusValueData>(key),
@ -130,7 +133,7 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
case IkarusValueDataType_Tuple: { case IkarusValueDataType_Tuple: {
std::vector<cppbase::owning_ptr<IkarusValueData>> values_data{}; std::vector<cppbase::owning_ptr<IkarusValueData>> values_data{};
VTRY( CPPBASE_VTRY(
auto values_json, auto values_json,
deserialize_any<std::vector<nlohmann::json>>(json, "data") deserialize_any<std::vector<nlohmann::json>>(json, "data")
); );
@ -138,7 +141,10 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
values_data.reserve(values_json.size()); values_data.reserve(values_json.size());
for (auto const & value_json : values_json) { for (auto const & value_json : values_json) {
VTRY(auto value_data, IkarusValueData::from_json(value_json)); CPPBASE_VTRY(
auto value_data,
IkarusValueData::from_json(value_json)
);
values_data.emplace_back( values_data.emplace_back(
cppbase::make_owning<IkarusValueData>(value_data) cppbase::make_owning<IkarusValueData>(value_data)
); );
@ -152,6 +158,19 @@ auto IkarusValueData::from_json(nlohmann::json const & json)
return cppbase::ok(value); return cppbase::ok(value);
} }
auto IkarusValueData::from_json_str(std::string_view json_str)
-> cppbase::Result<IkarusValueData, IkarusValueDataParseError> {
auto json = nlohmann::json::parse(json_str, nullptr, false);
if (json.is_discarded()) {
return cppbase::err(
IkarusValueDataParseError{IkarusJsonError{IkarusJsonParseError{}}}
);
}
return IkarusValueData::from_json(json);
}
auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json { auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json {
nlohmann::json json = nlohmann::json::object(); nlohmann::json json = nlohmann::json::object();
@ -170,7 +189,7 @@ auto IkarusValueData::to_json(IkarusValueData const & value) -> nlohmann::json {
json["primitive"] = IkarusValuePrimitiveType_Number; json["primitive"] = IkarusValuePrimitiveType_Number;
json["data"] = number.value; json["data"] = number.value;
}, },
[&](IkarusValueDataPrimitiveString const & string) { [&](IkarusValueDataPrimitiveText const & string) {
json["type"] = IkarusValueDataType_Primitive; json["type"] = IkarusValueDataType_Primitive;
json["primitive"] = IkarusValuePrimitiveType_Text; json["primitive"] = IkarusValuePrimitiveType_Text;
json["data"] = string.value; json["data"] = string.value;

View file

@ -22,14 +22,14 @@ struct IkarusValueDataPrimitiveNumber {
double value; double value;
}; };
struct IkarusValueDataPrimitiveString { struct IkarusValueDataPrimitiveText {
std::string value; std::string value;
}; };
using IkarusValueDataPrimitive = std::variant< using IkarusValueDataPrimitive = std::variant<
IkarusValueDataPrimitiveToggle, IkarusValueDataPrimitiveToggle,
IkarusValueDataPrimitiveNumber, IkarusValueDataPrimitiveNumber,
IkarusValueDataPrimitiveString>; IkarusValueDataPrimitiveText>;
struct IkarusValueDataList { struct IkarusValueDataList {
std::vector<cppbase::owning_ptr<IkarusValueData>> values; std::vector<cppbase::owning_ptr<IkarusValueData>> values;
@ -53,7 +53,9 @@ struct IkarusValueData {
IkarusValueDataMap, IkarusValueDataMap,
IkarusValueDataTuple>; IkarusValueDataTuple>;
static auto from_json(nlohmann::json const & json) static auto from_json(nlohmann::json const & json_str)
-> cppbase::Result<IkarusValueData, IkarusValueDataParseError>;
static auto from_json_str(std::string_view json_str)
-> cppbase::Result<IkarusValueData, IkarusValueDataParseError>; -> cppbase::Result<IkarusValueData, IkarusValueDataParseError>;
static auto to_json(IkarusValueData const & value) -> nlohmann::json; static auto to_json(IkarusValueData const & value) -> nlohmann::json;

View file

@ -10,12 +10,15 @@ struct IkarusJsonInvalidTypeError {};
struct IkarusJsonEnumOutOfBoundsError {}; struct IkarusJsonEnumOutOfBoundsError {};
struct IkarusJsonParseError {};
struct IkarusJsonUnknownError {}; struct IkarusJsonUnknownError {};
using IkarusJsonError = std::variant< using IkarusJsonError = std::variant<
IkarusJsonMissingKeyError, IkarusJsonMissingKeyError,
IkarusJsonInvalidTypeError, IkarusJsonInvalidTypeError,
IkarusJsonEnumOutOfBoundsError, IkarusJsonEnumOutOfBoundsError,
IkarusJsonParseError,
IkarusJsonUnknownError>; IkarusJsonUnknownError>;
struct IkarusValueSchemaParseError { struct IkarusValueSchemaParseError {
@ -57,6 +60,16 @@ struct fmt::formatter<IkarusJsonInvalidTypeError> : formatter<string_view> {
} }
}; };
template<>
struct fmt::formatter<IkarusJsonParseError> : formatter<string_view> {
constexpr static auto format(
[[maybe_unused]] IkarusJsonParseError const & error,
fmt::format_context & ctx
) {
return fmt::format_to(ctx.out(), "buffer isn't valid JSON");
}
};
template<> template<>
struct fmt::formatter<IkarusJsonEnumOutOfBoundsError> : formatter<string_view> { struct fmt::formatter<IkarusJsonEnumOutOfBoundsError> : formatter<string_view> {
constexpr static auto format( constexpr static auto format(

View file

@ -5,6 +5,7 @@
#include <ikarus/values/data.hpp> #include <ikarus/values/data.hpp>
#include <ikarus/values/schema.h> #include <ikarus/values/schema.h>
#include <ikarus/values/shared.hpp> #include <ikarus/values/shared.hpp>
#include <ikarus/values/value.hpp>
auto IkarusValueSchema::from_json(nlohmann::json const & json) auto IkarusValueSchema::from_json(nlohmann::json const & json)
-> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError> { -> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError> {
@ -18,7 +19,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json)
IkarusValueSchema schema{}; IkarusValueSchema schema{};
VTRY( CPPBASE_VTRY(
auto type, auto type,
deserialize_enum<IkarusValueSchemaType>( deserialize_enum<IkarusValueSchemaType>(
json, json,
@ -30,7 +31,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json)
switch (type) { switch (type) {
case IkarusValueSchemaType_Primitive: { case IkarusValueSchemaType_Primitive: {
VTRY( CPPBASE_VTRY(
auto primitive, auto primitive,
deserialize_enum<IkarusValuePrimitiveType>( deserialize_enum<IkarusValuePrimitiveType>(
json, json,
@ -43,15 +44,21 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json)
break; break;
} }
case IkarusValueSchemaType_List: { case IkarusValueSchemaType_List: {
VTRY(auto sub_schema, IkarusValueSchema::from_json(json["schema"])); CPPBASE_VTRY(
auto sub_schema,
IkarusValueSchema::from_json(json["schema"])
);
schema.variant = IkarusValueSchemaList{ schema.variant = IkarusValueSchemaList{
cppbase::make_owning<IkarusValueSchema>(std::move(sub_schema)) cppbase::make_owning<IkarusValueSchema>(std::move(sub_schema))
}; };
break; break;
} }
case IkarusValueSchemaType_Map: { case IkarusValueSchemaType_Map: {
VTRY(auto key_schema, IkarusValueSchema::from_json(json["key_schema"])); CPPBASE_VTRY(
VTRY( auto key_schema,
IkarusValueSchema::from_json(json["key_schema"])
);
CPPBASE_VTRY(
auto value_schema, auto value_schema,
IkarusValueSchema::from_json(json["value_schema"]) IkarusValueSchema::from_json(json["value_schema"])
); );
@ -62,7 +69,7 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json)
break; break;
} }
case IkarusValueSchemaType_Tuple: { case IkarusValueSchemaType_Tuple: {
VTRY( CPPBASE_VTRY(
auto sub_schemas_json, auto sub_schemas_json,
deserialize_any<std::vector<nlohmann::json>>(json, "schemas") deserialize_any<std::vector<nlohmann::json>>(json, "schemas")
); );
@ -71,7 +78,10 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json)
sub_schemas.reserve(sub_schemas_json.size()); sub_schemas.reserve(sub_schemas_json.size());
for (auto const & sub_schema_json : sub_schemas_json) { for (auto const & sub_schema_json : sub_schemas_json) {
VTRY(auto schema, IkarusValueSchema::from_json(sub_schema_json)); CPPBASE_VTRY(
auto schema,
IkarusValueSchema::from_json(sub_schema_json)
);
sub_schemas.emplace_back( sub_schemas.emplace_back(
cppbase::make_owning<IkarusValueSchema>(std::move(schema)) cppbase::make_owning<IkarusValueSchema>(std::move(schema))
); );
@ -85,6 +95,19 @@ auto IkarusValueSchema::from_json(nlohmann::json const & json)
return cppbase::ok(std::move(schema)); return cppbase::ok(std::move(schema));
} }
auto IkarusValueSchema::from_json_str(std::string_view json_str)
-> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError> {
auto json = nlohmann::json::parse(json_str, nullptr, false);
if (json.is_discarded()) {
return cppbase::err(
IkarusValueSchemaParseError{IkarusJsonError{IkarusJsonParseError{}}}
);
}
return IkarusValueSchema::from_json(json);
}
auto IkarusValueSchema::to_json(IkarusValueSchema const & schema) auto IkarusValueSchema::to_json(IkarusValueSchema const & schema)
-> nlohmann::json { -> nlohmann::json {
nlohmann::json json = nlohmann::json::object(); nlohmann::json json = nlohmann::json::object();
@ -173,3 +196,46 @@ auto IkarusValueSchema::validate(IkarusValueData const & data) const -> bool {
data.variant data.variant
); );
} }
auto IkarusValueSchema::default_value_data() const -> IkarusValueData {
return std::visit(
cppbase::overloaded{
[](IkarusValueSchemaPrimitive const & schema) -> IkarusValueData {
switch (schema.type) {
case IkarusValuePrimitiveType_Toggle: {
return {{IkarusValueDataPrimitiveToggle{false}}};
}
case IkarusValuePrimitiveType_Number:
return {{IkarusValueDataPrimitiveNumber{0.0}}};
case IkarusValuePrimitiveType_Text:
return {{IkarusValueDataPrimitiveText{""}}};
}
},
[](IkarusValueSchemaList const & schema) -> IkarusValueData {
return {IkarusValueDataList{{}}};
},
[](IkarusValueSchemaMap const & schema) -> IkarusValueData {
return {IkarusValueDataMap{{}}};
},
[](IkarusValueSchemaTuple const & schema) -> IkarusValueData {
IkarusValueDataTuple data{};
data.values.reserve(schema.sub_schemas.size());
for (auto const & sub_schema : schema.sub_schemas) {
data.values.emplace_back(
cppbase::make_owning<IkarusValueData>(
sub_schema->default_value().data
)
);
}
return {data};
}
},
variant
);
}
auto IkarusValueSchema::default_value() const -> IkarusValue {
return IkarusValue{*this, default_value_data()};
}

View file

@ -42,9 +42,13 @@ struct IkarusValueSchema {
static auto from_json(nlohmann::json const & json) static auto from_json(nlohmann::json const & json)
-> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError>; -> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError>;
static auto from_json_str(std::string_view json_str)
-> cppbase::Result<IkarusValueSchema, IkarusValueSchemaParseError>;
static auto to_json(IkarusValueSchema const & value) -> nlohmann::json; static auto to_json(IkarusValueSchema const & value) -> nlohmann::json;
auto validate(IkarusValueData const & data) const -> bool; auto validate(IkarusValueData const & data) const -> bool;
auto default_value_data() const -> IkarusValueData;
auto default_value() const -> IkarusValue;
IkarusValueSchemaVariant variant; IkarusValueSchemaVariant variant;
}; };

View file

@ -32,7 +32,7 @@ auto deserialize_enum(
E min, E min,
E max E max
) -> cppbase::Result<E, IkarusJsonError> { ) -> cppbase::Result<E, IkarusJsonError> {
VTRY(auto iter, get_key(json, key)); CPPBASE_VTRY(auto iter, get_key(json, key));
if (!iter->is_number_integer()) { if (!iter->is_number_integer()) {
return cppbase::err(IkarusJsonError{}); return cppbase::err(IkarusJsonError{});
@ -51,7 +51,7 @@ auto deserialize_enum(
template<typename T> template<typename T>
auto deserialize_any(nlohmann::json const & json, std::string_view key) auto deserialize_any(nlohmann::json const & json, std::string_view key)
-> cppbase::Result<T, IkarusJsonError> { -> cppbase::Result<T, IkarusJsonError> {
VTRY(auto iter, get_key(json, key)); CPPBASE_VTRY(auto iter, get_key(json, key));
try { try {
return cppbase::ok(iter->get<T>()); return cppbase::ok(iter->get<T>());

View file

@ -15,8 +15,8 @@ auto IkarusValue::from_json(nlohmann::json const & json)
IkarusValue value{}; IkarusValue value{};
VTRY(value.schema, IkarusValueSchema::from_json(json["schema"])); CPPBASE_VTRY(value.schema, IkarusValueSchema::from_json(json["schema"]));
VTRY(value.data, IkarusValueData::from_json(json["data"])); CPPBASE_VTRY(value.data, IkarusValueData::from_json(json["data"]));
if (!value.schema.validate(value.data)) { if (!value.schema.validate(value.data)) {
return cppbase::err(IkarusValueParseErrorDataSchemaMismatch{}); return cppbase::err(IkarusValueParseErrorDataSchemaMismatch{});
@ -25,6 +25,19 @@ auto IkarusValue::from_json(nlohmann::json const & json)
return cppbase::ok(std::move(value)); return cppbase::ok(std::move(value));
} }
auto IkarusValue::from_json_str(std::string_view json_str)
-> cppbase::Result<IkarusValue, IkarusValueParseError> {
auto json = nlohmann::json::parse(json_str, nullptr, false);
if (json.is_discarded()) {
return cppbase::err(
IkarusValueDataParseError{IkarusJsonError{IkarusJsonParseError{}}}
);
}
return IkarusValue::from_json(json);
}
auto IkarusValue::to_json(IkarusValue const & value) -> nlohmann::json { auto IkarusValue::to_json(IkarusValue const & value) -> nlohmann::json {
nlohmann::json json = nlohmann::json::object(); nlohmann::json json = nlohmann::json::object();

View file

@ -14,5 +14,7 @@ struct IkarusValue {
static auto from_json(nlohmann::json const & json) static auto from_json(nlohmann::json const & json)
-> cppbase::Result<IkarusValue, IkarusValueParseError>; -> cppbase::Result<IkarusValue, IkarusValueParseError>;
static auto from_json_str(std::string_view json_str)
-> cppbase::Result<IkarusValue, IkarusValueParseError>;
static auto to_json(IkarusValue const & value) -> nlohmann::json; static auto to_json(IkarusValue const & value) -> nlohmann::json;
}; };

2
vendor/cppbase vendored

@ -1 +1 @@
Subproject commit e7741e0e60f380f8b473c0881f3d8b4f23d0df91 Subproject commit 82da6775fc56649cff6e611d6826a8e8fd321baa

2
vendor/sqlitecpp vendored

@ -1 +1 @@
Subproject commit 5bfd32f66164ed3d2c28003139fc5f76ef5e206d Subproject commit 5fd5731c98d6e260e35d05a59c99822e32fe7191